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.

fuzzy

Mamdani. 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, BaseEstimator

Scikit-learn-compatible fast Type-1 fuzzy rules regressor.

consequent_type selects how a rule states its output:

crisp

Zero-order Takagi-Sugeno. Each rule carries a number and predictions are the firing-strength-weighted average of those numbers.

fuzzy

Mamdani. 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_mode selects how many rules speak per sample: additive lets every rule contribute, while sufficient keeps only the single strongest rule and falls back to the target mean when even that one fires below tolerance.

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 (default False) keeps only the first of several rules that share the same antecedents. The genetic search can otherwise spend part of the nRules budget 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. With True the 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 (default False) lets a rule also state x IS at least t or x IS at most t for the terms t of an input other than its lowest and highest, the terms being ordered as listed, low to high (True for every input that can have them, or a collection of input names or indexes). compound_terms (default None) maps an input to [(name, [terms])...], each entry a condition x IS <name> whose membership is the bounded sum of the listed terms, which need not be contiguous. Such a condition covers t and every term above (below) it, with the bounded-sum membership min(1, sum of their memberships). The term genes select among 3n - 4 conditions for a variable of n terms, so the search space is larger; the fitted rule base prints IS at least t / IS at most t and rules.rule_conditions reads the operator. Off, the encoding, rules and fits are unchanged.

n_linguistic_variables is the number of terms of each input partition built from the data. It is ignored when linguistic_variables is 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.

predict(X)[source]#

Predict a continuous target for each sample.

score(X, y)[source]#

Return the coefficient of determination of the predictions.

set_fit_request(*, n_gen='$UNCHANGED$', pop_size='$UNCHANGED$', random_state='$UNCHANGED$')#

Configure whether metadata should be requested to be passed to the fit method.

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 (see sklearn.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 to fit if provided. The request is ignored if metadata is not provided.

  • False: metadata is not requested and the meta-estimator will not pass it to fit.

  • 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_gen parameter in fit.

  • pop_size (str, True, False, or None, default=sklearn.utils.metadata_routing.UNCHANGED) – Metadata routing for pop_size parameter in fit.

  • random_state (str, True, False, or None, default=sklearn.utils.metadata_routing.UNCHANGED) – Metadata routing for random_state parameter in fit.

Returns:

self – The updated object.

Return type:

object

print_rules(return_rules=False)[source]#

Print or return the fitted rule base in IF-THEN form.

get_rulebase()[source]#

Return the fitted scalar-consequent rule base.

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: Problem

Optimizer-independent problem for fast optimization of a fixed Type-1 partition.

The integer chromosome always starts with nRules * nAnts feature indices followed by nRules * nAnts term indices (-1 means don’t care). The consequent genes depend on consequent_type:

crisp

nRules scalar consequents encoded into the target range.

fuzzy

nRules output-set indices, then 4 * n_output_lvs trapezoid breakpoints. Each set’s four breakpoints are sorted on decoding, so every chromosome yields a valid trapezoid.

With unique_antecedents a 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_conditions the term genes also select at least t_j and at most t_j conditions (j = 1 .. n - 2 of a variable with n terms), coded after the terms (see fuzzy_sets.ordinal_alphabet_size()), over copies of the given variables. ordinal_conditions may also be a collection of features (names or indexes) to give them to those only, and compound_terms maps 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: _RegressionRuleBase

Type-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.

__init__(antecedents, rule_list, scalar_consequents, y_mean=None, rule_mode='additive', tolerance=0.0, tnorm=<function prod>)[source]#

Create a scalar-consequent Type-1 regression rule base.

inference(X, cached=None)[source]#

Predict continuous outputs with weighted-average inference.

print_rules(return_rules=False)[source]#

Print or return human-readable scalar-consequent rules.

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: _RegressionRuleBase

Type-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.

__init__(antecedents, rule_list, output_sets, y_mean=None, universe=None, n_universe_points=101, rule_mode='additive', tolerance=0.0, tnorm=<function prod>)[source]#

Create a Mamdani regression rule base over the given output sets.

inference(X, cached=None)[source]#

Predict continuous outputs with max-min Mamdani inference.

print_rules(return_rules=False)[source]#

Print or return human-readable linguistic-consequent rules.

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.