Fuzzy Regression#
The ex_fuzzy.evolutionary_fit_regression module learns interpretable
Type-1 fuzzy rules for continuous targets. During optimization it precomputes
the fixed input-partition memberships and scores candidate rule bases through a
vectorized NumPy inference path with PyMoo, or a batched PyTorch path with the
optional EvoX backend.
Two consequent styles are available, selected with consequent_type:
crisp(default)Zero-order Takagi-Sugeno. Each rule carries a number, and a prediction is the firing-strength-weighted average of those numbers. Best numeric resolution.
fuzzyMamdani. Each rule names an output fuzzy set whose trapezoid is evolved alongside the rules; predictions are the centroid of the clipped and max-aggregated consequents. Rules read fully linguistically, at some cost in resolution.
rule_mode controls how many rules speak per sample. additive (default)
lets every rule contribute. sufficient keeps only each sample’s strongest
rule, and falls back to the training-target mean when even that rule fires at
or below tolerance – giving piecewise-constant, winner-takes-all output.
BaseFuzzyRulesRegressor#
- class ex_fuzzy.evolutionary_fit_regression.BaseFuzzyRulesRegressor(nRules=30, nAnts=4, n_linguistic_variables=3, fuzzy_type=FUZZY_SETS.t1, linguistic_variables=None, consequent_type='crisp', n_output_lvs=3, n_universe_points=101, rule_mode='additive', tolerance=0.0, verbose=False, backend='pymoo', unique_antecedents=False, ordinal_conditions=False, compound_terms=None)[source]#
Bases:
RegressorMixin,BaseEstimatorScikit-learn-compatible fast Type-1 fuzzy rules regressor.
consequent_typeselects how a rule states its output:crispZero-order Takagi-Sugeno. Each rule carries a number and predictions are the firing-strength-weighted average of those numbers.
fuzzyMamdani. Each rule names an output fuzzy set whose shape is evolved alongside the rules; predictions are the centroid of the clipped and aggregated consequents. Rules read fully linguistically (
THEN output IS Output_2) at some cost in numeric resolution.
rule_modeselects how many rules speak per sample:additivelets every rule contribute, whilesufficientkeeps only the single strongest rule and falls back to the target mean when even that one fires belowtolerance.backend="pymoo"uses the vectorized NumPy objective on the CPU.backend="evox"evolves populations with EvoX and evaluates complete population batches with PyTorch on CUDA when available.unique_antecedents(defaultFalse) keeps only the first of several rules that share the same antecedents. The genetic search can otherwise spend part of thenRulesbudget on such repeats: in additive mode they act as one rule with the mean consequent (and double weight), in sufficient mode the later ones never speak. WithTruethe repeats are ignored by the objective and absent from the fitted rule base, so every rule slot left to the search describes a distinct antecedent set. The default leaves the objective, and so seeded fits, unchanged.ordinal_conditions(defaultFalse) lets a rule also statex IS at least torx IS at most tfor the termstof an input other than its lowest and highest, the terms being ordered as listed, low to high (Truefor every input that can have them, or a collection of input names or indexes).compound_terms(defaultNone) maps an input to[(name, [terms])...], each entry a conditionx IS <name>whose membership is the bounded sum of the listed terms, which need not be contiguous. Such a condition coverstand every term above (below) it, with the bounded-sum membershipmin(1, sum of their memberships). The term genes select among3n - 4conditions for a variable ofnterms, so the search space is larger; the fitted rule base printsIS at least t/IS at most tandrules.rule_conditionsreads the operator. Off, the encoding, rules and fits are unchanged.n_linguistic_variablesis the number of terms of each input partition built from the data. It is ignored whenlinguistic_variablesis given: the term counts are then read from those variables, and they may differ between features.- __init__(nRules=30, nAnts=4, n_linguistic_variables=3, fuzzy_type=FUZZY_SETS.t1, linguistic_variables=None, consequent_type='crisp', n_output_lvs=3, n_universe_points=101, rule_mode='additive', tolerance=0.0, verbose=False, backend='pymoo', unique_antecedents=False, ordinal_conditions=False, compound_terms=None)[source]#
Configure the regressor; optimization is performed in
fit().
- ordinal_conditions = False#
- compound_terms = None#
- fit(X, y, n_gen=50, pop_size=50, random_state=42)[source]#
Fit scalar consequents and rule antecedents with a genetic algorithm.
- set_fit_request(*, n_gen='$UNCHANGED$', pop_size='$UNCHANGED$', random_state='$UNCHANGED$')#
Configure whether metadata should be requested to be passed to the
fitmethod.Note that this method is only relevant when this estimator is used as a sub-estimator within a meta-estimator and metadata routing is enabled with
enable_metadata_routing=True(seesklearn.set_config()). Please check the User Guide on how the routing mechanism works.The options for each parameter are:
True: metadata is requested, and passed tofitif provided. The request is ignored if metadata is not provided.False: metadata is not requested and the meta-estimator will not pass it tofit.None: metadata is not requested, and the meta-estimator will raise an error if the user provides it.str: metadata should be passed to the meta-estimator with this given alias instead of the original name.
The default (
sklearn.utils.metadata_routing.UNCHANGED) retains the existing request. This allows you to change the request for some parameters and not others.Added in version 1.3.
- Parameters:
n_gen (str, True, False, or None, default=sklearn.utils.metadata_routing.UNCHANGED) – Metadata routing for
n_genparameter infit.pop_size (str, True, False, or None, default=sklearn.utils.metadata_routing.UNCHANGED) – Metadata routing for
pop_sizeparameter infit.random_state (str, True, False, or None, default=sklearn.utils.metadata_routing.UNCHANGED) – Metadata routing for
random_stateparameter infit.
- Returns:
self – The updated object.
- Return type:
object
FitRuleBaseRegression#
- class ex_fuzzy.evolutionary_fit_regression.FitRuleBaseRegression(X, y, nRules, nAnts, linguistic_variables, y_min=None, y_max=None, y_mean=None, consequent_type='crisp', n_output_lvs=3, n_universe_points=101, rule_mode='additive', tolerance=0.0, unique_antecedents=False, ordinal_conditions=False, compound_terms=None)[source]#
Bases:
ProblemOptimizer-independent problem for fast optimization of a fixed Type-1 partition.
The integer chromosome always starts with
nRules * nAntsfeature indices followed bynRules * nAntsterm indices (-1means don’t care). The consequent genes depend onconsequent_type:crispnRulesscalar consequents encoded into the target range.fuzzynRulesoutput-set indices, then4 * n_output_lvstrapezoid breakpoints. Each set’s four breakpoints are sorted on decoding, so every chromosome yields a valid trapezoid.
With
unique_antecedentsa rule whose antecedents repeat those of an earlier rule is dropped on decoding, so the search, the objective and the fitted rule base all see each antecedent set once (the first occurrence keeps its consequent). Off by default, which leaves every objective value unchanged.With
ordinal_conditionsthe term genes also selectat least t_jandat most t_jconditions (j = 1 .. n - 2of a variable withnterms), coded after the terms (seefuzzy_sets.ordinal_alphabet_size()), over copies of the given variables.ordinal_conditionsmay also be a collection of features (names or indexes) to give them to those only, andcompound_termsmaps a feature to[(name, [terms])...], named conditions whose membership is the bounded sum of the listed terms, coded after the ordinal ones. Off by default, which changes nothing.- __init__(X, y, nRules, nAnts, linguistic_variables, y_min=None, y_max=None, y_mean=None, consequent_type='crisp', n_output_lvs=3, n_universe_points=101, rule_mode='additive', tolerance=0.0, unique_antecedents=False, ordinal_conditions=False, compound_terms=None)[source]#
Initialize the vectorized regression optimization problem.
RuleBaseT1Regression#
- class ex_fuzzy.evolutionary_fit_regression.RuleBaseT1Regression(antecedents, rule_list, scalar_consequents, y_mean=None, rule_mode='additive', tolerance=0.0, tnorm=<function prod>)[source]#
Bases:
_RegressionRuleBaseType-1 fuzzy rule base with scalar consequents.
Predictions use zero-order Takagi-Sugeno (height) inference. Samples for which no rule fires fall back to the training-target mean.
RuleBaseT1MamdaniRegression#
- class ex_fuzzy.evolutionary_fit_regression.RuleBaseT1MamdaniRegression(antecedents, rule_list, output_sets, y_mean=None, universe=None, n_universe_points=101, rule_mode='additive', tolerance=0.0, tnorm=<function prod>)[source]#
Bases:
_RegressionRuleBaseType-1 fuzzy rule base whose consequents are output fuzzy sets.
Each rule names one output set. Inference clips every consequent by its rule’s firing strength (min), aggregates the clipped sets with max, and defuzzifies the result by taking its centroid over a discretized universe. Samples for which nothing fires fall back to the training-target mean.
Example#
from ex_fuzzy import BaseFuzzyRulesRegressor
from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split
X, y = make_regression(n_samples=200, n_features=4, random_state=0)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.25, random_state=0
)
regressor = BaseFuzzyRulesRegressor(nRules=20, nAnts=3)
regressor.fit(X_train, y_train, n_gen=50, pop_size=50)
predictions = regressor.predict(X_test)
print(regressor.score(X_test, y_test))
regressor.print_rules()
# Rule 1: IF x0 IS Low AND x2 IS High THEN output = 41.8203
GPU optimization#
Install ex-fuzzy[evox] and select the EvoX backend to evaluate complete
populations on CUDA. If CUDA is unavailable, EvoX runs the same PyTorch path on
the CPU.
regressor = BaseFuzzyRulesRegressor(
nRules=30, nAnts=4, backend="evox", verbose=True
)
regressor.fit(X_train, y_train, n_gen=50, pop_size=100)
print(regressor.optimization_device_)
print(regressor.gpu_accelerated_)
Linguistic consequents#
Switch to Mamdani inference when a rule should name an output label rather than a number:
regressor = BaseFuzzyRulesRegressor(
nRules=20,
nAnts=3,
consequent_type="fuzzy",
n_output_lvs=4, # how many output sets to evolve
n_universe_points=101, # centroid integration grid
)
regressor.fit(X_train, y_train, n_gen=50, pop_size=50)
regressor.print_rules()
# Rule 1: IF x0 IS Low AND x2 IS High THEN output IS Output_3
Every chromosome decodes to a valid trapezoid because each output set’s four breakpoints are sorted before being scaled into the target range.
Winner-takes-all rules#
regressor = BaseFuzzyRulesRegressor(
nRules=20, nAnts=3, rule_mode="sufficient", tolerance=0.05
)
With sufficient only the strongest rule fires per sample, so a crisp-
consequent model becomes piecewise constant: every prediction is one rule’s
consequent, or the target mean where nothing clears the tolerance.
The genetic search optimizes training-set \(R^2\). To estimate
generalization with cross-validation, evaluate the complete estimator through
scikit-learn’s cross_val_score or cross_validate; the estimator must be
refitted independently inside each fold.