Skip to content

API reference

Generated from docstrings with mkdocstrings.

Membership functions

fuzzytool.membership

Membership functions.

Every membership function (MF) is a callable mapping a crisp value (scalar or NumPy array) to a membership degree in [0, 1]. The :class:MembershipFunction Protocol is the only contract the rest of the library relies on: the inference engine never inspects a concrete MF type, so a new shape = a new callable, with no changes to the core (mirroring the "one variant = one impl" design of the sibling project turboswarm).

The lowercase factory functions (:func:tri, :func:trap, :func:gauss, :func:gbell, :func:sigmoid) are the public API; they return small classes so the parameters stay introspectable for visualization and serialization.

MembershipFunction

Bases: Protocol

A callable x -> degree with membership degrees in [0, 1].

Triangular

Triangular MF with feet at a/c and peak at b.

Trapezoidal

Trapezoidal MF; flat top between b and c.

Gaussian

Gaussian MF centered at c with spread sigma.

GeneralizedBell

Generalized bell MF: 1 / (1 + |(x - c) / a|^(2b)).

Sigmoid

Sigmoidal MF: 1 / (1 + exp(-a (x - c))). Monotonic (invertible).

inverse

inverse(y: float) -> float

The x at which membership equals y (for Tsukamoto inference).

RampUp

Monotonically increasing ramp: 0 below a, 1 above b.

RampDown

Monotonically decreasing ramp: 1 below a, 0 above b.

tri

tri(a: float, b: float, c: float) -> Triangular

Triangular MF: feet at a/c, peak at b.

trap

trap(a: float, b: float, c: float, d: float) -> Trapezoidal

Trapezoidal MF: shoulders a/d, flat top b..c.

gauss

gauss(c: float, sigma: float) -> Gaussian

Gaussian MF: center c, spread sigma.

gbell

gbell(a: float, b: float, c: float) -> GeneralizedBell

Generalized bell MF: width a, slope b, center c.

sigmoid

sigmoid(a: float, c: float) -> Sigmoid

Sigmoidal MF: slope a, inflection c.

ramp_up

ramp_up(a: float, b: float) -> RampUp

Increasing ramp MF from 0 at a to 1 at b (monotonic).

ramp_down

ramp_down(a: float, b: float) -> RampDown

Decreasing ramp MF from 1 at a to 0 at b (monotonic).

to_dict

to_dict(mf) -> dict

Serialize a built-in membership function to a JSON-ready dict.

from_dict

from_dict(d: dict)

Rebuild a membership function from :func:to_dict output.

Connectives (t-norms / s-norms)

fuzzytool.norms

Fuzzy connectives: t-norms (AND) and s-norms / t-conorms (OR).

Connectives are plain vectorized callables (a, b) -> result operating elementwise on membership degrees. They are pluggable: the inference engines look them up by name through :func:get_tnorm / :func:get_snorm, so adding a connective means registering one function — the engine never changes.

Norm

Bases: Protocol

A binary connective on membership degrees.

t_min

t_min(a, b)

Minimum (Gödel) t-norm.

t_prod

t_prod(a, b)

Product (algebraic) t-norm.

t_lukasiewicz

t_lukasiewicz(a, b)

Łukasiewicz t-norm: max(0, a + b - 1).

s_max

s_max(a, b)

Maximum (Gödel) s-norm.

s_probor

s_probor(a, b)

Probabilistic OR (algebraic sum): a + b - a*b.

s_lukasiewicz

s_lukasiewicz(a, b)

Łukasiewicz s-norm: min(1, a + b).

get_tnorm

get_tnorm(name: str | Callable) -> Callable

Resolve a t-norm by name (or pass a callable through unchanged).

get_snorm

get_snorm(name: str | Callable) -> Callable

Resolve an s-norm by name (or pass a callable through unchanged).

complement

complement(a)

Standard fuzzy complement (NOT): 1 - a.

Sets, variables, and rule antecedents

fuzzytool.sets

Fuzzy sets, linguistic variables, and the rule-antecedent expression tree.

A :class:Variable is a linguistic variable: a named universe of discourse plus a dictionary of terms (named membership functions). Indexing a variable with a term name (score["good"]) returns a :class:Proposition, the atom of a rule antecedent. Propositions compose with Python operators:

score["poor"] | dti["high"]         # OR  (s-norm)
score["good"] & dti["low"]          # AND (t-norm)
~dti["high"]                        # NOT (complement)

The result is a small expression tree that the inference engine evaluates against crisp inputs to get a firing strength. The same atom doubles as a rule consequent (premium["high"]), so there is a single concept to learn.

Antecedent

Base node of a rule-antecedent expression tree.

eval

eval(inputs: Mapping[str, float], tnorm, snorm) -> np.ndarray

Return the (type-1) firing strength given crisp inputs.

eval_interval

eval_interval(inputs: Mapping[str, float], tnorm, snorm)

Return the firing interval (lower, upper) for IT2 inference.

Type-1 terms collapse to a degenerate interval (d, d), so type-1 and IT2 terms may be mixed freely in the same antecedent.

to_dict

to_dict() -> dict

Serialize this antecedent subtree to a JSON-ready dict.

Proposition

Bases: Antecedent

An atomic variable is term clause; also used as a consequent.

Variable

A linguistic variable: a named universe with named fuzzy-set terms.

Parameters:

Name Type Description Default
name str

identifier; rule inputs are matched to variables by this name.

required
universe tuple[float, float]

(low, high) range of the universe of discourse.

required
terms Iterable[str] | Mapping[str, MembershipFunction] | None

optional list of term names to auto-generate evenly across the universe, or a mapping {name: MembershipFunction}.

None
kind str

shape used by auto-generation ("triangular" or "gauss").

'triangular'
resolution int

number of samples used to discretize the universe for Mamdani defuzzification.

501

auto_terms

auto_terms(names: list[str], kind: str = 'triangular') -> Variable

Generate evenly-spaced terms named names across the universe.

to_dict

to_dict() -> dict

Serialize this variable (universe + terms) to a JSON-ready dict.

Only built-in membership functions can be serialized (see :func:fuzzytool.membership.to_dict).

from_dict classmethod

from_dict(d: dict) -> Variable

Rebuild a variable from :meth:to_dict output.

antecedent_from_dict

antecedent_from_dict(d: dict, variables: Mapping[str, Variable]) -> Antecedent

Rebuild an antecedent tree from :meth:Antecedent.to_dict output.

variables maps variable names to the :class:Variable instances the propositions should reference.

Rules

fuzzytool.rules

Fuzzy rules shared by the inference engines.

A :class:Rule pairs an antecedent expression with a consequent and an optional weight. The consequent is interpreted by the engine: Mamdani expects a :class:~fuzzytool.sets.Proposition (output is term); TSK expects a constant or a linear coefficient vector.

Rule dataclass

IF antecedent THEN consequent with an optional firing weight.

Defuzzification

fuzzytool.defuzz

Defuzzification: collapse an aggregated output set into a crisp value.

Each defuzzifier takes the discretized universe x and the aggregated membership y (same shape) and returns a scalar. They are looked up by name through :func:get_defuzzifier, so adding a method = registering a function.

centroid

centroid(x: ndarray, y: ndarray) -> float

Center of gravity of the area under y (the most common choice).

bisector

bisector(x: ndarray, y: ndarray) -> float

Abscissa that splits the area under y into two equal halves.

mom

mom(x: ndarray, y: ndarray) -> float

Mean of maxima.

som

som(x: ndarray, y: ndarray) -> float

Smallest of maxima.

lom

lom(x: ndarray, y: ndarray) -> float

Largest of maxima.

get_defuzzifier

get_defuzzifier(name: str | Callable) -> Callable

Resolve a defuzzifier by name (or pass a callable through unchanged).

Inference — Mamdani

fuzzytool.inference.mamdani

Mamdani fuzzy inference.

The engine knows nothing about specific membership functions, connectives, or defuzzifiers: t-norm, s-norm, implication, aggregation and defuzzification are all resolved by name (or supplied as callables) and applied uniformly. Adding a behavior means registering a function in :mod:fuzzytool.norms or :mod:fuzzytool.defuzz, never editing this loop.

Pipeline per call: evaluate each rule's antecedent to a firing strength → shape its consequent term over the output universe via the implication operator → aggregate the shaped sets per output variable → defuzzify.

Mamdani

A Mamdani inference system.

Parameters:

Name Type Description Default
tnorm str | Callable

t-norm for AND in antecedents (default "min").

'min'
snorm str | Callable

s-norm for OR in antecedents (default "max").

'max'
implication str

how a firing strength shapes its consequent set — "min" (clip) or "prod" (scale).

'min'
aggregation str | Callable

s-norm combining shaped sets per output (default "max").

'max'
defuzz str | Callable

defuzzification method (default "centroid").

'centroid'

rule

rule(antecedent: Antecedent, consequent: Proposition, weight: float = 1.0) -> Mamdani

Add IF antecedent THEN output is term and return self.

__call__

__call__(**inputs: float)

Run inference. Returns a float for one output, else a dict by name.

predict

predict(**inputs)

Vectorized inference over array-valued inputs.

Each keyword is an array of n samples. Returns an array of length n for a single output, else a dict of arrays. Equivalent to calling the system once per sample, but evaluated in batch.

Inference — Takagi-Sugeno (TSK)

fuzzytool.inference.tsk

Takagi-Sugeno-Kang (TSK) fuzzy inference.

Consequents are crisp functions of the inputs rather than fuzzy sets, so there is no defuzzification: the output is the firing-weighted average of the rule consequents. A consequent may be

  • a number — zero-order (Sugeno constant), e.g. 5.0;
  • a mapping {"const": b0, "x": b1, ...} — first-order linear in the inputs;
  • any callable f(**inputs) -> float — arbitrary.

TSK

A (zero- or first-order) Takagi-Sugeno inference system.

Parameters:

Name Type Description Default
tnorm str | Callable

t-norm for AND in antecedents (default "min").

'min'
snorm str | Callable

s-norm for OR in antecedents (default "max").

'max'

rule

rule(antecedent: Antecedent, consequent, weight: float = 1.0) -> TSK

Add IF antecedent THEN output = consequent and return self.

__call__

__call__(**inputs: float) -> float

Run inference, returning the firing-weighted average output.

predict

predict(**inputs) -> np.ndarray

Vectorized inference over array-valued inputs (n per keyword).

Returns an array of length n (nan for samples where no rule fired).

Inference — Tsukamoto

fuzzytool.inference.tsukamoto

Tsukamoto fuzzy inference.

In a Tsukamoto system every rule's consequent is a monotonic membership function. A rule firing with strength w produces the crisp value at which its consequent reaches w (the inverse of the monotonic MF). The system output is the firing-weighted average of those crisp values — so, like TSK, there is no defuzzification step.

Consequents must expose inverse(degree) -> value; the built-in :func:~fuzzytool.membership.ramp_up, :func:~fuzzytool.membership.ramp_down and :func:~fuzzytool.membership.sigmoid do.

Tsukamoto

A Tsukamoto inference system (monotonic consequents).

Parameters:

Name Type Description Default
tnorm str | Callable

t-norm for AND in antecedents (default "min").

'min'
snorm str | Callable

s-norm for OR in antecedents (default "max").

'max'

rule

rule(antecedent: Antecedent, consequent, weight: float = 1.0) -> Tsukamoto

Add a rule; consequent is a monotonic MF with an inverse.

__call__

__call__(**inputs: float) -> float

Run inference, returning the firing-weighted average crisp output.

Rule learning (Wang-Mendel)

fuzzytool.learn

Learning fuzzy rule bases from data.

:func:wang_mendel implements the classic Wang-Mendel (1992) method: it turns each training sample into one fuzzy rule (picking, per variable, the term with the highest membership), assigns the rule a degree equal to the product of those memberships, and resolves antecedent conflicts by keeping the highest-degree consequent. The result is a ready-to-use :class:~fuzzytool.inference.Mamdani.

wang_mendel

wang_mendel(X: ndarray, y: ndarray, inputs: list[Variable], output: Variable) -> Mamdani

Generate a Mamdani rule base from data with the Wang-Mendel method.

Parameters:

Name Type Description Default
X ndarray

inputs, shape (n_samples, len(inputs)).

required
y ndarray

targets, shape (n_samples,).

required
inputs list[Variable]

the input linguistic variables (each pre-populated with terms); column j of X maps to inputs[j].

required
output Variable

the output linguistic variable (pre-populated with terms).

required

Returns:

Name Type Description
A Mamdani

class:~fuzzytool.inference.Mamdani with one rule per distinct

Mamdani

antecedent (conflicts resolved by rule degree).

Fuzzy numbers

fuzzytool.fuzzynum

Fuzzy numbers and their arithmetic.

A fuzzy number is a convex, normal fuzzy set on the reals. This module provides the two most used shapes — triangular (TFN) and trapezoidal (TrFN) — with standard fuzzy arithmetic (+ - * / and scalar scaling), alpha-cuts, a crisp (centroid) value, and a vertex distance used by fuzzy MCDM (see :mod:fuzzytool.mcdm).

Multiplication and division use the common positive-support approximation (operate on the ordered defining points), which is exact for +/- and a good approximation for *// when supports are positive.

FuzzyNumber

Base class for fuzzy numbers defined by ordered points p.

distance

distance(other: FuzzyNumber) -> float

Vertex distance to another fuzzy number of the same shape.

TriangularFuzzyNumber

Bases: FuzzyNumber

Triangular fuzzy number (a, b, c) with peak at b.

TrapezoidalFuzzyNumber

Bases: FuzzyNumber

Trapezoidal fuzzy number (a, b, c, d) with flat top [b, c].

tfn

tfn(a: float, b: float, c: float) -> TriangularFuzzyNumber

Shortcut for :class:TriangularFuzzyNumber.

trfn

trfn(a: float, b: float, c: float, d: float) -> TrapezoidalFuzzyNumber

Shortcut for :class:TrapezoidalFuzzyNumber.

rank

rank(numbers: list[FuzzyNumber]) -> list[int]

Indices that sort numbers from largest to smallest by centroid.

Fuzzy MCDM

fuzzytool.mcdm

Fuzzy multi-criteria decision making (MCDM).

  • :func:fuzzy_topsis — Chen's (2000) fuzzy TOPSIS: rank alternatives by their closeness to the fuzzy positive-ideal solution.
  • :func:fuzzy_ahp — Chang's (1996) extent-analysis AHP: derive crisp criterion weights from a triangular-fuzzy pairwise-comparison matrix.

Inputs are triangular fuzzy numbers (:class:~fuzzytool.fuzzynum.TriangularFuzzyNumber).

TopsisResult dataclass

Result of :func:fuzzy_topsis.

Attributes:

Name Type Description
closeness ndarray

closeness coefficient CC per alternative (higher = better).

ranking list[int]

alternative indices ordered best-to-worst.

d_plus ndarray

distance to the fuzzy positive-ideal solution per alternative.

d_minus ndarray

distance to the fuzzy negative-ideal solution per alternative.

fuzzy_topsis

fuzzy_topsis(matrix: list, weights: list, benefit: list) -> TopsisResult

Rank alternatives with Chen's fuzzy TOPSIS.

Parameters:

Name Type Description Default
matrix list

m x n ratings as TriangularFuzzyNumber (m alternatives, n criteria).

required
weights list

n criterion weights as TriangularFuzzyNumber.

required
benefit list

length-n booleans — True if a criterion is to be maximized (benefit), False if minimized (cost).

required

Returns:

Name Type Description
A TopsisResult

class:TopsisResult.

fuzzy_ahp

fuzzy_ahp(matrix: list) -> np.ndarray

Crisp criterion weights from a fuzzy pairwise matrix (Chang's method).

Parameters:

Name Type Description Default
matrix list

n x n pairwise comparisons as TriangularFuzzyNumber; the diagonal is (1, 1, 1) and matrix[j][i] is the reciprocal of matrix[i][j].

required

Returns:

Type Description
ndarray

A length-n array of normalized, non-negative weights summing to 1.

Serialization

fuzzytool.serialize

Save and load fuzzy inference systems as JSON.

Supports :class:~fuzzytool.inference.Mamdani and :class:~fuzzytool.inference.TSK systems whose connectives/defuzzifier were given by name and whose variables use built-in membership functions. TSK consequents must be numbers or coefficient mappings (callable consequents cannot be serialized).

fz.save(system, "system.json")
system = fz.load("system.json")

to_dict

to_dict(system) -> dict

Serialize a Mamdani or TSK system to a JSON-ready dict.

from_dict

from_dict(d: dict)

Rebuild a Mamdani or TSK system from :func:to_dict output.

save

save(system, path: str) -> None

Write system to path as JSON.

load

load(path: str)

Read a system back from a JSON file written by :func:save.

Interval type-2 — sets

fuzzytool.type2.sets

Interval type-2 (IT2) membership functions.

An :class:IntervalType2MF carries a lower (LMF) and an upper (UMF) type-1 membership function. Calling it returns the membership interval (lower, upper); the engines also call .lower(x) / .upper(x) directly.

The constructors cover the standard ways of building an FOU from a type-1 set:

  • :func:it2 — explicit LMF/UMF;
  • :func:it2_scale — height uncertainty (LMF is a scaled-down UMF);
  • :func:it2_gauss_uncertain_mean — a Gaussian with mean in [c1, c2];
  • :func:it2_gauss_uncertain_std — a Gaussian with uncertain spread.

IntervalType2MF

An IT2 set defined by a lower (LMF) and an upper (UMF) type-1 MF.

The UMF must dominate the LMF everywhere (LMF(x) <= UMF(x)); this is not enforced at construction (it would require sampling the universe) but holds for every set built through the constructors below.

it2

it2(lower: MembershipFunction, upper: MembershipFunction) -> IntervalType2MF

An IT2 set from explicit lower and upper type-1 membership functions.

it2_scale

it2_scale(mf: MembershipFunction, scale: float) -> IntervalType2MF

Height-uncertainty FOU: UMF is mf, LMF is scale * mf (0 < scale ≤ 1).

it2_gauss_uncertain_mean

it2_gauss_uncertain_mean(c1: float, c2: float, sigma: float) -> IntervalType2MF

Gaussian with an uncertain mean spanning [c1, c2] (c1 < c2).

The UMF is the upper envelope of all Gaussians with mean in [c1, c2] (flat-topped at 1 between the means); the LMF is their lower envelope.

it2_gauss_uncertain_std

it2_gauss_uncertain_std(c: float, sigma_lo: float, sigma_hi: float) -> IntervalType2MF

Gaussian with an uncertain spread: UMF uses the wider σ, LMF the narrower.

Interval type-2 — inference

fuzzytool.type2.inference

Interval type-2 inference engines.

Both engines evaluate each rule's antecedent to a firing interval [f_low, f_high] (via :meth:Antecedent.eval_interval) and then collapse the rule base with Karnik-Mendel type reduction, returning the midpoint of the type-reduced interval [y_l, y_r].

  • :class:IT2Mamdani uses center-of-sets type reduction: each consequent IT2 set is summarized by its centroid interval (computed once and cached), and KM combines those centroids weighted by the firing intervals.
  • :class:IT2TSK has crisp consequents (numbers, coefficient mappings, or callables) and applies KM directly to them.

IT2Mamdani

Interval type-2 Mamdani inference (center-of-sets type reduction).

Parameters:

Name Type Description Default
tnorm str | Callable

t-norm for AND in antecedents (default "min").

'min'
snorm str | Callable

s-norm for OR in antecedents (default "max").

'max'

rule

rule(antecedent: Antecedent, consequent: Proposition, weight: float = 1.0) -> IT2Mamdani

Add IF antecedent THEN output is term and return self.

__call__

__call__(**inputs: float)

Run inference. Returns a float for one output, else a dict by name.

IT2TSK

Interval type-2 Takagi-Sugeno inference (KM over crisp consequents).

Consequents follow the same convention as the type-1 :class:~fuzzytool.inference.tsk.TSK engine: a number, a coefficient mapping {"const": b0, "x": b1, ...}, or a callable f(**inputs) -> float.

rule

rule(antecedent: Antecedent, consequent, weight: float = 1.0) -> IT2TSK

Add IF antecedent THEN output = consequent and return self.

__call__

__call__(**inputs: float) -> float

Run inference, returning the midpoint of the type-reduced interval.

Interval type-2 — type reduction

fuzzytool.type2.reduction

Karnik-Mendel type reduction.

Type reduction turns the interval-valued output of an IT2 system into a crisp interval [y_l, y_r] (typically defuzzified as its midpoint). The Karnik-Mendel (KM) algorithm finds each endpoint by locating the switch point where the per-point weight flips between its lower and upper bound.

The single primitive :func:km_endpoint is reused everywhere: for an IT2 set's centroid (points = universe samples, weights = the FOU) and for an IT2 rule base (points = consequent centroids, weights = rule firing intervals).

km_endpoint

km_endpoint(points: ndarray, lower: ndarray, upper: ndarray, side: str = 'l', max_iter: int = 100) -> float

One endpoint of the type-reduced interval via Karnik-Mendel.

Parameters:

Name Type Description Default
points ndarray

the y-values (e.g. consequent centroids or universe samples).

required
lower ndarray

per-point weight lower bounds (same shape as points).

required
upper ndarray

per-point weight upper bounds (same shape as points).

required
side str

"l" for the left endpoint y_l, "r" for the right y_r.

'l'
max_iter int

safeguard on the Karnik-Mendel iteration count.

100

Left endpoint uses the upper weights to the left of the switch and the lower weights to its right; the right endpoint mirrors this.

karnik_mendel

karnik_mendel(points, lower, upper) -> tuple[float, float]

Type-reduce to the interval (y_l, y_r) over a shared set of points.

centroid_it2

centroid_it2(it2mf, universe) -> tuple[float, float]

Centroid interval (c_l, c_r) of an IT2 set over universe.

Fuzzy clustering

fuzzytool.cluster

Fuzzy clustering.

Unlike crisp k-means, fuzzy clustering lets each sample belong to several clusters with graded membership. This module provides three algorithms and a small set of validity metrics, all on plain NumPy arrays of shape (n_samples, n_features):

  • :func:fuzzy_cmeans — Bezdek's FCM (spherical clusters, Euclidean norm).
  • :func:gustafson_kessel — GK, an adaptive norm per cluster that captures ellipsoidal shapes.
  • :func:possibilistic_cmeans — PCM, which drops the "memberships sum to 1" constraint so outliers get low typicality in every cluster.

Each returns a :class:ClusterResult. Every algorithm accepts a seed for reproducibility.

ClusterResult dataclass

Outcome of a fuzzy clustering run.

Attributes:

Name Type Description
centers ndarray

cluster prototypes, shape (c, n_features).

u ndarray

membership/typicality matrix, shape (c, n_samples).

n_iter int

iterations run until convergence.

objective float

final value of the algorithm's objective function.

labels ndarray

hard assignment argmax over u, shape (n_samples,).

fuzzy_cmeans

fuzzy_cmeans(X: ndarray, c: int, m: float = 2.0, max_iter: int = 150, tol: float = 1e-05, seed: int | None = None) -> ClusterResult

Bezdek's fuzzy c-means.

Parameters:

Name Type Description Default
X ndarray

data, shape (n_samples, n_features).

required
c int

number of clusters.

required
m float

fuzziness exponent (> 1; 2.0 is standard).

2.0
max_iter int

maximum iterations.

150
tol float

stop when the membership matrix changes by less than this (max-norm).

1e-05
seed int | None

RNG seed for the membership initialization.

None

gustafson_kessel

gustafson_kessel(X: ndarray, c: int, m: float = 2.0, max_iter: int = 150, tol: float = 1e-05, seed: int | None = None, reg: float = 1e-10) -> ClusterResult

Gustafson-Kessel clustering (adaptive per-cluster Mahalanobis norm).

Each cluster learns a covariance-shaped, unit-determinant norm, so GK fits ellipsoidal clusters that FCM (fixed spherical norm) cannot.

Parameters:

Name Type Description Default
X ndarray

data, shape (n_samples, n_features).

required
c int

number of clusters.

required
m float

fuzziness exponent (> 1).

2.0
max_iter int

maximum iterations.

150
tol float

convergence threshold on the membership matrix (max-norm).

1e-05
seed int | None

RNG seed for initialization.

None
reg float

ridge added to each fuzzy covariance for numerical stability.

1e-10

possibilistic_cmeans

possibilistic_cmeans(X: ndarray, c: int, m: float = 2.0, max_iter: int = 150, tol: float = 1e-05, seed: int | None = None, k: float = 1.0) -> ClusterResult

Possibilistic c-means (Krishnapuram-Keller).

PCM drops the probabilistic constraint that memberships sum to 1: each value is a typicality in [0, 1], so noise points score low everywhere. It is initialized from an FCM run, which also fixes each cluster's scale eta.

Parameters:

Name Type Description Default
X ndarray

data, shape (n_samples, n_features).

required
c int

number of clusters.

required
m float

fuzziness exponent (> 1).

2.0
max_iter int

maximum iterations.

150
tol float

convergence threshold on the typicality matrix (max-norm).

1e-05
seed int | None

RNG seed (passed to the initializing FCM).

None
k float

scale multiplier for the bandwidth eta (1.0 is standard).

1.0

partition_coefficient

partition_coefficient(u: ndarray) -> float

Bezdek's partition coefficient in (1/c, 1]; higher = crisper.

partition_entropy

partition_entropy(u: ndarray) -> float

Partition entropy in [0, log c); lower = crisper.

xie_beni

xie_beni(X, centers: ndarray, u: ndarray, m: float = 2.0) -> float

Xie-Beni index (compactness / separation); lower is better.

ANFIS

fuzzytool.anfis

ANFIS — an adaptive network-based fuzzy inference system (Jang, 1993).

ANFIS is a first-order Takagi-Sugeno system whose parameters are learned from data. With a grid partition, each of the p inputs carries n_mf Gaussian membership functions, giving n_mf ** p rules; rule i fires with the product of its inputs' memberships and emits an affine function of the inputs.

Training uses Jang's hybrid scheme, one pass per epoch:

  1. with the premise (Gaussian) parameters fixed, the consequent (affine) parameters are solved in closed form by least squares — the output is linear in them;
  2. with the consequents fixed, the premise centers and widths take a gradient-descent step on the mean squared error.

Pure NumPy; intended for low-dimensional problems (the rule count grows as n_mf ** p).

ANFIS

A trainable first-order Sugeno system over a grid partition.

Parameters:

Name Type Description Default
n_inputs int

number of input features p.

required
n_mf int

Gaussian membership functions per input (rules = n_mf ** p).

3
learning_rate float

step size for the premise gradient updates.

0.05

fit

fit(X, y, epochs: int = 100) -> ANFIS

Train on X (n, p) and targets y (n,) for epochs epochs.

predict

predict(X) -> np.ndarray

Predict outputs for X (n, p). Requires a prior :meth:fit.

get_params

get_params(deep: bool = True) -> dict

Hyperparameters, for scikit-learn compatibility (Pipeline/GridSearch).

set_params

set_params(**params) -> ANFIS

Set hyperparameters (resets trained state), returning self.

F-transform

fuzzytool.ftransform

F-transform — the fuzzy transform of Perfilieva.

The F-transform projects a function onto a fuzzy partition of its domain. Over a uniform partition by triangular basis functions A_1..A_n that satisfy the Ruspini condition (they sum to 1 everywhere on [a, b]):

  • the direct transform reduces the data to n components, each a membership-weighted average of the samples falling under one basis function;
  • the inverse transform reconstructs an approximation f̂(x) = Σ_k F_k · A_k(x).

With few components the round trip smooths/denoises a signal; with many it approximates it closely. Pure NumPy.

FTransform

A triangular F-transform over a uniform fuzzy partition of [a, b].

Parameters:

Name Type Description Default
a float

left end of the domain.

required
b float

right end of the domain (b > a).

required
n_basis int

number of basis functions / components (>= 2).

required

basis

basis(x) -> np.ndarray

Triangular basis matrix A of shape (len(x), n_basis).

Columns form a partition of unity on [a, b] (each row sums to 1).

direct

direct(x, y) -> np.ndarray

Direct F-transform: the n_basis components from samples (x, y).

inverse

inverse(x, components: ndarray | None = None) -> np.ndarray

Inverse F-transform: reconstruct values at x from components.

fit

fit(x, y) -> FTransform

Compute and store the direct transform of (x, y); returns self.

smooth

smooth(x) -> np.ndarray

Convenience: direct-then-inverse at x (requires a prior fit).

Visualization

fuzzytool.viz

Matplotlib visualization helpers.

Importing this module requires matplotlib (an optional dependency; install with pip install fuzzytool[viz]). Visualization is a first-class priority of the project, mirroring its sibling turboswarm.

plot_variable

plot_variable(variable: Variable, ax=None)

Plot every term's membership function over the variable's universe.

plot_it2_variable

plot_it2_variable(variable: Variable, ax=None)

Plot an IT2 variable: each term's lower/upper MF with a shaded FOU.

Type-1 terms (if any are mixed in) are drawn as a single line.

control_surface

control_surface(system, x_var: Variable, y_var: Variable, n: int = 41, ax=None)

Plot a system's output as a surface over two input variables.

system must be callable as system(**{x_var.name: x, y_var.name: y}) and return a scalar (single-output Mamdani or TSK).

plot_clusters

plot_clusters(X, result, ax=None)

Scatter 2-D data colored by hard label, with cluster centers marked.

result is a :class:~fuzzytool.cluster.ClusterResult. Point opacity encodes the top membership, so fuzzy/boundary points appear fainter.

Integrations — pandas

fuzzytool.integrations.pandas

pandas integration: DataFrame in/out and tabular views of fuzzy objects.

Install with pip install fuzzytool[pandas]. Nothing here imports pandas at module load beyond a guarded check, so the rest of the toolkit stays dependency -free.

  • :func:predict_df — batch inference straight from a DataFrame.
  • :func:rules_dataframe — a readable table of a system's rule base.
  • :func:memberships_dataframe — a clustering result's membership matrix.
  • :func:components_dataframe — an F-transform's components.

predict_df

predict_df(system: Mamdani | TSK, df: DataFrame, columns: list[str] | None = None) -> pd.Series | pd.DataFrame

Run vectorized inference over a DataFrame.

Parameters:

Name Type Description Default
system Mamdani | TSK

a :class:~fuzzytool.inference.Mamdani or :class:~fuzzytool.inference.TSK system.

required
df DataFrame

a DataFrame whose columns include the system's input variables.

required
columns list[str] | None

input column names to use, in order. Defaults to the variables the system's rules reference (see :func:~fuzzytool.integrations._util.input_variable_names).

None

Returns:

Type Description
Series | DataFrame

A Series (single output, named after the output variable) or a

Series | DataFrame

DataFrame (multiple outputs), aligned to df's index.

rules_dataframe

rules_dataframe(system)

Return the system's rule base as a DataFrame (one row per rule).

Columns: antecedent (the IF part as text), consequent (the THEN part) and weight.

memberships_dataframe

memberships_dataframe(result, index=None, prefix: str = 'cluster')

Return a clustering result's membership matrix as a DataFrame.

One row per sample, one column {prefix}_{k} per cluster, plus a label column holding the hard (argmax) assignment.

components_dataframe

components_dataframe(ftransform)

Return an :class:~fuzzytool.ftransform.FTransform's components as a DataFrame.

Columns: node (the basis-function center) and component (its value).

Integrations — scikit-learn

fuzzytool.integrations.sklearn

scikit-learn integration: fuzzy systems as estimators and transformers.

These objects follow the scikit-learn estimator protocol (fit returns self, plus predict/transform, get_params/set_params and a score/fit_transform), so they slot into a Pipeline, GridSearchCV or cross_val_score — yet, like :class:~fuzzytool.anfis.ANFIS, they do not import scikit-learn. The [sklearn] extra simply installs scikit-learn so you have those tools available:

pip install fuzzytool[sklearn]
  • :class:Fuzzifier — a transformer turning crisp features into fuzzy membership-degree features (one column per term).
  • :class:WangMendelRegressor — learns a Mamdani rule base from data (Wang-Mendel) and predicts with it.
  • :class:FuzzySystemRegressor — wraps an already-built Mamdani/TSK system so it can be scored or pipelined.

Fuzzifier

Bases: _Estimator

Expand crisp features into fuzzy membership-degree features.

Column j of X is interpreted through variables[j]: each of that variable's terms becomes one output column holding the membership degree. The result is a soft, interpretable encoding that downstream linear or tree models can consume.

Parameters:

Name Type Description Default
variables list[Variable]

one :class:~fuzzytool.sets.Variable per input column, in column order.

required

WangMendelRegressor

Bases: _Estimator

A regressor that learns a Mamdani rule base from data (Wang-Mendel).

Parameters:

Name Type Description Default
inputs list[Variable]

input linguistic variables (pre-populated with terms), one per column of X.

required
output Variable

the output linguistic variable (pre-populated with terms).

required

FuzzySystemRegressor

Bases: _Estimator

Wrap an already-built single-output fuzzy system as an sklearn regressor.

fit does not change the system (it is already defined); it only records the input column order. Use this to cross-validate, score, or pipeline a hand-written Mamdani/TSK system.

Parameters:

Name Type Description Default
system Mamdani | TSK

a single-output Mamdani or TSK system.

required
columns list[str] | None

input variable names matching X's columns, in order. Defaults to the variables the system's rules reference.

None

Integrations — SciPy

fuzzytool.integrations.scipy

SciPy integration: tune a fuzzy system's membership functions to data.

Install with pip install fuzzytool[scipy]. :func:tune adjusts the parameters of a system's built-in membership functions so its output fits a dataset, using :func:scipy.optimize.least_squares. It mutates the system in place and returns SciPy's OptimizeResult (.x, .cost, .nfev, .success …) — a drop-in way to refine a hand-written Mamdani/TSK system.

Only built-in membership shapes (tri, trap, gauss, gbell, sigmoid, ramp_up, ramp_down) are tunable; custom callables are left untouched. Shape validity is preserved every iteration (breakpoints are kept ordered, widths kept positive), so the optimizer explores freely without producing degenerate sets.

tune

tune(system: Mamdani | TSK, X: ndarray, y: ndarray, columns: list[str] | None = None, tune_outputs: bool = True, **least_squares_kwargs) -> OptimizeResult

Fit a system's membership-function parameters to data.

Any extra keyword arguments are forwarded to :func:scipy.optimize.least_squares (e.g. max_nfev).

Parameters:

Name Type Description Default
system Mamdani | TSK

a Mamdani or TSK system (mutated in place with the best params).

required
X ndarray

inputs, shape (n_samples, n_inputs).

required
y ndarray

targets, shape (n_samples,).

required
columns list[str] | None

input variable names matching X's columns, in order. Defaults to the variables the rules reference.

None
tune_outputs bool

also tune the output variables' MFs (Mamdani only).

True

Returns:

Type Description
OptimizeResult

The SciPy OptimizeResult.

Integrations — Optuna

fuzzytool.integrations.optuna

Optuna integration: search fuzzy-system structure and hyperparameters.

Install with pip install fuzzytool[optuna]. Fuzzy systems have plenty of discrete/continuous knobs — which t-norm, which defuzzifier, how many membership functions, what learning rate — that are awkward to grid-search by hand. These helpers turn an Optuna trial into a configured system, plus a ready-made :func:tune_anfis study.

  • :func:suggest_inference_spec — sample the connectives/implication/defuzz of a Mamdani system from a trial.
  • :func:suggest_anfis — build an :class:~fuzzytool.anfis.ANFIS with a trial-suggested n_mf and learning_rate.
  • :func:tune_anfis — run a full study that minimizes training RMSE and returns the best (refit) model.

suggest_inference_spec

suggest_inference_spec(trial, prefix: str = '') -> dict

Sample a Mamdani spec {tnorm, snorm, implication, defuzz} from a trial.

prefix namespaces the parameter names so several specs can coexist in one study. Pass the result straight to :class:~fuzzytool.inference.Mamdani.

suggest_anfis

suggest_anfis(trial, n_inputs: int, n_mf_range: tuple[int, int] = (2, 5), lr_range: tuple[float, float] = (0.001, 0.2)) -> ANFIS

Build an ANFIS with a trial-suggested n_mf and learning_rate.

tune_anfis

tune_anfis(X: ndarray, y: ndarray, n_trials: int = 20, n_mf_range: tuple[int, int] = (2, 5), lr_range: tuple[float, float] = (0.001, 0.2), epochs: int = 100, seed: int | None = None) -> tuple

Tune an ANFIS's n_mf/learning_rate with an Optuna study.

Parameters:

Name Type Description Default
X ndarray

inputs, shape (n_samples, n_inputs).

required
y ndarray

targets, shape (n_samples,).

required
n_trials int

number of Optuna trials.

20
n_mf_range tuple[int, int]

(min, max) membership functions per input to search.

(2, 5)
lr_range tuple[float, float]

(min, max) learning rate to search (log scale).

(0.001, 0.2)
epochs int

training epochs per trial.

100
seed int | None

seed for Optuna's sampler (reproducible search).

None

Returns:

Type Description
tuple

A (best_model, study) tuple: a fresh ANFIS trained with the best

tuple

hyperparameters, and the Optuna study.

Integrations — Joblib / Dask

fuzzytool.integrations.parallel

Parallel execution helpers (Joblib and Dask).

Install with pip install fuzzytool[parallel] (Joblib) or fuzzytool[dask] (Dask). These spread embarrassingly-parallel fuzzy workloads across cores or a Dask cluster:

  • :func:parallel_predict — chunked batch inference with Joblib.
  • :func:multi_start_cmeans — run fuzzy c-means from many seeds in parallel and keep the best (FCM is sensitive to initialization).
  • :func:dask_predict — the same chunked inference on a Dask scheduler.

.. note:: The process backends pickle the system. A hand-written Mamdani/TSK built from the built-in shapes pickles fine; a TSK with lambda consequents does not — pass backend="threading" for those.

parallel_predict

parallel_predict(system: Mamdani | TSK, X: ndarray, columns: list[str] | None = None, n_jobs: int = -1, n_chunks: int | None = None, backend: str = 'loky') -> np.ndarray | dict

Run batch inference over X in parallel chunks with Joblib.

Parameters:

Name Type Description Default
system Mamdani | TSK

a Mamdani or TSK system.

required
X ndarray

inputs, shape (n_samples, n_inputs).

required
columns list[str] | None

input variable names matching X's columns, in order. Defaults to the variables the rules reference.

None
n_jobs int

Joblib worker count (-1 = all cores).

-1
n_chunks int | None

number of row chunks (defaults to 4 * n_jobs or 8).

None
backend str

Joblib backend ("loky" processes, "threading" threads).

'loky'

Returns:

Type Description
ndarray | dict

The stacked predictions — an array (single output) or a dict of arrays.

multi_start_cmeans

multi_start_cmeans(X: ndarray, c: int, n_starts: int = 8, n_jobs: int = -1, backend: str = 'loky', **kwargs) -> ClusterResult

Run :func:~fuzzytool.cluster.fuzzy_cmeans from many seeds; keep the best.

Fuzzy c-means converges to a local optimum that depends on initialization; running several seeds and keeping the lowest-objective result is a standard safeguard. The runs are independent, so they parallelize cleanly. Extra keyword arguments are forwarded to :func:~fuzzytool.cluster.fuzzy_cmeans.

Parameters:

Name Type Description Default
X ndarray

data, shape (n_samples, n_features).

required
c int

number of clusters.

required
n_starts int

number of random restarts (seeds 0 .. n_starts - 1).

8
n_jobs int

Joblib worker count (-1 = all cores).

-1
backend str

Joblib backend.

'loky'

Returns:

Type Description
ClusterResult

The best :class:~fuzzytool.cluster.ClusterResult (minimum objective).

dask_predict

dask_predict(system: Mamdani | TSK, X: ndarray, columns: list[str] | None = None, n_chunks: int | None = None) -> np.ndarray | dict

Run chunked batch inference on a Dask scheduler.

Mirrors :func:parallel_predict but builds a Dask graph (dask.delayed) and computes it, so it scales to a distributed cluster.

Parameters:

Name Type Description Default
system Mamdani | TSK

a Mamdani or TSK system.

required
X ndarray

inputs, shape (n_samples, n_inputs).

required
columns list[str] | None

input variable names matching X's columns.

None
n_chunks int | None

number of row chunks (default 8).

None

Returns:

Type Description
ndarray | dict

The stacked predictions — an array or a dict of arrays.

Integrations — LLM agents

fuzzytool.integrations.agents

LLM-agent integration: expose a fuzzy system as an explainable tool.

Install with pip install fuzzytool[agents]. A fuzzy inference system is an unusually good citizen for tool-using LLMs: it is deterministic, bounded, and — unlike a black-box model — it can say why it produced an answer by reporting which rules fired and how strongly.

  • :func:explain — run a system and return its crisp output plus the fired rules (no third-party dependency; useful on its own).
  • :func:inference_tool — wrap a system as a LangChain StructuredTool an agent can call.

explain

explain(system, **inputs) -> dict

Run system on inputs and explain the result.

Returns a dict with the crisp output (a float, or a dict for a multi-output Mamdani) and fired_rules: the rules whose firing strength is positive, each {"rule", "firing"}, sorted strongest-first. This is the payload an agent (or a human) needs to trust the answer.

inference_tool

inference_tool(system: Mamdani | TSK, columns: list[str] | None = None, name: str = 'fuzzy_inference', description: str | None = None) -> StructuredTool

Wrap a fuzzy system as a LangChain StructuredTool.

The tool takes one float argument per input variable and returns the crisp output together with the rules that fired — so the agent can both use and explain the system.

Parameters:

Name Type Description Default
system Mamdani | TSK

a Mamdani or TSK system.

required
columns list[str] | None

input variable names (the tool's arguments). Defaults to the variables the rules reference.

None
name str

tool name exposed to the agent.

'fuzzy_inference'
description str | None

tool description; a sensible default is generated.

None

Returns:

Type Description
StructuredTool

A langchain_core.tools.StructuredTool.

Datasets

fuzzytool.datasets

Ready-made example systems.

credit_risk

credit_risk() -> tuple[Mamdani, Variable, Variable, Variable]

A credit-risk-premium Mamdani system.

Given a borrower's credit score (300-850) and dti (debt-to-income ratio, 0-50%), recommend the premium (risk points, 0-12) a lender should add on top of its base interest rate. Returns (system, score, dti, premium) so callers can inspect the variables (e.g. for plotting).

sys, score, dti, premium = credit_risk() safe = sys(score=800, dti=10) # great score, low leverage risky = sys(score=520, dti=42) # poor score, high leverage safe < risky True

credit_risk_it2

credit_risk_it2() -> tuple[IT2Mamdani, Variable, Variable, Variable]

An interval type-2 version of :func:credit_risk.

The score and premium terms carry a footprint of uncertainty (uncertain Gaussian means), modeling vagueness in how a "good" score or a "low" premium is defined. Returns (system, score, dti, premium).

sys, score, dti, premium = credit_risk_it2() sys(score=800, dti=10) < sys(score=520, dti=42) True

make_blobs

make_blobs(centers=((0.0, 0.0), (6.0, 6.0), (0.0, 6.0)), n_per: int = 60, spread: float = 0.7, seed: int | None = 0) -> np.ndarray

Synthetic isotropic Gaussian blobs for clustering demos and tests.

Returns the stacked data X of shape (len(centers) * n_per, n_features).

X = make_blobs(seed=0) X.shape (180, 2)