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')[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.

__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')[source]#

Configure the regressor; optimization is performed in fit().

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.

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.

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

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)[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.

__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)[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.