Skip to content

Models

Concrete TSK model variants.

All public model classes are re-exported from their respective sub-modules.

ADATSKClassifierModel

Bases: BaseTSKClassifierModel

TSK classifier with adaptive softmin antecedent (ADATSK).

The firing strength of each rule is computed with the Ada-softmin operator.

Reference

G. Xue, Q. Chang, J. Wang, K. Zhang and N. R. Pal, "An Adaptive Neuro-Fuzzy System With Integrated Feature Selection and Rule Extraction for High-Dimensional Classification Problems," in IEEE Transactions on Fuzzy Systems, vol. 31, no. 7, pp. 2167-2181, July 2023, doi: 10.1109/TFUZZ.2022.3220950.

Initialise the ADATSK classifier.

Parameters:

Name Type Description Default
input_mfs Mapping[str, Sequence[MembershipFunction]]

Mapping from feature name to a sequence of :class:~highfis.memberships.MembershipFunction objects.

required
n_classes int

Number of output classes (must be ≥ 2).

required
rule_base str

Rule-base construction strategy. "coco" (default, same-index compact), "cartesian" (all MF combinations), "en" (enhanced FRB), or "custom" (explicit rules via rules).

'coco'
rules Sequence[Sequence[int]] | None

Explicit rule antecedent indices.

None
defuzzifier nn.Module | None

Custom defuzzifier. Defaults to :class:~highfis.defuzzifiers.SumBasedDefuzzifier.

None
consequent_batch_norm bool

Batch normalisation on consequent inputs.

False
eps float | None

Numerical stability epsilon for the Ada-softmin operator.

None
zero_consequent_init bool

If True (default), initialize consequent weights and biases to zeros.

True

Raises:

Type Description
ValueError

If n_classes < 2.

Source code in highfis/models/_adaptive.py
def __init__(
    self,
    input_mfs: Mapping[str, Sequence[MembershipFunction]],
    n_classes: int,
    rule_base: str = "coco",
    rules: Sequence[Sequence[int]] | None = None,
    defuzzifier: nn.Module | None = None,
    consequent_batch_norm: bool = False,
    eps: float | None = None,
    zero_consequent_init: bool = True,
) -> None:
    """Initialise the ADATSK classifier.

    Args:
        input_mfs: Mapping from feature name to a sequence of
            :class:`~highfis.memberships.MembershipFunction` objects.
        n_classes: Number of output classes (must be ≥ 2).
        rule_base: Rule-base construction strategy.  ``"coco"``
            (default, same-index compact), ``"cartesian"`` (all MF
            combinations), ``"en"`` (enhanced FRB), or ``"custom"``
            (explicit rules via *rules*).
        rules: Explicit rule antecedent indices.
        defuzzifier: Custom defuzzifier.  Defaults to
            :class:`~highfis.defuzzifiers.SumBasedDefuzzifier`.
        consequent_batch_norm: Batch normalisation on consequent inputs.
        eps: Numerical stability epsilon for the Ada-softmin operator.
        zero_consequent_init: If ``True`` (default), initialize
            consequent weights and biases to zeros.

    Raises:
        ValueError: If ``n_classes < 2``.
    """
    if n_classes < 2:
        raise ValueError("n_classes must be >= 2")

    self.n_classes = int(n_classes)
    self.eps = eps
    self.zero_consequent_init = bool(zero_consequent_init)

    super().__init__(
        input_mfs,
        rule_base=rule_base,
        t_norm="prod",
        rules=rules,
        defuzzifier=defuzzifier or SumBasedDefuzzifier(),
        consequent_batch_norm=consequent_batch_norm,
    )

    self.rule_layer = AdaSoftminRuleLayer(
        self.input_names,
        [len(input_mfs[name]) for name in self.input_names],
        rules=rules,
        rule_base=rule_base,
        eps=self.eps,
    )
    if self.zero_consequent_init:
        self._zero_initialize_consequents()

forward

Full forward pass through the TSK pipeline.

Source code in highfis/models/_base.py
def forward(self, x: Tensor) -> Tensor:
    """Full forward pass through the TSK pipeline."""
    output, _ = self._forward_train(x)
    return output

forward_antecedents

Compute normalized rule strengths from model antecedents.

Source code in highfis/models/_base.py
def forward_antecedents(self, x: Tensor) -> Tensor:
    """Compute normalized rule strengths from model antecedents."""
    mu = self.membership_layer(x)
    w = self.rule_layer(mu)
    return self.defuzzifier(w)

get_consequent_weights

Return the consequent layer weights or None when unavailable.

Source code in highfis/models/_base.py
def get_consequent_weights(self) -> Tensor | None:
    """Return the consequent layer weights or ``None`` when unavailable."""
    weight = getattr(self.consequent_layer, "weight", None)
    if isinstance(weight, Tensor):
        return weight.detach()
    return None

get_mf_params

Return a serializable description of the model's membership functions.

Source code in highfis/models/_base.py
def get_mf_params(self) -> dict[str, list[dict[str, Any]]]:
    """Return a serializable description of the model's membership functions."""
    return {
        name: [{"type": type(mf).__name__, **mf.inspect_params()} for mf in cast(Sequence[MembershipFunction], mfs)]
        for name, mfs in self.membership_layer.input_mfs.items()
    }

get_rule_table

Return the rule base as a table of feature-to-MF indices.

Source code in highfis/models/_base.py
def get_rule_table(self) -> list[dict[str, Any]]:
    """Return the rule base as a table of feature-to-MF indices."""
    return [
        dict(
            rule_id=rule_index,
            **dict(zip(self.rule_layer.input_names, rule, strict=False)),
        )
        for rule_index, rule in enumerate(self.rule_layer.rules)
    ]

predict

Return predicted class indices.

Source code in highfis/models/_common.py
def predict(self, x: Tensor) -> Tensor:
    """Return predicted class indices."""
    with torch.no_grad():
        return torch.argmax(self.predict_proba(x), dim=1)

predict_proba

Return class probabilities computed with softmax.

Source code in highfis/models/_common.py
def predict_proba(self, x: Tensor) -> Tensor:
    """Return class probabilities computed with softmax."""
    with torch.no_grad():
        if x.dtype == torch.float64:
            was_training = self.training
            self.double()
            try:
                self.eval()
                logits = self.forward(x)
                return torch.softmax(logits, dim=1)
            finally:
                self.float()
                self.train(was_training)
        else:
            was_training = self.training
            try:
                self.eval()
                logits = self.forward(x)
                return torch.softmax(logits, dim=1)
            finally:
                self.train(was_training)

ADATSKRegressorModel

Bases: BaseTSKRegressorModel

TSK regressor with adaptive softmin antecedent (ADATSK).

The firing strength of each rule is computed with the Ada-softmin operator.

Reference

G. Xue, Q. Chang, J. Wang, K. Zhang and N. R. Pal, "An Adaptive Neuro-Fuzzy System With Integrated Feature Selection and Rule Extraction for High-Dimensional Classification Problems," in IEEE Transactions on Fuzzy Systems, vol. 31, no. 7, pp. 2167-2181, July 2023, doi: 10.1109/TFUZZ.2022.3220950.

Initialise the ADATSK regressor.

Parameters:

Name Type Description Default
input_mfs Mapping[str, Sequence[MembershipFunction]]

Mapping from feature name to a sequence of :class:~highfis.memberships.MembershipFunction objects.

required
rule_base str

Rule-base construction strategy. "coco" (default, same-index compact), "cartesian" (all MF combinations), "en" (enhanced FRB), or "custom" (explicit rules via rules).

'coco'
rules Sequence[Sequence[int]] | None

Explicit rule antecedent indices.

None
defuzzifier nn.Module | None

Custom defuzzifier. Defaults to :class:~highfis.defuzzifiers.SumBasedDefuzzifier.

None
consequent_batch_norm bool

Batch normalisation on consequent inputs.

False
eps float | None

Numerical stability epsilon for the Ada-softmin operator.

None
Source code in highfis/models/_adaptive.py
def __init__(
    self,
    input_mfs: Mapping[str, Sequence[MembershipFunction]],
    rule_base: str = "coco",
    rules: Sequence[Sequence[int]] | None = None,
    defuzzifier: nn.Module | None = None,
    consequent_batch_norm: bool = False,
    eps: float | None = None,
) -> None:
    """Initialise the ADATSK regressor.

    Args:
        input_mfs: Mapping from feature name to a sequence of
            :class:`~highfis.memberships.MembershipFunction` objects.
        rule_base: Rule-base construction strategy.  ``"coco"``
            (default, same-index compact), ``"cartesian"`` (all MF
            combinations), ``"en"`` (enhanced FRB), or ``"custom"``
            (explicit rules via *rules*).
        rules: Explicit rule antecedent indices.
        defuzzifier: Custom defuzzifier.  Defaults to
            :class:`~highfis.defuzzifiers.SumBasedDefuzzifier`.
        consequent_batch_norm: Batch normalisation on consequent inputs.
        eps: Numerical stability epsilon for the Ada-softmin operator.
    """
    self.eps = eps

    super().__init__(
        input_mfs,
        rule_base=rule_base,
        t_norm="prod",
        rules=rules,
        defuzzifier=defuzzifier or SumBasedDefuzzifier(),
        consequent_batch_norm=consequent_batch_norm,
    )

    self.rule_layer = AdaSoftminRuleLayer(
        self.input_names,
        [len(input_mfs[name]) for name in self.input_names],
        rules=rules,
        rule_base=rule_base,
        eps=self.eps,
    )

forward

Full forward pass through the TSK pipeline.

Source code in highfis/models/_base.py
def forward(self, x: Tensor) -> Tensor:
    """Full forward pass through the TSK pipeline."""
    output, _ = self._forward_train(x)
    return output

forward_antecedents

Compute normalized rule strengths from model antecedents.

Source code in highfis/models/_base.py
def forward_antecedents(self, x: Tensor) -> Tensor:
    """Compute normalized rule strengths from model antecedents."""
    mu = self.membership_layer(x)
    w = self.rule_layer(mu)
    return self.defuzzifier(w)

get_consequent_weights

Return the consequent layer weights or None when unavailable.

Source code in highfis/models/_base.py
def get_consequent_weights(self) -> Tensor | None:
    """Return the consequent layer weights or ``None`` when unavailable."""
    weight = getattr(self.consequent_layer, "weight", None)
    if isinstance(weight, Tensor):
        return weight.detach()
    return None

get_mf_params

Return a serializable description of the model's membership functions.

Source code in highfis/models/_base.py
def get_mf_params(self) -> dict[str, list[dict[str, Any]]]:
    """Return a serializable description of the model's membership functions."""
    return {
        name: [{"type": type(mf).__name__, **mf.inspect_params()} for mf in cast(Sequence[MembershipFunction], mfs)]
        for name, mfs in self.membership_layer.input_mfs.items()
    }

get_rule_table

Return the rule base as a table of feature-to-MF indices.

Source code in highfis/models/_base.py
def get_rule_table(self) -> list[dict[str, Any]]:
    """Return the rule base as a table of feature-to-MF indices."""
    return [
        dict(
            rule_id=rule_index,
            **dict(zip(self.rule_layer.input_names, rule, strict=False)),
        )
        for rule_index, rule in enumerate(self.rule_layer.rules)
    ]

predict

Return predicted values as a 1-D tensor.

Source code in highfis/models/_common.py
def predict(self, x: Tensor) -> Tensor:
    """Return predicted values as a 1-D tensor."""
    with torch.no_grad():
        if x.dtype == torch.float64:
            was_training = self.training
            self.double()
            try:
                self.eval()
                return self.forward(x).squeeze(1)
            finally:
                self.float()
                self.train(was_training)
        else:
            was_training = self.training
            try:
                self.eval()
                return self.forward(x).squeeze(1)
            finally:
                self.train(was_training)

ADMTSKClassifierModel

Bases: BaseTSKClassifierModel

Adaptive Dombi TSK classifier with Composite Gaussian membership functions.

ADMTSK is an adaptive Dombi TSK fuzzy system designed for high-dimensional inference. It combines a Dombi T-norm antecedent with a positive lower-bound Composite Gaussian membership function (CGMF) and normalized first-order consequents.

Reference

G. Xue, L. Hu, J. Wang and S. Ablameyko, "ADMTSK: A High-Dimensional Takagi-Sugeno-Kang Fuzzy System Based on Adaptive Dombi T-Norm," in IEEE Transactions on Fuzzy Systems, vol. 33, no. 6, pp. 1767-1780, June 2025, doi: 10.1109/TFUZZ.2025.3535640.

Initialize the ADMTSK classifier.

Parameters:

Name Type Description Default
input_mfs Mapping[str, Sequence[MembershipFunction]]

Mapping from feature name to a sequence of membership functions.

required
n_classes int

Number of output classes. Must be >= 2.

required
rule_base str

Rule-base construction strategy. "coco" (default, same-index compact), "cartesian" (all MF combinations), "en" (enhanced FRB), or "custom" (explicit rules via rules).

'coco'
t_norm str | TNormFn

T-norm name or callable (default "dombi"). When a string, an adaptive or fixed DombiTNorm is built from adaptive, lambda_, lower_bound, and k.

'dombi'
adaptive bool

If True, compute adaptive lambda using the feature dimension and membership lower bound.

True
lambda_ float

Fixed Dombi parameter λ > 0 when adaptive is False.

1.0
lower_bound float

The lower bound for Composite GMF values.

1.0 / math.e
k float

Heuristic constant used to compute adaptive lambda.

10.0
rules Sequence[Sequence[int]] | None

Explicit rule antecedent indices for custom rule bases.

None
defuzzifier nn.Module | None

Optional defuzzifier module.

None
consequent_batch_norm bool

If True, apply batch normalization to consequent inputs.

False
zero_consequent_init bool

If True (default), initialize consequent weights and biases to zero.

True

Raises:

Type Description
ValueError

If n_classes < 2 or if lambda_ is invalid when adaptive is False.

Source code in highfis/models/_dombi.py
def __init__(
    self,
    input_mfs: Mapping[str, Sequence[MembershipFunction]],
    n_classes: int,
    rule_base: str = "coco",
    t_norm: str | TNormFn = "dombi",
    adaptive: bool = True,
    lambda_: float = 1.0,
    lower_bound: float = 1.0 / math.e,
    k: float = 10.0,
    rules: Sequence[Sequence[int]] | None = None,
    defuzzifier: nn.Module | None = None,
    consequent_batch_norm: bool = False,
    zero_consequent_init: bool = True,
) -> None:
    """Initialize the ADMTSK classifier.

    Args:
        input_mfs: Mapping from feature name to a sequence of
            membership functions.
        n_classes: Number of output classes. Must be >= 2.
        rule_base: Rule-base construction strategy.  ``"coco"``
            (default, same-index compact), ``"cartesian"`` (all MF
            combinations), ``"en"`` (enhanced FRB), or ``"custom"``
            (explicit rules via *rules*).
        t_norm: T-norm name or callable (default ``"dombi"``). When a
            string, an adaptive or fixed ``DombiTNorm`` is built from
            *adaptive*, *lambda_*, *lower_bound*, and *k*.
        adaptive: If True, compute adaptive lambda using the feature
            dimension and membership lower bound.
        lambda_: Fixed Dombi parameter ``λ > 0`` when adaptive is False.
        lower_bound: The lower bound for Composite GMF values.
        k: Heuristic constant used to compute adaptive lambda.
        rules: Explicit rule antecedent indices for custom rule bases.
        defuzzifier: Optional defuzzifier module.
        consequent_batch_norm: If True, apply batch normalization to
            consequent inputs.
        zero_consequent_init: If ``True`` (default), initialize
            consequent weights and biases to zero.

    Raises:
        ValueError: If ``n_classes < 2`` or if ``lambda_`` is invalid
            when adaptive is False.
    """
    if n_classes < 2:
        raise ValueError("n_classes must be >= 2")
    if not adaptive and lambda_ <= 0.0:
        raise ValueError("lambda_ must be > 0")

    self.n_classes = int(n_classes)
    self.adaptive = bool(adaptive)
    self.lambda_ = float(lambda_)
    self.lower_bound = float(lower_bound)
    self.k = float(k)
    self.zero_consequent_init = bool(zero_consequent_init)

    if not callable(t_norm):
        if self.adaptive:
            t_norm = AdaptiveDombiTNorm(
                dimension=len(input_mfs),
                lower_bound=self.lower_bound,
                k=self.k,
            )
        else:
            t_norm = DombiTNorm(lambda_=self.lambda_)

    super().__init__(
        input_mfs,
        rule_base=rule_base,
        t_norm=t_norm,
        rules=rules,
        defuzzifier=defuzzifier or SumBasedDefuzzifier(),
        consequent_batch_norm=consequent_batch_norm,
    )
    if self.zero_consequent_init:
        self._zero_initialize_consequents()

forward

Full forward pass through the TSK pipeline.

Source code in highfis/models/_base.py
def forward(self, x: Tensor) -> Tensor:
    """Full forward pass through the TSK pipeline."""
    output, _ = self._forward_train(x)
    return output

forward_antecedents

Compute normalized rule strengths from model antecedents.

Source code in highfis/models/_base.py
def forward_antecedents(self, x: Tensor) -> Tensor:
    """Compute normalized rule strengths from model antecedents."""
    mu = self.membership_layer(x)
    w = self.rule_layer(mu)
    return self.defuzzifier(w)

get_consequent_weights

Return the consequent layer weights or None when unavailable.

Source code in highfis/models/_base.py
def get_consequent_weights(self) -> Tensor | None:
    """Return the consequent layer weights or ``None`` when unavailable."""
    weight = getattr(self.consequent_layer, "weight", None)
    if isinstance(weight, Tensor):
        return weight.detach()
    return None

get_mf_params

Return a serializable description of the model's membership functions.

Source code in highfis/models/_base.py
def get_mf_params(self) -> dict[str, list[dict[str, Any]]]:
    """Return a serializable description of the model's membership functions."""
    return {
        name: [{"type": type(mf).__name__, **mf.inspect_params()} for mf in cast(Sequence[MembershipFunction], mfs)]
        for name, mfs in self.membership_layer.input_mfs.items()
    }

get_rule_table

Return the rule base as a table of feature-to-MF indices.

Source code in highfis/models/_base.py
def get_rule_table(self) -> list[dict[str, Any]]:
    """Return the rule base as a table of feature-to-MF indices."""
    return [
        dict(
            rule_id=rule_index,
            **dict(zip(self.rule_layer.input_names, rule, strict=False)),
        )
        for rule_index, rule in enumerate(self.rule_layer.rules)
    ]

predict

Return predicted class indices.

Source code in highfis/models/_common.py
def predict(self, x: Tensor) -> Tensor:
    """Return predicted class indices."""
    with torch.no_grad():
        return torch.argmax(self.predict_proba(x), dim=1)

predict_proba

Return class probabilities computed with softmax.

Source code in highfis/models/_common.py
def predict_proba(self, x: Tensor) -> Tensor:
    """Return class probabilities computed with softmax."""
    with torch.no_grad():
        if x.dtype == torch.float64:
            was_training = self.training
            self.double()
            try:
                self.eval()
                logits = self.forward(x)
                return torch.softmax(logits, dim=1)
            finally:
                self.float()
                self.train(was_training)
        else:
            was_training = self.training
            try:
                self.eval()
                logits = self.forward(x)
                return torch.softmax(logits, dim=1)
            finally:
                self.train(was_training)

ADMTSKRegressorModel

Bases: BaseTSKRegressorModel

Adaptive Dombi TSK regressor with Composite Gaussian membership functions.

ADMTSK is an adaptive Dombi TSK fuzzy system designed for high-dimensional inference. It combines a Dombi T-norm antecedent with a positive lower-bound Composite Gaussian membership function (CGMF) and normalized first-order consequents.

Reference

G. Xue, L. Hu, J. Wang and S. Ablameyko, "ADMTSK: A High-Dimensional Takagi-Sugeno-Kang Fuzzy System Based on Adaptive Dombi T-Norm," in IEEE Transactions on Fuzzy Systems, vol. 33, no. 6, pp. 1767-1780, June 2025, doi: 10.1109/TFUZZ.2025.3535640.

Initialize the ADMTSK regressor.

Parameters:

Name Type Description Default
input_mfs Mapping[str, Sequence[MembershipFunction]]

Mapping from feature name to a sequence of membership functions.

required
rule_base str

Rule-base construction strategy. "coco" (default, same-index compact), "cartesian" (all MF combinations), "en" (enhanced FRB), or "custom" (explicit rules via rules).

'coco'
t_norm str | TNormFn

T-norm name or callable (default "dombi"). When a string, an adaptive or fixed DombiTNorm is built from adaptive, lambda_, lower_bound, and k.

'dombi'
adaptive bool

If True, compute adaptive lambda using the feature dimension and membership lower bound.

True
lambda_ float

Fixed Dombi parameter λ > 0 when adaptive is False.

1.0
lower_bound float

The lower bound for Composite GMF values.

1.0 / math.e
k float

Heuristic constant used to compute adaptive lambda.

10.0
rules Sequence[Sequence[int]] | None

Explicit rule antecedent indices for custom rule bases.

None
defuzzifier nn.Module | None

Optional defuzzifier module.

None
consequent_batch_norm bool

If True, apply batch normalization to consequent inputs.

False
zero_consequent_init bool

If True (default), initialize consequent weights and biases to zero.

True

Raises:

Type Description
ValueError

If lambda_ is invalid when adaptive is False.

Source code in highfis/models/_dombi.py
def __init__(
    self,
    input_mfs: Mapping[str, Sequence[MembershipFunction]],
    rule_base: str = "coco",
    t_norm: str | TNormFn = "dombi",
    adaptive: bool = True,
    lambda_: float = 1.0,
    lower_bound: float = 1.0 / math.e,
    k: float = 10.0,
    rules: Sequence[Sequence[int]] | None = None,
    defuzzifier: nn.Module | None = None,
    consequent_batch_norm: bool = False,
    zero_consequent_init: bool = True,
) -> None:
    """Initialize the ADMTSK regressor.

    Args:
        input_mfs: Mapping from feature name to a sequence of
            membership functions.
        rule_base: Rule-base construction strategy.  ``"coco"``
            (default, same-index compact), ``"cartesian"`` (all MF
            combinations), ``"en"`` (enhanced FRB), or ``"custom"``
            (explicit rules via *rules*).
        t_norm: T-norm name or callable (default ``"dombi"``). When a
            string, an adaptive or fixed ``DombiTNorm`` is built from
            *adaptive*, *lambda_*, *lower_bound*, and *k*.
        adaptive: If True, compute adaptive lambda using the feature
            dimension and membership lower bound.
        lambda_: Fixed Dombi parameter ``λ > 0`` when adaptive is False.
        lower_bound: The lower bound for Composite GMF values.
        k: Heuristic constant used to compute adaptive lambda.
        rules: Explicit rule antecedent indices for custom rule bases.
        defuzzifier: Optional defuzzifier module.
        consequent_batch_norm: If True, apply batch normalization to
            consequent inputs.
        zero_consequent_init: If ``True`` (default), initialize
            consequent weights and biases to zero.

    Raises:
        ValueError: If ``lambda_`` is invalid when adaptive is False.
    """
    if not adaptive and lambda_ <= 0.0:
        raise ValueError("lambda_ must be > 0")

    self.adaptive = bool(adaptive)
    self.lambda_ = float(lambda_)
    self.lower_bound = float(lower_bound)
    self.k = float(k)
    self.zero_consequent_init = bool(zero_consequent_init)

    if not callable(t_norm):
        if self.adaptive:
            t_norm = AdaptiveDombiTNorm(
                dimension=len(input_mfs),
                lower_bound=self.lower_bound,
                k=self.k,
            )
        else:
            t_norm = DombiTNorm(lambda_=self.lambda_)

    super().__init__(
        input_mfs,
        rule_base=rule_base,
        t_norm=t_norm,
        rules=rules,
        defuzzifier=defuzzifier or SumBasedDefuzzifier(),
        consequent_batch_norm=consequent_batch_norm,
    )
    if self.zero_consequent_init:
        self._zero_initialize_consequents()

forward

Full forward pass through the TSK pipeline.

Source code in highfis/models/_base.py
def forward(self, x: Tensor) -> Tensor:
    """Full forward pass through the TSK pipeline."""
    output, _ = self._forward_train(x)
    return output

forward_antecedents

Compute normalized rule strengths from model antecedents.

Source code in highfis/models/_base.py
def forward_antecedents(self, x: Tensor) -> Tensor:
    """Compute normalized rule strengths from model antecedents."""
    mu = self.membership_layer(x)
    w = self.rule_layer(mu)
    return self.defuzzifier(w)

get_consequent_weights

Return the consequent layer weights or None when unavailable.

Source code in highfis/models/_base.py
def get_consequent_weights(self) -> Tensor | None:
    """Return the consequent layer weights or ``None`` when unavailable."""
    weight = getattr(self.consequent_layer, "weight", None)
    if isinstance(weight, Tensor):
        return weight.detach()
    return None

get_mf_params

Return a serializable description of the model's membership functions.

Source code in highfis/models/_base.py
def get_mf_params(self) -> dict[str, list[dict[str, Any]]]:
    """Return a serializable description of the model's membership functions."""
    return {
        name: [{"type": type(mf).__name__, **mf.inspect_params()} for mf in cast(Sequence[MembershipFunction], mfs)]
        for name, mfs in self.membership_layer.input_mfs.items()
    }

get_rule_table

Return the rule base as a table of feature-to-MF indices.

Source code in highfis/models/_base.py
def get_rule_table(self) -> list[dict[str, Any]]:
    """Return the rule base as a table of feature-to-MF indices."""
    return [
        dict(
            rule_id=rule_index,
            **dict(zip(self.rule_layer.input_names, rule, strict=False)),
        )
        for rule_index, rule in enumerate(self.rule_layer.rules)
    ]

predict

Return predicted values as a 1-D tensor.

Source code in highfis/models/_common.py
def predict(self, x: Tensor) -> Tensor:
    """Return predicted values as a 1-D tensor."""
    with torch.no_grad():
        if x.dtype == torch.float64:
            was_training = self.training
            self.double()
            try:
                self.eval()
                return self.forward(x).squeeze(1)
            finally:
                self.float()
                self.train(was_training)
        else:
            was_training = self.training
            try:
                self.eval()
                return self.forward(x).squeeze(1)
            finally:
                self.train(was_training)

ADPTSKClassifierModel

Bases: BaseTSKClassifierModel

TSK classifier with adaptive double-parameter softmin antecedent (ADPTSK).

The firing strengths of each rule are computed with the ADP-softmin operator, and membership functions are wrapped as Gaussian PIMFs to preserve a positive infimum during high-dimensional training.

Reference

Ma, M., Qian, L., Zhang, Y., Fang, Q., & Xue, G. (2025). An adaptive double-parameter softmin based Takagi-Sugeno-Kang fuzzy system for high-dimensional data. Fuzzy Sets and Systems, 521, 109582. https://doi.org/10.1016/j.fss.2025.109582

Initialise the ADPTSK classifier.

Parameters:

Name Type Description Default
input_mfs Mapping[str, Sequence[MembershipFunction]]

Mapping from feature name to a sequence of :class:~highfis.memberships.MembershipFunction objects.

required
n_classes int

Number of output classes (must be ≥ 2).

required
rule_base str

Rule-base construction strategy. "coco" (default, same-index compact), "cartesian" (all MF combinations), "en" (enhanced FRB), or "custom" (explicit rules via rules).

'coco'
rules Sequence[Sequence[int]] | None

Explicit rule antecedent indices.

None
defuzzifier nn.Module | None

Custom defuzzifier. Defaults to :class:~highfis.defuzzifiers.SumBasedDefuzzifier.

None
consequent_batch_norm bool

Batch normalisation on consequent inputs.

False
kappa float

ADP-softmin parameter κ > 0 (default 690.0).

690.0
xi float

ADP-softmin parameter ξ > 0 (default 730.0).

730.0
eps float | None

Numerical stability epsilon.

None
zero_consequent_init bool

If True (default), initialize consequent weights and biases to zeros.

True

Raises:

Type Description
ValueError

If n_classes < 2, kappa <= 0, or xi <= 0.

Source code in highfis/models/_adaptive.py
def __init__(
    self,
    input_mfs: Mapping[str, Sequence[MembershipFunction]],
    n_classes: int,
    rule_base: str = "coco",
    rules: Sequence[Sequence[int]] | None = None,
    defuzzifier: nn.Module | None = None,
    consequent_batch_norm: bool = False,
    kappa: float = 690.0,
    xi: float = 730.0,
    eps: float | None = None,
    zero_consequent_init: bool = True,
) -> None:
    """Initialise the ADPTSK classifier.

    Args:
        input_mfs: Mapping from feature name to a sequence of
            :class:`~highfis.memberships.MembershipFunction` objects.
        n_classes: Number of output classes (must be ≥ 2).
        rule_base: Rule-base construction strategy.  ``"coco"``
            (default, same-index compact), ``"cartesian"`` (all MF
            combinations), ``"en"`` (enhanced FRB), or ``"custom"``
            (explicit rules via *rules*).
        rules: Explicit rule antecedent indices.
        defuzzifier: Custom defuzzifier.  Defaults to
            :class:`~highfis.defuzzifiers.SumBasedDefuzzifier`.
        consequent_batch_norm: Batch normalisation on consequent inputs.
        kappa: ADP-softmin parameter ``κ > 0`` (default ``690.0``).
        xi: ADP-softmin parameter ``ξ > 0`` (default ``730.0``).
        eps: Numerical stability epsilon.
        zero_consequent_init: If ``True`` (default), initialize
            consequent weights and biases to zeros.

    Raises:
        ValueError: If ``n_classes < 2``, ``kappa <= 0``, or ``xi <= 0``.
    """
    if n_classes < 2:
        raise ValueError("n_classes must be >= 2")
    if kappa <= 0.0:
        raise ValueError("kappa must be > 0")
    if xi <= 0.0:
        raise ValueError("xi must be > 0")

    self.n_classes = int(n_classes)
    self.kappa = float(kappa)
    self.xi = float(xi)
    self.eps = eps
    self.zero_consequent_init = bool(zero_consequent_init)

    super().__init__(
        input_mfs,
        rule_base=rule_base,
        t_norm="prod",
        rules=rules,
        defuzzifier=defuzzifier or SumBasedDefuzzifier(),
        consequent_batch_norm=consequent_batch_norm,
    )

    self.rule_layer = ADPSoftminRuleLayer(
        self.input_names,
        [len(input_mfs[name]) for name in self.input_names],
        rules=rules,
        rule_base=rule_base,
        kappa=self.kappa,
        xi=self.xi,
        eps=self.eps,
    )
    if self.zero_consequent_init:
        self._zero_initialize_consequents()

forward

Full forward pass through the TSK pipeline.

Source code in highfis/models/_base.py
def forward(self, x: Tensor) -> Tensor:
    """Full forward pass through the TSK pipeline."""
    output, _ = self._forward_train(x)
    return output

forward_antecedents

Compute normalized rule strengths from model antecedents.

Source code in highfis/models/_base.py
def forward_antecedents(self, x: Tensor) -> Tensor:
    """Compute normalized rule strengths from model antecedents."""
    mu = self.membership_layer(x)
    w = self.rule_layer(mu)
    return self.defuzzifier(w)

get_consequent_weights

Return the consequent layer weights or None when unavailable.

Source code in highfis/models/_base.py
def get_consequent_weights(self) -> Tensor | None:
    """Return the consequent layer weights or ``None`` when unavailable."""
    weight = getattr(self.consequent_layer, "weight", None)
    if isinstance(weight, Tensor):
        return weight.detach()
    return None

get_mf_params

Return a serializable description of the model's membership functions.

Source code in highfis/models/_base.py
def get_mf_params(self) -> dict[str, list[dict[str, Any]]]:
    """Return a serializable description of the model's membership functions."""
    return {
        name: [{"type": type(mf).__name__, **mf.inspect_params()} for mf in cast(Sequence[MembershipFunction], mfs)]
        for name, mfs in self.membership_layer.input_mfs.items()
    }

get_rule_table

Return the rule base as a table of feature-to-MF indices.

Source code in highfis/models/_base.py
def get_rule_table(self) -> list[dict[str, Any]]:
    """Return the rule base as a table of feature-to-MF indices."""
    return [
        dict(
            rule_id=rule_index,
            **dict(zip(self.rule_layer.input_names, rule, strict=False)),
        )
        for rule_index, rule in enumerate(self.rule_layer.rules)
    ]

predict

Return predicted class indices.

Source code in highfis/models/_common.py
def predict(self, x: Tensor) -> Tensor:
    """Return predicted class indices."""
    with torch.no_grad():
        return torch.argmax(self.predict_proba(x), dim=1)

predict_proba

Return class probabilities computed with softmax.

Source code in highfis/models/_common.py
def predict_proba(self, x: Tensor) -> Tensor:
    """Return class probabilities computed with softmax."""
    with torch.no_grad():
        if x.dtype == torch.float64:
            was_training = self.training
            self.double()
            try:
                self.eval()
                logits = self.forward(x)
                return torch.softmax(logits, dim=1)
            finally:
                self.float()
                self.train(was_training)
        else:
            was_training = self.training
            try:
                self.eval()
                logits = self.forward(x)
                return torch.softmax(logits, dim=1)
            finally:
                self.train(was_training)

ADPTSKRegressorModel

Bases: BaseTSKRegressorModel

TSK regressor with adaptive double-parameter softmin antecedent (ADPTSK).

The firing strengths of each rule are computed with the ADP-softmin operator, and membership functions are wrapped as Gaussian PIMFs to preserve a positive infimum during high-dimensional training.

Reference

Ma, M., Qian, L., Zhang, Y., Fang, Q., & Xue, G. (2025). An adaptive double-parameter softmin based Takagi-Sugeno-Kang fuzzy system for high-dimensional data. Fuzzy Sets and Systems, 521, 109582. https://doi.org/10.1016/j.fss.2025.109582

Initialise the ADPTSK regressor.

Parameters:

Name Type Description Default
input_mfs Mapping[str, Sequence[MembershipFunction]]

Mapping from feature name to a sequence of :class:~highfis.memberships.MembershipFunction objects.

required
rule_base str

Rule-base construction strategy. "coco" (default, same-index compact), "cartesian" (all MF combinations), "en" (enhanced FRB), or "custom" (explicit rules via rules).

'coco'
rules Sequence[Sequence[int]] | None

Explicit rule antecedent indices.

None
defuzzifier nn.Module | None

Custom defuzzifier. Defaults to :class:~highfis.defuzzifiers.SumBasedDefuzzifier.

None
consequent_batch_norm bool

Batch normalisation on consequent inputs.

False
kappa float

ADP-softmin parameter κ > 0 (default 690.0).

690.0
xi float

ADP-softmin parameter ξ > 0 (default 730.0).

730.0
eps float | None

Numerical stability epsilon.

None
zero_consequent_init bool

If True (default), initialize consequent weights and biases to zeros.

True

Raises:

Type Description
ValueError

If kappa <= 0 or xi <= 0.

Source code in highfis/models/_adaptive.py
def __init__(
    self,
    input_mfs: Mapping[str, Sequence[MembershipFunction]],
    rule_base: str = "coco",
    rules: Sequence[Sequence[int]] | None = None,
    defuzzifier: nn.Module | None = None,
    consequent_batch_norm: bool = False,
    kappa: float = 690.0,
    xi: float = 730.0,
    eps: float | None = None,
    zero_consequent_init: bool = True,
) -> None:
    """Initialise the ADPTSK regressor.

    Args:
        input_mfs: Mapping from feature name to a sequence of
            :class:`~highfis.memberships.MembershipFunction` objects.
        rule_base: Rule-base construction strategy.  ``"coco"``
            (default, same-index compact), ``"cartesian"`` (all MF
            combinations), ``"en"`` (enhanced FRB), or ``"custom"``
            (explicit rules via *rules*).
        rules: Explicit rule antecedent indices.
        defuzzifier: Custom defuzzifier.  Defaults to
            :class:`~highfis.defuzzifiers.SumBasedDefuzzifier`.
        consequent_batch_norm: Batch normalisation on consequent inputs.
        kappa: ADP-softmin parameter ``κ > 0`` (default ``690.0``).
        xi: ADP-softmin parameter ``ξ > 0`` (default ``730.0``).
        eps: Numerical stability epsilon.
        zero_consequent_init: If ``True`` (default), initialize
            consequent weights and biases to zeros.

    Raises:
        ValueError: If ``kappa <= 0`` or ``xi <= 0``.
    """
    if kappa <= 0.0:
        raise ValueError("kappa must be > 0")
    if xi <= 0.0:
        raise ValueError("xi must be > 0")
    self.kappa = float(kappa)
    self.xi = float(xi)
    self.eps = eps
    self.zero_consequent_init = bool(zero_consequent_init)

    super().__init__(
        input_mfs,
        rule_base=rule_base,
        t_norm="prod",
        rules=rules,
        defuzzifier=defuzzifier or SumBasedDefuzzifier(),
        consequent_batch_norm=consequent_batch_norm,
    )

    self.rule_layer = ADPSoftminRuleLayer(
        self.input_names,
        [len(input_mfs[name]) for name in self.input_names],
        rules=rules,
        rule_base=rule_base,
        kappa=self.kappa,
        xi=self.xi,
        eps=self.eps,
    )
    if self.zero_consequent_init:
        self._zero_initialize_consequents()

forward

Full forward pass through the TSK pipeline.

Source code in highfis/models/_base.py
def forward(self, x: Tensor) -> Tensor:
    """Full forward pass through the TSK pipeline."""
    output, _ = self._forward_train(x)
    return output

forward_antecedents

Compute normalized rule strengths from model antecedents.

Source code in highfis/models/_base.py
def forward_antecedents(self, x: Tensor) -> Tensor:
    """Compute normalized rule strengths from model antecedents."""
    mu = self.membership_layer(x)
    w = self.rule_layer(mu)
    return self.defuzzifier(w)

get_consequent_weights

Return the consequent layer weights or None when unavailable.

Source code in highfis/models/_base.py
def get_consequent_weights(self) -> Tensor | None:
    """Return the consequent layer weights or ``None`` when unavailable."""
    weight = getattr(self.consequent_layer, "weight", None)
    if isinstance(weight, Tensor):
        return weight.detach()
    return None

get_mf_params

Return a serializable description of the model's membership functions.

Source code in highfis/models/_base.py
def get_mf_params(self) -> dict[str, list[dict[str, Any]]]:
    """Return a serializable description of the model's membership functions."""
    return {
        name: [{"type": type(mf).__name__, **mf.inspect_params()} for mf in cast(Sequence[MembershipFunction], mfs)]
        for name, mfs in self.membership_layer.input_mfs.items()
    }

get_rule_table

Return the rule base as a table of feature-to-MF indices.

Source code in highfis/models/_base.py
def get_rule_table(self) -> list[dict[str, Any]]:
    """Return the rule base as a table of feature-to-MF indices."""
    return [
        dict(
            rule_id=rule_index,
            **dict(zip(self.rule_layer.input_names, rule, strict=False)),
        )
        for rule_index, rule in enumerate(self.rule_layer.rules)
    ]

predict

Return predicted values as a 1-D tensor.

Source code in highfis/models/_common.py
def predict(self, x: Tensor) -> Tensor:
    """Return predicted values as a 1-D tensor."""
    with torch.no_grad():
        if x.dtype == torch.float64:
            was_training = self.training
            self.double()
            try:
                self.eval()
                return self.forward(x).squeeze(1)
            finally:
                self.float()
                self.train(was_training)
        else:
            was_training = self.training
            try:
                self.eval()
                return self.forward(x).squeeze(1)
            finally:
                self.train(was_training)

AYATSKClassifierModel

Bases: BaseTSKClassifierModel

TSK classifier with an adaptive Yager T-norm in the antecedent.

AYATSK extends TSK by using an adaptive Yager T-norm aggregation and optional positive lower-bound membership functions to improve stability and performance in high-dimensional settings.

Reference

G. Xue, Y. Yang and J. Wang, "Adaptive Yager T-Norm-Based Takagi-Sugeno-Kang Fuzzy Systems," in IEEE Transactions on Systems, Man, and Cybernetics: Systems, vol. 55, no. 12, pp. 9802-9815, Dec. 2025, doi: 10.1109/TSMC.2025.3621346.

Initialise the AYATSK classifier.

Parameters:

Name Type Description Default
input_mfs Mapping[str, Sequence[MembershipFunction]]

Mapping from feature name to a sequence of :class:~highfis.memberships.MembershipFunction objects.

required
n_classes int

Number of output classes (must be ≥ 2).

required
rule_base str

Rule-base construction strategy. "coco" (default, same-index compact), "cartesian" (all MF combinations), "en" (enhanced FRB), or "custom" (explicit rules via rules).

'coco'
t_norm str | TNormFn

T-norm name or callable (default "yager").

'yager'
rules Sequence[Sequence[int]] | None

Explicit rule antecedent indices.

None
defuzzifier nn.Module | None

Custom defuzzifier. Defaults to :class:~highfis.defuzzifiers.SumBasedDefuzzifier.

None
consequent_batch_norm bool

Batch normalisation on consequent inputs.

False

Raises:

Type Description
ValueError

If n_classes < 2.

Source code in highfis/models/_yager.py
def __init__(
    self,
    input_mfs: Mapping[str, Sequence[MembershipFunction]],
    n_classes: int,
    rule_base: str = "coco",
    t_norm: str | TNormFn = "yager",
    rules: Sequence[Sequence[int]] | None = None,
    defuzzifier: nn.Module | None = None,
    consequent_batch_norm: bool = False,
) -> None:
    """Initialise the AYATSK classifier.

    Args:
        input_mfs: Mapping from feature name to a sequence of
            :class:`~highfis.memberships.MembershipFunction` objects.
        n_classes: Number of output classes (must be ≥ 2).
        rule_base: Rule-base construction strategy.  ``"coco"``
            (default, same-index compact), ``"cartesian"`` (all MF
            combinations), ``"en"`` (enhanced FRB), or ``"custom"``
            (explicit rules via *rules*).
        t_norm: T-norm name or callable (default ``"yager"``).
        rules: Explicit rule antecedent indices.
        defuzzifier: Custom defuzzifier.  Defaults to
            :class:`~highfis.defuzzifiers.SumBasedDefuzzifier`.
        consequent_batch_norm: Batch normalisation on consequent inputs.

    Raises:
        ValueError: If ``n_classes < 2``.
    """
    if n_classes < 2:
        raise ValueError("n_classes must be >= 2")
    self.n_classes = int(n_classes)
    self.lower_bound_ = _infer_lower_bound(input_mfs)
    self.lambda_ = _adaptive_yager_lambda(len(input_mfs), self.lower_bound_)
    super().__init__(
        input_mfs,
        rule_base=rule_base,
        t_norm=YagerTNorm(lambda_=self.lambda_) if t_norm == "yager" else t_norm,
        rules=rules,
        defuzzifier=defuzzifier or SumBasedDefuzzifier(),
        consequent_batch_norm=consequent_batch_norm,
    )
    _zero_initialize_consequents(self.consequent_layer)

forward

Full forward pass through the TSK pipeline.

Source code in highfis/models/_base.py
def forward(self, x: Tensor) -> Tensor:
    """Full forward pass through the TSK pipeline."""
    output, _ = self._forward_train(x)
    return output

forward_antecedents

Compute normalized rule strengths from model antecedents.

Source code in highfis/models/_base.py
def forward_antecedents(self, x: Tensor) -> Tensor:
    """Compute normalized rule strengths from model antecedents."""
    mu = self.membership_layer(x)
    w = self.rule_layer(mu)
    return self.defuzzifier(w)

get_consequent_weights

Return the consequent layer weights or None when unavailable.

Source code in highfis/models/_base.py
def get_consequent_weights(self) -> Tensor | None:
    """Return the consequent layer weights or ``None`` when unavailable."""
    weight = getattr(self.consequent_layer, "weight", None)
    if isinstance(weight, Tensor):
        return weight.detach()
    return None

get_mf_params

Return a serializable description of the model's membership functions.

Source code in highfis/models/_base.py
def get_mf_params(self) -> dict[str, list[dict[str, Any]]]:
    """Return a serializable description of the model's membership functions."""
    return {
        name: [{"type": type(mf).__name__, **mf.inspect_params()} for mf in cast(Sequence[MembershipFunction], mfs)]
        for name, mfs in self.membership_layer.input_mfs.items()
    }

get_rule_table

Return the rule base as a table of feature-to-MF indices.

Source code in highfis/models/_base.py
def get_rule_table(self) -> list[dict[str, Any]]:
    """Return the rule base as a table of feature-to-MF indices."""
    return [
        dict(
            rule_id=rule_index,
            **dict(zip(self.rule_layer.input_names, rule, strict=False)),
        )
        for rule_index, rule in enumerate(self.rule_layer.rules)
    ]

predict

Return predicted class indices.

Source code in highfis/models/_common.py
def predict(self, x: Tensor) -> Tensor:
    """Return predicted class indices."""
    with torch.no_grad():
        return torch.argmax(self.predict_proba(x), dim=1)

predict_proba

Return class probabilities computed with softmax.

Source code in highfis/models/_common.py
def predict_proba(self, x: Tensor) -> Tensor:
    """Return class probabilities computed with softmax."""
    with torch.no_grad():
        if x.dtype == torch.float64:
            was_training = self.training
            self.double()
            try:
                self.eval()
                logits = self.forward(x)
                return torch.softmax(logits, dim=1)
            finally:
                self.float()
                self.train(was_training)
        else:
            was_training = self.training
            try:
                self.eval()
                logits = self.forward(x)
                return torch.softmax(logits, dim=1)
            finally:
                self.train(was_training)

AYATSKRegressorModel

Bases: BaseTSKRegressorModel

TSK regressor with an adaptive Yager T-norm in the antecedent.

AYATSK extends TSK by using an adaptive Yager T-norm aggregation and optional positive lower-bound membership functions to improve stability and performance in high-dimensional settings.

Reference

G. Xue, Y. Yang and J. Wang, "Adaptive Yager T-Norm-Based Takagi-Sugeno-Kang Fuzzy Systems," in IEEE Transactions on Systems, Man, and Cybernetics: Systems, vol. 55, no. 12, pp. 9802-9815, Dec. 2025, doi: 10.1109/TSMC.2025.3621346.

Initialise the AYATSK regressor.

Parameters:

Name Type Description Default
input_mfs Mapping[str, Sequence[MembershipFunction]]

Mapping from feature name to a sequence of :class:~highfis.memberships.MembershipFunction objects.

required
rule_base str

Rule-base construction strategy. "coco" (default, same-index compact), "cartesian" (all MF combinations), "en" (enhanced FRB), or "custom" (explicit rules via rules).

'coco'
t_norm str | TNormFn

T-norm name or callable (default "yager").

'yager'
rules Sequence[Sequence[int]] | None

Explicit rule antecedent indices.

None
defuzzifier nn.Module | None

Custom defuzzifier. Defaults to :class:~highfis.defuzzifiers.SumBasedDefuzzifier.

None
consequent_batch_norm bool

Batch normalisation on consequent inputs.

False
Source code in highfis/models/_yager.py
def __init__(
    self,
    input_mfs: Mapping[str, Sequence[MembershipFunction]],
    rule_base: str = "coco",
    t_norm: str | TNormFn = "yager",
    rules: Sequence[Sequence[int]] | None = None,
    defuzzifier: nn.Module | None = None,
    consequent_batch_norm: bool = False,
) -> None:
    """Initialise the AYATSK regressor.

    Args:
        input_mfs: Mapping from feature name to a sequence of
            :class:`~highfis.memberships.MembershipFunction` objects.
        rule_base: Rule-base construction strategy.  ``"coco"``
            (default, same-index compact), ``"cartesian"`` (all MF
            combinations), ``"en"`` (enhanced FRB), or ``"custom"``
            (explicit rules via *rules*).
        t_norm: T-norm name or callable (default ``"yager"``).
        rules: Explicit rule antecedent indices.
        defuzzifier: Custom defuzzifier.  Defaults to
            :class:`~highfis.defuzzifiers.SumBasedDefuzzifier`.
        consequent_batch_norm: Batch normalisation on consequent inputs.
    """
    self.lower_bound_ = _infer_lower_bound(input_mfs)
    self.lambda_ = _adaptive_yager_lambda(len(input_mfs), self.lower_bound_)
    super().__init__(
        input_mfs,
        rule_base=rule_base,
        t_norm=YagerTNorm(lambda_=self.lambda_) if t_norm == "yager" else t_norm,
        rules=rules,
        defuzzifier=defuzzifier or SumBasedDefuzzifier(),
        consequent_batch_norm=consequent_batch_norm,
    )
    _zero_initialize_consequents(self.consequent_layer)

forward

Full forward pass through the TSK pipeline.

Source code in highfis/models/_base.py
def forward(self, x: Tensor) -> Tensor:
    """Full forward pass through the TSK pipeline."""
    output, _ = self._forward_train(x)
    return output

forward_antecedents

Compute normalized rule strengths from model antecedents.

Source code in highfis/models/_base.py
def forward_antecedents(self, x: Tensor) -> Tensor:
    """Compute normalized rule strengths from model antecedents."""
    mu = self.membership_layer(x)
    w = self.rule_layer(mu)
    return self.defuzzifier(w)

get_consequent_weights

Return the consequent layer weights or None when unavailable.

Source code in highfis/models/_base.py
def get_consequent_weights(self) -> Tensor | None:
    """Return the consequent layer weights or ``None`` when unavailable."""
    weight = getattr(self.consequent_layer, "weight", None)
    if isinstance(weight, Tensor):
        return weight.detach()
    return None

get_mf_params

Return a serializable description of the model's membership functions.

Source code in highfis/models/_base.py
def get_mf_params(self) -> dict[str, list[dict[str, Any]]]:
    """Return a serializable description of the model's membership functions."""
    return {
        name: [{"type": type(mf).__name__, **mf.inspect_params()} for mf in cast(Sequence[MembershipFunction], mfs)]
        for name, mfs in self.membership_layer.input_mfs.items()
    }

get_rule_table

Return the rule base as a table of feature-to-MF indices.

Source code in highfis/models/_base.py
def get_rule_table(self) -> list[dict[str, Any]]:
    """Return the rule base as a table of feature-to-MF indices."""
    return [
        dict(
            rule_id=rule_index,
            **dict(zip(self.rule_layer.input_names, rule, strict=False)),
        )
        for rule_index, rule in enumerate(self.rule_layer.rules)
    ]

predict

Return predicted values as a 1-D tensor.

Source code in highfis/models/_common.py
def predict(self, x: Tensor) -> Tensor:
    """Return predicted values as a 1-D tensor."""
    with torch.no_grad():
        if x.dtype == torch.float64:
            was_training = self.training
            self.double()
            try:
                self.eval()
                return self.forward(x).squeeze(1)
            finally:
                self.float()
                self.train(was_training)
        else:
            was_training = self.training
            try:
                self.eval()
                return self.forward(x).squeeze(1)
            finally:
                self.train(was_training)

BaseTSK

Bases: nn.Module

Abstract base for TSK fuzzy models.

Subclasses must implement :meth:_build_consequent_layer.

Initialize the TSK pipeline layers.

Parameters:

Name Type Description Default
input_mfs Mapping[str, Sequence[MembershipFunction]]

Mapping from feature names to sequences of :class:~highfis.memberships.MembershipFunction objects. Must not be empty.

required
rule_base str

Rule-base construction strategy. Supported values: "cartesian" (all MF combinations), "coco" (same-index compact), "en" (enhanced FRB), or "custom" (explicit rules via rules).

'cartesian'
t_norm str | TNormFn

T-norm name or callable. Common string values: "prod", "gmean", "min", "dombi", "yager". A callable implementing the T-norm interface may be passed directly.

'gmean'
rules Sequence[Sequence[int]] | None

Explicit rule index sequences. Required when rule_base is "custom".

None
defuzzifier Defuzzifier | None

Normalization module applied to raw rule firing strengths. Defaults to :class:~highfis.defuzzifiers.SoftmaxLogDefuzzifier.

None
consequent_batch_norm bool

If True, insert a :class:~torch.nn.BatchNorm1d layer on the inputs before the consequent computation.

False

Raises:

Type Description
ValueError

If input_mfs is empty.

Source code in highfis/models/_base.py
def __init__(
    self,
    input_mfs: Mapping[str, Sequence[MembershipFunction]],
    *,
    rule_base: str = "cartesian",
    t_norm: str | TNormFn = "gmean",
    rules: Sequence[Sequence[int]] | None = None,
    defuzzifier: Defuzzifier | None = None,
    consequent_batch_norm: bool = False,
) -> None:
    """Initialize the TSK pipeline layers.

    Args:
        input_mfs: Mapping from feature names to sequences of
            :class:`~highfis.memberships.MembershipFunction` objects.
            Must not be empty.
        rule_base: Rule-base construction strategy.  Supported values:
            ``"cartesian"`` (all MF combinations), ``"coco"``
            (same-index compact), ``"en"`` (enhanced FRB), or
            ``"custom"`` (explicit rules via *rules*).
        t_norm: T-norm name or callable.  Common string values: ``"prod"``,
            ``"gmean"``, ``"min"``, ``"dombi"``, ``"yager"``.  A
            callable implementing the T-norm interface may be passed
            directly.
        rules: Explicit rule index sequences.  Required when
            *rule_base* is ``"custom"``.
        defuzzifier: Normalization module applied to raw rule firing
            strengths.  Defaults to
            :class:`~highfis.defuzzifiers.SoftmaxLogDefuzzifier`.
        consequent_batch_norm: If ``True``, insert a
            :class:`~torch.nn.BatchNorm1d` layer on the inputs before
            the consequent computation.

    Raises:
        ValueError: If *input_mfs* is empty.
    """
    super().__init__()
    if not input_mfs:
        raise ValueError("input_mfs must not be empty")

    self.input_mfs = input_mfs
    self.input_names = list(input_mfs.keys())
    self.n_inputs = len(self.input_names)
    mf_per_input = [len(input_mfs[name]) for name in self.input_names]

    self.membership_layer = MembershipLayer(input_mfs)
    self.rule_layer = RuleLayer(
        self.input_names,
        mf_per_input,
        rules=rules,
        rule_base=rule_base,
        t_norm=t_norm,
    )
    self.n_rules = self.rule_layer.n_rules
    self.defuzzifier: Defuzzifier = defuzzifier or SoftmaxLogDefuzzifier()
    self.consequent_batch_norm = bool(consequent_batch_norm)
    self.consequent_bn = nn.BatchNorm1d(self.n_inputs) if self.consequent_batch_norm else None
    self.consequent_layer = self._build_consequent_layer()
    self.logger = logging.getLogger(f"{self.__class__.__module__}.{self.__class__.__name__}")
    if not self.logger.handlers:
        stream_handler = logging.StreamHandler(sys.stdout)
        stream_handler.setFormatter(logging.Formatter("%(message)s"))
        self.logger.addHandler(stream_handler)
        self.logger.setLevel(logging.INFO)
        self.logger.propagate = False

forward

Full forward pass through the TSK pipeline.

Source code in highfis/models/_base.py
def forward(self, x: Tensor) -> Tensor:
    """Full forward pass through the TSK pipeline."""
    output, _ = self._forward_train(x)
    return output

forward_antecedents

Compute normalized rule strengths from model antecedents.

Source code in highfis/models/_base.py
def forward_antecedents(self, x: Tensor) -> Tensor:
    """Compute normalized rule strengths from model antecedents."""
    mu = self.membership_layer(x)
    w = self.rule_layer(mu)
    return self.defuzzifier(w)

get_consequent_weights

Return the consequent layer weights or None when unavailable.

Source code in highfis/models/_base.py
def get_consequent_weights(self) -> Tensor | None:
    """Return the consequent layer weights or ``None`` when unavailable."""
    weight = getattr(self.consequent_layer, "weight", None)
    if isinstance(weight, Tensor):
        return weight.detach()
    return None

get_mf_params

Return a serializable description of the model's membership functions.

Source code in highfis/models/_base.py
def get_mf_params(self) -> dict[str, list[dict[str, Any]]]:
    """Return a serializable description of the model's membership functions."""
    return {
        name: [{"type": type(mf).__name__, **mf.inspect_params()} for mf in cast(Sequence[MembershipFunction], mfs)]
        for name, mfs in self.membership_layer.input_mfs.items()
    }

get_rule_table

Return the rule base as a table of feature-to-MF indices.

Source code in highfis/models/_base.py
def get_rule_table(self) -> list[dict[str, Any]]:
    """Return the rule base as a table of feature-to-MF indices."""
    return [
        dict(
            rule_id=rule_index,
            **dict(zip(self.rule_layer.input_names, rule, strict=False)),
        )
        for rule_index, rule in enumerate(self.rule_layer.rules)
    ]

BaseTSKClassifierModel

Bases: BaseTSK

Abstract classifier base that provides task-specific training and inference helpers.

Initialize the TSK pipeline layers.

Parameters:

Name Type Description Default
input_mfs Mapping[str, Sequence[MembershipFunction]]

Mapping from feature names to sequences of :class:~highfis.memberships.MembershipFunction objects. Must not be empty.

required
rule_base str

Rule-base construction strategy. Supported values: "cartesian" (all MF combinations), "coco" (same-index compact), "en" (enhanced FRB), or "custom" (explicit rules via rules).

'cartesian'
t_norm str | TNormFn

T-norm name or callable. Common string values: "prod", "gmean", "min", "dombi", "yager". A callable implementing the T-norm interface may be passed directly.

'gmean'
rules Sequence[Sequence[int]] | None

Explicit rule index sequences. Required when rule_base is "custom".

None
defuzzifier Defuzzifier | None

Normalization module applied to raw rule firing strengths. Defaults to :class:~highfis.defuzzifiers.SoftmaxLogDefuzzifier.

None
consequent_batch_norm bool

If True, insert a :class:~torch.nn.BatchNorm1d layer on the inputs before the consequent computation.

False

Raises:

Type Description
ValueError

If input_mfs is empty.

Source code in highfis/models/_base.py
def __init__(
    self,
    input_mfs: Mapping[str, Sequence[MembershipFunction]],
    *,
    rule_base: str = "cartesian",
    t_norm: str | TNormFn = "gmean",
    rules: Sequence[Sequence[int]] | None = None,
    defuzzifier: Defuzzifier | None = None,
    consequent_batch_norm: bool = False,
) -> None:
    """Initialize the TSK pipeline layers.

    Args:
        input_mfs: Mapping from feature names to sequences of
            :class:`~highfis.memberships.MembershipFunction` objects.
            Must not be empty.
        rule_base: Rule-base construction strategy.  Supported values:
            ``"cartesian"`` (all MF combinations), ``"coco"``
            (same-index compact), ``"en"`` (enhanced FRB), or
            ``"custom"`` (explicit rules via *rules*).
        t_norm: T-norm name or callable.  Common string values: ``"prod"``,
            ``"gmean"``, ``"min"``, ``"dombi"``, ``"yager"``.  A
            callable implementing the T-norm interface may be passed
            directly.
        rules: Explicit rule index sequences.  Required when
            *rule_base* is ``"custom"``.
        defuzzifier: Normalization module applied to raw rule firing
            strengths.  Defaults to
            :class:`~highfis.defuzzifiers.SoftmaxLogDefuzzifier`.
        consequent_batch_norm: If ``True``, insert a
            :class:`~torch.nn.BatchNorm1d` layer on the inputs before
            the consequent computation.

    Raises:
        ValueError: If *input_mfs* is empty.
    """
    super().__init__()
    if not input_mfs:
        raise ValueError("input_mfs must not be empty")

    self.input_mfs = input_mfs
    self.input_names = list(input_mfs.keys())
    self.n_inputs = len(self.input_names)
    mf_per_input = [len(input_mfs[name]) for name in self.input_names]

    self.membership_layer = MembershipLayer(input_mfs)
    self.rule_layer = RuleLayer(
        self.input_names,
        mf_per_input,
        rules=rules,
        rule_base=rule_base,
        t_norm=t_norm,
    )
    self.n_rules = self.rule_layer.n_rules
    self.defuzzifier: Defuzzifier = defuzzifier or SoftmaxLogDefuzzifier()
    self.consequent_batch_norm = bool(consequent_batch_norm)
    self.consequent_bn = nn.BatchNorm1d(self.n_inputs) if self.consequent_batch_norm else None
    self.consequent_layer = self._build_consequent_layer()
    self.logger = logging.getLogger(f"{self.__class__.__module__}.{self.__class__.__name__}")
    if not self.logger.handlers:
        stream_handler = logging.StreamHandler(sys.stdout)
        stream_handler.setFormatter(logging.Formatter("%(message)s"))
        self.logger.addHandler(stream_handler)
        self.logger.setLevel(logging.INFO)
        self.logger.propagate = False

forward

Full forward pass through the TSK pipeline.

Source code in highfis/models/_base.py
def forward(self, x: Tensor) -> Tensor:
    """Full forward pass through the TSK pipeline."""
    output, _ = self._forward_train(x)
    return output

forward_antecedents

Compute normalized rule strengths from model antecedents.

Source code in highfis/models/_base.py
def forward_antecedents(self, x: Tensor) -> Tensor:
    """Compute normalized rule strengths from model antecedents."""
    mu = self.membership_layer(x)
    w = self.rule_layer(mu)
    return self.defuzzifier(w)

get_consequent_weights

Return the consequent layer weights or None when unavailable.

Source code in highfis/models/_base.py
def get_consequent_weights(self) -> Tensor | None:
    """Return the consequent layer weights or ``None`` when unavailable."""
    weight = getattr(self.consequent_layer, "weight", None)
    if isinstance(weight, Tensor):
        return weight.detach()
    return None

get_mf_params

Return a serializable description of the model's membership functions.

Source code in highfis/models/_base.py
def get_mf_params(self) -> dict[str, list[dict[str, Any]]]:
    """Return a serializable description of the model's membership functions."""
    return {
        name: [{"type": type(mf).__name__, **mf.inspect_params()} for mf in cast(Sequence[MembershipFunction], mfs)]
        for name, mfs in self.membership_layer.input_mfs.items()
    }

get_rule_table

Return the rule base as a table of feature-to-MF indices.

Source code in highfis/models/_base.py
def get_rule_table(self) -> list[dict[str, Any]]:
    """Return the rule base as a table of feature-to-MF indices."""
    return [
        dict(
            rule_id=rule_index,
            **dict(zip(self.rule_layer.input_names, rule, strict=False)),
        )
        for rule_index, rule in enumerate(self.rule_layer.rules)
    ]

predict

Return predicted class indices.

Source code in highfis/models/_common.py
def predict(self, x: Tensor) -> Tensor:
    """Return predicted class indices."""
    with torch.no_grad():
        return torch.argmax(self.predict_proba(x), dim=1)

predict_proba

Return class probabilities computed with softmax.

Source code in highfis/models/_common.py
def predict_proba(self, x: Tensor) -> Tensor:
    """Return class probabilities computed with softmax."""
    with torch.no_grad():
        if x.dtype == torch.float64:
            was_training = self.training
            self.double()
            try:
                self.eval()
                logits = self.forward(x)
                return torch.softmax(logits, dim=1)
            finally:
                self.float()
                self.train(was_training)
        else:
            was_training = self.training
            try:
                self.eval()
                logits = self.forward(x)
                return torch.softmax(logits, dim=1)
            finally:
                self.train(was_training)

BaseTSKRegressorModel

Bases: BaseTSK

Abstract regressor base that provides task-specific training and inference helpers.

Initialize the TSK pipeline layers.

Parameters:

Name Type Description Default
input_mfs Mapping[str, Sequence[MembershipFunction]]

Mapping from feature names to sequences of :class:~highfis.memberships.MembershipFunction objects. Must not be empty.

required
rule_base str

Rule-base construction strategy. Supported values: "cartesian" (all MF combinations), "coco" (same-index compact), "en" (enhanced FRB), or "custom" (explicit rules via rules).

'cartesian'
t_norm str | TNormFn

T-norm name or callable. Common string values: "prod", "gmean", "min", "dombi", "yager". A callable implementing the T-norm interface may be passed directly.

'gmean'
rules Sequence[Sequence[int]] | None

Explicit rule index sequences. Required when rule_base is "custom".

None
defuzzifier Defuzzifier | None

Normalization module applied to raw rule firing strengths. Defaults to :class:~highfis.defuzzifiers.SoftmaxLogDefuzzifier.

None
consequent_batch_norm bool

If True, insert a :class:~torch.nn.BatchNorm1d layer on the inputs before the consequent computation.

False

Raises:

Type Description
ValueError

If input_mfs is empty.

Source code in highfis/models/_base.py
def __init__(
    self,
    input_mfs: Mapping[str, Sequence[MembershipFunction]],
    *,
    rule_base: str = "cartesian",
    t_norm: str | TNormFn = "gmean",
    rules: Sequence[Sequence[int]] | None = None,
    defuzzifier: Defuzzifier | None = None,
    consequent_batch_norm: bool = False,
) -> None:
    """Initialize the TSK pipeline layers.

    Args:
        input_mfs: Mapping from feature names to sequences of
            :class:`~highfis.memberships.MembershipFunction` objects.
            Must not be empty.
        rule_base: Rule-base construction strategy.  Supported values:
            ``"cartesian"`` (all MF combinations), ``"coco"``
            (same-index compact), ``"en"`` (enhanced FRB), or
            ``"custom"`` (explicit rules via *rules*).
        t_norm: T-norm name or callable.  Common string values: ``"prod"``,
            ``"gmean"``, ``"min"``, ``"dombi"``, ``"yager"``.  A
            callable implementing the T-norm interface may be passed
            directly.
        rules: Explicit rule index sequences.  Required when
            *rule_base* is ``"custom"``.
        defuzzifier: Normalization module applied to raw rule firing
            strengths.  Defaults to
            :class:`~highfis.defuzzifiers.SoftmaxLogDefuzzifier`.
        consequent_batch_norm: If ``True``, insert a
            :class:`~torch.nn.BatchNorm1d` layer on the inputs before
            the consequent computation.

    Raises:
        ValueError: If *input_mfs* is empty.
    """
    super().__init__()
    if not input_mfs:
        raise ValueError("input_mfs must not be empty")

    self.input_mfs = input_mfs
    self.input_names = list(input_mfs.keys())
    self.n_inputs = len(self.input_names)
    mf_per_input = [len(input_mfs[name]) for name in self.input_names]

    self.membership_layer = MembershipLayer(input_mfs)
    self.rule_layer = RuleLayer(
        self.input_names,
        mf_per_input,
        rules=rules,
        rule_base=rule_base,
        t_norm=t_norm,
    )
    self.n_rules = self.rule_layer.n_rules
    self.defuzzifier: Defuzzifier = defuzzifier or SoftmaxLogDefuzzifier()
    self.consequent_batch_norm = bool(consequent_batch_norm)
    self.consequent_bn = nn.BatchNorm1d(self.n_inputs) if self.consequent_batch_norm else None
    self.consequent_layer = self._build_consequent_layer()
    self.logger = logging.getLogger(f"{self.__class__.__module__}.{self.__class__.__name__}")
    if not self.logger.handlers:
        stream_handler = logging.StreamHandler(sys.stdout)
        stream_handler.setFormatter(logging.Formatter("%(message)s"))
        self.logger.addHandler(stream_handler)
        self.logger.setLevel(logging.INFO)
        self.logger.propagate = False

forward

Full forward pass through the TSK pipeline.

Source code in highfis/models/_base.py
def forward(self, x: Tensor) -> Tensor:
    """Full forward pass through the TSK pipeline."""
    output, _ = self._forward_train(x)
    return output

forward_antecedents

Compute normalized rule strengths from model antecedents.

Source code in highfis/models/_base.py
def forward_antecedents(self, x: Tensor) -> Tensor:
    """Compute normalized rule strengths from model antecedents."""
    mu = self.membership_layer(x)
    w = self.rule_layer(mu)
    return self.defuzzifier(w)

get_consequent_weights

Return the consequent layer weights or None when unavailable.

Source code in highfis/models/_base.py
def get_consequent_weights(self) -> Tensor | None:
    """Return the consequent layer weights or ``None`` when unavailable."""
    weight = getattr(self.consequent_layer, "weight", None)
    if isinstance(weight, Tensor):
        return weight.detach()
    return None

get_mf_params

Return a serializable description of the model's membership functions.

Source code in highfis/models/_base.py
def get_mf_params(self) -> dict[str, list[dict[str, Any]]]:
    """Return a serializable description of the model's membership functions."""
    return {
        name: [{"type": type(mf).__name__, **mf.inspect_params()} for mf in cast(Sequence[MembershipFunction], mfs)]
        for name, mfs in self.membership_layer.input_mfs.items()
    }

get_rule_table

Return the rule base as a table of feature-to-MF indices.

Source code in highfis/models/_base.py
def get_rule_table(self) -> list[dict[str, Any]]:
    """Return the rule base as a table of feature-to-MF indices."""
    return [
        dict(
            rule_id=rule_index,
            **dict(zip(self.rule_layer.input_names, rule, strict=False)),
        )
        for rule_index, rule in enumerate(self.rule_layer.rules)
    ]

predict

Return predicted values as a 1-D tensor.

Source code in highfis/models/_common.py
def predict(self, x: Tensor) -> Tensor:
    """Return predicted values as a 1-D tensor."""
    with torch.no_grad():
        if x.dtype == torch.float64:
            was_training = self.training
            self.double()
            try:
                self.eval()
                return self.forward(x).squeeze(1)
            finally:
                self.float()
                self.train(was_training)
        else:
            was_training = self.training
            try:
                self.eval()
                return self.forward(x).squeeze(1)
            finally:
                self.train(was_training)

DGALETSKClassifierModel

Bases: BaseTSKClassifierModel

DG-ALETSK classifier with ALE-softmin antecedent and double-group gates.

DG-ALETSK extends FSRE-ADATSK by replacing the adaptive softmin with the Adaptive Ln-Exp (ALE) softmin — a smoother variant with improved numerical stability. It also uses a zero-order consequent in the DG (data-guided) training phase and optionally converts to first-order after gate-based pruning.

Reference

G. Xue, J. Wang, B. Yuan and C. Dai, "DG-ALETSK: A High-Dimensional Fuzzy Approach With Simultaneous Feature Selection and Rule Extraction," in IEEE Transactions on Fuzzy Systems, vol. 31, no. 11, pp. 3866-3880, Nov. 2023, doi: 10.1109/TFUZZ.2023.3270445.

Initialise the DG-ALETSK classifier.

Parameters:

Name Type Description Default
input_mfs Mapping[str, Sequence[MembershipFunction]]

Mapping from feature name to a sequence of :class:~highfis.memberships.MembershipFunction objects.

required
n_classes int

Number of output classes (must be ≥ 2).

required
rule_base str

Rule-base construction strategy. "coco" (default, same-index compact), "cartesian" (all MF combinations), "en" (enhanced FRB; see also use_en_frb), or "custom" (explicit rules via rules).

'coco'
rules Sequence[Sequence[int]] | None

Explicit rule antecedent indices; ignored when use_en_frb=True.

None
defuzzifier nn.Module | None

Custom defuzzifier. Defaults to :class:~highfis.defuzzifiers.SoftmaxLogDefuzzifier.

None
consequent_batch_norm bool

Batch normalisation on consequent inputs.

False
eps float | None

Numerical stability epsilon for the ALE-softmin operator.

None
use_en_frb bool

Start directly from the Enhanced FRB (En-FRB).

False
optimizer_type str

Optimizer for all training phases. "sgd" (default, paper) or "adamw".

'sgd'

Raises:

Type Description
ValueError

If n_classes < 2.

Source code in highfis/models/_dg_aletsk.py
def __init__(
    self,
    input_mfs: Mapping[str, Sequence[MembershipFunction]],
    n_classes: int,
    rule_base: str = "coco",
    rules: Sequence[Sequence[int]] | None = None,
    defuzzifier: nn.Module | None = None,
    consequent_batch_norm: bool = False,
    eps: float | None = None,
    use_en_frb: bool = False,
    optimizer_type: str = "sgd",
) -> None:
    """Initialise the DG-ALETSK classifier.

    Args:
        input_mfs: Mapping from feature name to a sequence of
            :class:`~highfis.memberships.MembershipFunction` objects.
        n_classes: Number of output classes (must be ≥ 2).
        rule_base: Rule-base construction strategy.  ``"coco"``
            (default, same-index compact), ``"cartesian"`` (all MF
            combinations), ``"en"`` (enhanced FRB; see also
            *use_en_frb*), or ``"custom"`` (explicit rules via *rules*).
        rules: Explicit rule antecedent indices; ignored when
            ``use_en_frb=True``.
        defuzzifier: Custom defuzzifier.  Defaults to
            :class:`~highfis.defuzzifiers.SoftmaxLogDefuzzifier`.
        consequent_batch_norm: Batch normalisation on consequent inputs.
        eps: Numerical stability epsilon for the ALE-softmin operator.
        use_en_frb: Start directly from the Enhanced FRB (En-FRB).
        optimizer_type: Optimizer for all training phases.  ``"sgd"`` (default,
            paper) or ``"adamw"``.

    Raises:
        ValueError: If ``n_classes < 2``.
    """
    if n_classes < 2:
        raise ValueError("n_classes must be >= 2")

    self.n_classes = int(n_classes)
    self.eps = eps
    self.use_en_frb = bool(use_en_frb)
    self._optimizer_type = str(optimizer_type)

    super().__init__(
        input_mfs,
        rule_base=rule_base,
        t_norm="prod",
        rules=rules,
        defuzzifier=defuzzifier or SoftmaxLogDefuzzifier(),
        consequent_batch_norm=consequent_batch_norm,
    )

    self.rule_layer = DGALETSKRuleLayer(
        self.input_names,
        [len(input_mfs[name]) for name in self.input_names],
        rules=rules if not self.use_en_frb else None,
        rule_base="en" if self.use_en_frb else rule_base,
        eps=self.eps,
    )
    self.n_rules = self.rule_layer.n_rules
    self.consequent_layer = self._build_zero_order_consequent_layer()

apply_thresholds

Apply threshold pruning to feature and rule gates.

Source code in highfis/models/_dg_aletsk.py
def apply_thresholds(self, tau_lambda: float, tau_theta: float) -> None:
    """Apply threshold pruning to feature and rule gates."""
    if not torch.isfinite(torch.tensor(tau_lambda)) or not torch.isfinite(torch.tensor(tau_theta)):
        raise ValueError("thresholds must be finite")

    feature_gate_values = self.get_feature_gate_values()
    pruned_features = feature_gate_values <= tau_lambda
    rule_layer = self.rule_layer
    cast(Tensor, rule_layer.lambda_gates.data)[pruned_features] = 0.0

    rule_gate_values = self.get_rule_gate_values()
    pruned_rules = rule_gate_values <= tau_theta
    consequent = self.consequent_layer
    cast(Tensor, consequent.theta_gates.data)[pruned_rules] = 0.0

compute_thresholds

Compute feature and rule thresholds from gate values and coefficient pairs.

Source code in highfis/models/_dg_aletsk.py
def compute_thresholds(self, zeta_lambda: float, zeta_theta: float) -> tuple[float, float]:
    """Compute feature and rule thresholds from gate values and coefficient pairs."""
    tau_lambda = _threshold_from_zeta(self.get_feature_gate_values(), zeta_lambda)
    tau_theta = _threshold_from_zeta(self.get_rule_gate_values(), zeta_theta)
    return tau_lambda, tau_theta

convert_to_first_order

Convert the DG phase zero-order consequent to first-order form.

Source code in highfis/models/_dg_aletsk.py
def convert_to_first_order(self) -> None:
    """Convert the DG phase zero-order consequent to first-order form."""
    previous = self.consequent_layer
    new_consequent = self._build_consequent_layer()
    if isinstance(previous, GatedClassificationZeroOrderConsequentLayer):
        new_consequent.theta_gates.data.copy_(previous.theta_gates.data)
    self.consequent_layer = new_consequent

forward

Full forward pass through the TSK pipeline.

Source code in highfis/models/_base.py
def forward(self, x: Tensor) -> Tensor:
    """Full forward pass through the TSK pipeline."""
    output, _ = self._forward_train(x)
    return output

forward_antecedents

Compute normalized rule strengths from model antecedents.

Source code in highfis/models/_base.py
def forward_antecedents(self, x: Tensor) -> Tensor:
    """Compute normalized rule strengths from model antecedents."""
    mu = self.membership_layer(x)
    w = self.rule_layer(mu)
    return self.defuzzifier(w)

get_consequent_weights

Return the consequent layer weights or None when unavailable.

Source code in highfis/models/_base.py
def get_consequent_weights(self) -> Tensor | None:
    """Return the consequent layer weights or ``None`` when unavailable."""
    weight = getattr(self.consequent_layer, "weight", None)
    if isinstance(weight, Tensor):
        return weight.detach()
    return None

get_feature_gate_values

Return normalized antecedent feature gate values for the DG phase.

Source code in highfis/models/_dg_aletsk.py
def get_feature_gate_values(self) -> Tensor:
    """Return normalized antecedent feature gate values for the DG phase."""
    rule_layer = self.rule_layer
    return rule_layer.gate_fn(rule_layer.lambda_gates)

get_mf_params

Return a serializable description of the model's membership functions.

Source code in highfis/models/_base.py
def get_mf_params(self) -> dict[str, list[dict[str, Any]]]:
    """Return a serializable description of the model's membership functions."""
    return {
        name: [{"type": type(mf).__name__, **mf.inspect_params()} for mf in cast(Sequence[MembershipFunction], mfs)]
        for name, mfs in self.membership_layer.input_mfs.items()
    }

get_rule_gate_values

Return normalized consequent rule gate values for the DG phase.

Source code in highfis/models/_dg_aletsk.py
def get_rule_gate_values(self) -> Tensor:
    """Return normalized consequent rule gate values for the DG phase."""
    consequent = self.consequent_layer
    return consequent.gate_fn(consequent.theta_gates)

get_rule_table

Return the rule base as a table of feature-to-MF indices.

Source code in highfis/models/_base.py
def get_rule_table(self) -> list[dict[str, Any]]:
    """Return the rule base as a table of feature-to-MF indices."""
    return [
        dict(
            rule_id=rule_index,
            **dict(zip(self.rule_layer.input_names, rule, strict=False)),
        )
        for rule_index, rule in enumerate(self.rule_layer.rules)
    ]

init_consequents_from_labels

Initialise zero-order consequent biases with one-hot labels for P-FRB.

Sets consequent_layer.bias[r, c] = 1 when sample r belongs to class c. Should be called before training when using P-FRB.

Parameters:

Name Type Description Default
y Tensor

Integer class labels of shape (N,) for the N training samples used to build the P-FRB. Only the first n_rules labels are used.

required

Raises:

Type Description
ValueError

If the model is not in zero-order consequent mode.

Source code in highfis/models/_dg_aletsk.py
def init_consequents_from_labels(self, y: Tensor) -> None:
    """Initialise zero-order consequent biases with one-hot labels for P-FRB.

    Sets ``consequent_layer.bias[r, c] = 1`` when sample *r* belongs to class *c*.
    Should be called before training when using P-FRB.

    Args:
        y: Integer class labels of shape ``(N,)`` for the *N* training samples
            used to build the P-FRB. Only the first ``n_rules`` labels are used.

    Raises:
        ValueError: If the model is not in zero-order consequent mode.
    """
    if not isinstance(self.consequent_layer, GatedClassificationZeroOrderConsequentLayer):
        raise ValueError(
            "init_consequents_from_labels() requires a zero-order consequent layer; "
            "call before convert_to_first_order()"
        )

    n = min(len(y), self.n_rules)
    dtype = self.consequent_layer.bias.dtype
    device = self.consequent_layer.bias.device
    one_hot = torch.zeros(self.n_rules, self.n_classes, dtype=dtype, device=device)
    one_hot[:n].scatter_(1, y[:n].to(device=device).unsqueeze(1), 1.0)
    self.consequent_layer.bias.data.copy_(one_hot)

predict

Return predicted class indices.

Source code in highfis/models/_common.py
def predict(self, x: Tensor) -> Tensor:
    """Return predicted class indices."""
    with torch.no_grad():
        return torch.argmax(self.predict_proba(x), dim=1)

predict_proba

Return class probabilities computed with softmax.

Source code in highfis/models/_common.py
def predict_proba(self, x: Tensor) -> Tensor:
    """Return class probabilities computed with softmax."""
    with torch.no_grad():
        if x.dtype == torch.float64:
            was_training = self.training
            self.double()
            try:
                self.eval()
                logits = self.forward(x)
                return torch.softmax(logits, dim=1)
            finally:
                self.float()
                self.train(was_training)
        else:
            was_training = self.training
            try:
                self.eval()
                logits = self.forward(x)
                return torch.softmax(logits, dim=1)
            finally:
                self.train(was_training)

prune_structure

Structurally prune the model to the given surviving feature and rule indices.

Rebuilds :attr:membership_layer, :attr:rule_layer, and :attr:consequent_layer in-place, retaining only the specified features and rules. All associated parameters are copied from the current layers.

Source code in highfis/models/_dg_aletsk.py
def prune_structure(
    self,
    surviving_features: list[int],
    surviving_rules: list[int],
) -> None:
    """Structurally prune the model to the given surviving feature and rule indices.

    Rebuilds :attr:`membership_layer`, :attr:`rule_layer`, and
    :attr:`consequent_layer` in-place, retaining only the specified
    features and rules.  All associated parameters are copied from
    the current layers.
    """
    if not surviving_features:
        raise ValueError("surviving_features must not be empty")
    if not surviving_rules:
        raise ValueError("surviving_rules must not be empty")

    surviving_names = [self.input_names[i] for i in surviving_features]

    new_input_mfs: dict[str, list[MembershipFunction]] = {}
    mf_remap: dict[int, dict[int, int]] = {}
    for orig_fi in surviving_features:
        name = self.input_names[orig_fi]
        all_mfs = list(cast(Any, self.membership_layer.input_mfs[name]))
        used = sorted({self.rule_layer.rules[r][orig_fi] for r in surviving_rules})
        new_input_mfs[name] = [all_mfs[mf_idx] for mf_idx in used]
        mf_remap[orig_fi] = {old: new for new, old in enumerate(used)}

    new_n_features = len(surviving_features)
    new_n_rules = len(surviving_rules)
    new_mf_per_input = [len(new_input_mfs[self.input_names[fi]]) for fi in surviving_features]
    new_rules = [
        tuple(mf_remap[fi][self.rule_layer.rules[r][fi]] for fi in surviving_features) for r in surviving_rules
    ]

    self.membership_layer = MembershipLayer(new_input_mfs)

    old_lambda = self.rule_layer.lambda_gates.data[surviving_features].clone()
    gate_fea = self.rule_layer.gate_fn
    new_rule_layer = DGALETSKRuleLayer(
        surviving_names,
        new_mf_per_input,
        rules=new_rules,
        eps=self.eps,
        gate_fea=gate_fea,
    )
    new_rule_layer.lambda_gates.data.copy_(old_lambda)
    self.rule_layer = new_rule_layer

    existing_gate_fn = self.consequent_layer.gate_fn
    if isinstance(self.consequent_layer, GatedClassificationZeroOrderConsequentLayer):
        old_bias = self.consequent_layer.bias.data[surviving_rules].clone()
        old_theta = self.consequent_layer.theta_gates.data[surviving_rules].clone()
        new_cons: GatedClassificationZeroOrderConsequentLayer | GatedClassificationConsequentLayer
        new_cons = GatedClassificationZeroOrderConsequentLayer(
            new_n_rules, new_n_features, self.n_classes, gate_fn=existing_gate_fn
        )
        new_cons.bias.data.copy_(old_bias)
        new_cons.theta_gates.data.copy_(old_theta)
    else:
        old_layer = cast(GatedClassificationConsequentLayer, self.consequent_layer)
        old_mode = old_layer.mode
        old_theta = old_layer.theta_gates.data[surviving_rules].clone()
        old_bias = old_layer.bias.data[surviving_rules].clone()
        old_weight = old_layer.weight.data[surviving_rules][:, :, surviving_features].clone()
        old_lam_cons = old_layer.lambda_gates.data[surviving_rules][:, surviving_features].clone()
        new_cons = GatedClassificationConsequentLayer(
            new_n_rules, new_n_features, self.n_classes, gate_fn=existing_gate_fn
        )
        new_cons.mode = old_mode
        new_cons.theta_gates.data.copy_(old_theta)
        new_cons.bias.data.copy_(old_bias)
        new_cons.weight.data.copy_(old_weight)
        new_cons.lambda_gates.data.copy_(old_lam_cons)
    self.consequent_layer = new_cons

    self.input_names = surviving_names
    self.n_inputs = new_n_features
    self.n_rules = new_n_rules
    self.input_mfs = new_input_mfs

search_thresholds

Search threshold coefficients for feature and rule pruning.

The search follows the DG-ALETSK paper strategy: thresholds are computed from gate values, applied to prune gates, and the first-order consequent parameters are refit with antecedents fixed.

Source code in highfis/models/_dg_aletsk.py
def search_thresholds(
    self,
    x: Tensor,
    y: Tensor,
    zeta_lambda: Sequence[float] | None = None,
    zeta_theta: Sequence[float] | None = None,
    x_val: Tensor | None = None,
    y_val: Tensor | None = None,
    use_lse: bool = True,
    inplace: bool = True,
    verbose: bool = False,
    structural: bool = True,
) -> dict[str, Any]:
    """Search threshold coefficients for feature and rule pruning.

    The search follows the DG-ALETSK paper strategy: thresholds are
    computed from gate values, applied to prune gates, and the first-order
    consequent parameters are refit with antecedents fixed.
    """
    if zeta_lambda is None:
        zeta_lambda = [0.0, 0.25, 0.5, 0.75, 1.0]
    if zeta_theta is None:
        zeta_theta = [0.0, 0.25, 0.5, 0.75, 1.0]

    x_eval = x_val if x_val is not None else x
    y_eval = y_val if y_val is not None else y

    (
        best_score,
        best_state,
        best_tau_lambda,
        best_tau_theta,
        best_zeta_lambda,
        best_zeta_theta,
    ) = _run_threshold_grid_search(self, x, y, x_eval, y_eval, zeta_lambda, zeta_theta, use_lse, verbose)

    result: dict[str, Any] = {
        "best_score": best_score,
        "best_zeta_lambda": best_zeta_lambda,
        "best_zeta_theta": best_zeta_theta,
        "tau_lambda": best_tau_lambda,
        "tau_theta": best_tau_theta,
    }

    result["surviving_feature_indices"] = list(range(self.n_inputs))
    result["surviving_rule_indices"] = list(range(self.n_rules))

    if inplace:
        if structural:
            feature_gate_values = self.get_feature_gate_values()
            rule_gate_values = self.get_rule_gate_values()
            sf = torch.where(feature_gate_values > best_tau_lambda)[0].tolist()
            sr = torch.where(rule_gate_values > best_tau_theta)[0].tolist()
            if not sf:
                sf = list(range(self.n_inputs))
            if not sr:
                sr = list(range(self.n_rules))
        else:
            sf, sr = [], []
        _apply_thresholds_and_pruning_inplace(
            self, x, y, best_tau_lambda, best_tau_theta, best_state, use_lse, structural, sf, sr, result
        )
    return result

DGALETSKRegressorModel

Bases: BaseTSKRegressorModel

DG-ALETSK regressor with ALE-softmin antecedent and double-group gates.

DG-ALETSK extends FSRE-ADATSK by replacing the adaptive softmin with the Adaptive Ln-Exp (ALE) softmin — a smoother variant with improved numerical stability. It also uses a zero-order consequent in the DG (data-guided) training phase and optionally converts to first-order after gate-based pruning.

Reference

G. Xue, J. Wang, B. Yuan and C. Dai, "DG-ALETSK: A High-Dimensional Fuzzy Approach With Simultaneous Feature Selection and Rule Extraction," in IEEE Transactions on Fuzzy Systems, vol. 31, no. 11, pp. 3866-3880, Nov. 2023, doi: 10.1109/TFUZZ.2023.3270445.

Initialise the DG-ALETSK regressor.

Parameters:

Name Type Description Default
input_mfs Mapping[str, Sequence[MembershipFunction]]

Mapping from feature name to a sequence of :class:~highfis.memberships.MembershipFunction objects.

required
rule_base str

Rule-base construction strategy. "coco" (default, same-index compact), "cartesian" (all MF combinations), "en" (enhanced FRB; see also use_en_frb), or "custom" (explicit rules via rules).

'coco'
rules Sequence[Sequence[int]] | None

Explicit rule antecedent indices; ignored when use_en_frb=True.

None
defuzzifier nn.Module | None

Custom defuzzifier. Defaults to :class:~highfis.defuzzifiers.SoftmaxLogDefuzzifier.

None
consequent_batch_norm bool

Batch normalisation on consequent inputs.

False
eps float | None

Numerical stability epsilon for the ALE-softmin operator.

None
use_en_frb bool

Start directly from the Enhanced FRB (En-FRB).

False
optimizer_type str

Optimizer for all training phases. "sgd" (default, paper) or "adamw".

'sgd'
Source code in highfis/models/_dg_aletsk.py
def __init__(
    self,
    input_mfs: Mapping[str, Sequence[MembershipFunction]],
    rule_base: str = "coco",
    rules: Sequence[Sequence[int]] | None = None,
    defuzzifier: nn.Module | None = None,
    consequent_batch_norm: bool = False,
    eps: float | None = None,
    use_en_frb: bool = False,
    optimizer_type: str = "sgd",
) -> None:
    """Initialise the DG-ALETSK regressor.

    Args:
        input_mfs: Mapping from feature name to a sequence of
            :class:`~highfis.memberships.MembershipFunction` objects.
        rule_base: Rule-base construction strategy.  ``"coco"``
            (default, same-index compact), ``"cartesian"`` (all MF
            combinations), ``"en"`` (enhanced FRB; see also
            *use_en_frb*), or ``"custom"`` (explicit rules via *rules*).
        rules: Explicit rule antecedent indices; ignored when
            ``use_en_frb=True``.
        defuzzifier: Custom defuzzifier.  Defaults to
            :class:`~highfis.defuzzifiers.SoftmaxLogDefuzzifier`.
        consequent_batch_norm: Batch normalisation on consequent inputs.
        eps: Numerical stability epsilon for the ALE-softmin operator.
        use_en_frb: Start directly from the Enhanced FRB (En-FRB).
        optimizer_type: Optimizer for all training phases.  ``"sgd"`` (default,
            paper) or ``"adamw"``.
    """
    self.eps = eps
    self.use_en_frb = bool(use_en_frb)
    self._optimizer_type = str(optimizer_type)

    super().__init__(
        input_mfs,
        rule_base=rule_base,
        t_norm="prod",
        rules=rules,
        defuzzifier=defuzzifier or SoftmaxLogDefuzzifier(),
        consequent_batch_norm=consequent_batch_norm,
    )

    self.rule_layer = DGALETSKRuleLayer(
        self.input_names,
        [len(input_mfs[name]) for name in self.input_names],
        rules=rules if not self.use_en_frb else None,
        rule_base="en" if self.use_en_frb else rule_base,
        eps=self.eps,
    )
    self.n_rules = self.rule_layer.n_rules
    self.consequent_layer = self._build_zero_order_consequent_layer()

apply_thresholds

Apply threshold pruning to feature and rule gates.

Source code in highfis/models/_dg_aletsk.py
def apply_thresholds(self, tau_lambda: float, tau_theta: float) -> None:
    """Apply threshold pruning to feature and rule gates."""
    if not torch.isfinite(torch.tensor(tau_lambda)) or not torch.isfinite(torch.tensor(tau_theta)):
        raise ValueError("thresholds must be finite")

    feature_gate_values = self.get_feature_gate_values()
    pruned_features = feature_gate_values <= tau_lambda
    rule_layer = self.rule_layer
    cast(Tensor, rule_layer.lambda_gates.data)[pruned_features] = 0.0

    rule_gate_values = self.get_rule_gate_values()
    pruned_rules = rule_gate_values <= tau_theta
    consequent = self.consequent_layer
    cast(Tensor, consequent.theta_gates.data)[pruned_rules] = 0.0

compute_thresholds

Compute feature and rule thresholds from gate values and coefficient pairs.

Source code in highfis/models/_dg_aletsk.py
def compute_thresholds(self, zeta_lambda: float, zeta_theta: float) -> tuple[float, float]:
    """Compute feature and rule thresholds from gate values and coefficient pairs."""
    tau_lambda = _threshold_from_zeta(self.get_feature_gate_values(), zeta_lambda)
    tau_theta = _threshold_from_zeta(self.get_rule_gate_values(), zeta_theta)
    return tau_lambda, tau_theta

convert_to_first_order

Convert the DG phase zero-order consequent to first-order form.

Source code in highfis/models/_dg_aletsk.py
def convert_to_first_order(self) -> None:
    """Convert the DG phase zero-order consequent to first-order form."""
    previous = self.consequent_layer
    new_consequent = self._build_consequent_layer()
    if isinstance(previous, GatedRegressionZeroOrderConsequentLayer):
        new_consequent.theta_gates.data.copy_(previous.theta_gates.data)
    self.consequent_layer = new_consequent

forward

Full forward pass through the TSK pipeline.

Source code in highfis/models/_base.py
def forward(self, x: Tensor) -> Tensor:
    """Full forward pass through the TSK pipeline."""
    output, _ = self._forward_train(x)
    return output

forward_antecedents

Compute normalized rule strengths from model antecedents.

Source code in highfis/models/_base.py
def forward_antecedents(self, x: Tensor) -> Tensor:
    """Compute normalized rule strengths from model antecedents."""
    mu = self.membership_layer(x)
    w = self.rule_layer(mu)
    return self.defuzzifier(w)

get_consequent_weights

Return the consequent layer weights or None when unavailable.

Source code in highfis/models/_base.py
def get_consequent_weights(self) -> Tensor | None:
    """Return the consequent layer weights or ``None`` when unavailable."""
    weight = getattr(self.consequent_layer, "weight", None)
    if isinstance(weight, Tensor):
        return weight.detach()
    return None

get_feature_gate_values

Return normalized antecedent feature gate values for the DG phase.

Source code in highfis/models/_dg_aletsk.py
def get_feature_gate_values(self) -> Tensor:
    """Return normalized antecedent feature gate values for the DG phase."""
    rule_layer = self.rule_layer
    return rule_layer.gate_fn(rule_layer.lambda_gates)

get_mf_params

Return a serializable description of the model's membership functions.

Source code in highfis/models/_base.py
def get_mf_params(self) -> dict[str, list[dict[str, Any]]]:
    """Return a serializable description of the model's membership functions."""
    return {
        name: [{"type": type(mf).__name__, **mf.inspect_params()} for mf in cast(Sequence[MembershipFunction], mfs)]
        for name, mfs in self.membership_layer.input_mfs.items()
    }

get_rule_gate_values

Return normalized consequent rule gate values for the DG phase.

Source code in highfis/models/_dg_aletsk.py
def get_rule_gate_values(self) -> Tensor:
    """Return normalized consequent rule gate values for the DG phase."""
    consequent = self.consequent_layer
    return consequent.gate_fn(consequent.theta_gates)

get_rule_table

Return the rule base as a table of feature-to-MF indices.

Source code in highfis/models/_base.py
def get_rule_table(self) -> list[dict[str, Any]]:
    """Return the rule base as a table of feature-to-MF indices."""
    return [
        dict(
            rule_id=rule_index,
            **dict(zip(self.rule_layer.input_names, rule, strict=False)),
        )
        for rule_index, rule in enumerate(self.rule_layer.rules)
    ]

predict

Return predicted values as a 1-D tensor.

Source code in highfis/models/_common.py
def predict(self, x: Tensor) -> Tensor:
    """Return predicted values as a 1-D tensor."""
    with torch.no_grad():
        if x.dtype == torch.float64:
            was_training = self.training
            self.double()
            try:
                self.eval()
                return self.forward(x).squeeze(1)
            finally:
                self.float()
                self.train(was_training)
        else:
            was_training = self.training
            try:
                self.eval()
                return self.forward(x).squeeze(1)
            finally:
                self.train(was_training)

prune_structure

Structurally prune the model to the given surviving feature and rule indices.

Rebuilds :attr:membership_layer, :attr:rule_layer, and :attr:consequent_layer in-place, retaining only the specified features and rules. All associated parameters are copied from the current layers.

Source code in highfis/models/_dg_aletsk.py
def prune_structure(
    self,
    surviving_features: list[int],
    surviving_rules: list[int],
) -> None:
    """Structurally prune the model to the given surviving feature and rule indices.

    Rebuilds :attr:`membership_layer`, :attr:`rule_layer`, and
    :attr:`consequent_layer` in-place, retaining only the specified
    features and rules.  All associated parameters are copied from
    the current layers.
    """
    if not surviving_features:
        raise ValueError("surviving_features must not be empty")
    if not surviving_rules:
        raise ValueError("surviving_rules must not be empty")

    surviving_names = [self.input_names[i] for i in surviving_features]

    new_input_mfs: dict[str, list[MembershipFunction]] = {}
    mf_remap: dict[int, dict[int, int]] = {}
    for orig_fi in surviving_features:
        name = self.input_names[orig_fi]
        all_mfs = list(cast(Any, self.membership_layer.input_mfs[name]))
        used = sorted({self.rule_layer.rules[r][orig_fi] for r in surviving_rules})
        new_input_mfs[name] = [all_mfs[mf_idx] for mf_idx in used]
        mf_remap[orig_fi] = {old: new for new, old in enumerate(used)}

    new_n_features = len(surviving_features)
    new_n_rules = len(surviving_rules)
    new_mf_per_input = [len(new_input_mfs[self.input_names[fi]]) for fi in surviving_features]
    new_rules = [
        tuple(mf_remap[fi][self.rule_layer.rules[r][fi]] for fi in surviving_features) for r in surviving_rules
    ]

    self.membership_layer = MembershipLayer(new_input_mfs)

    old_lambda = self.rule_layer.lambda_gates.data[surviving_features].clone()
    gate_fea = self.rule_layer.gate_fn
    new_rule_layer = DGALETSKRuleLayer(
        surviving_names,
        new_mf_per_input,
        rules=new_rules,
        eps=self.eps,
        gate_fea=gate_fea,
    )
    new_rule_layer.lambda_gates.data.copy_(old_lambda)
    self.rule_layer = new_rule_layer

    existing_gate_fn = self.consequent_layer.gate_fn
    if isinstance(self.consequent_layer, GatedRegressionZeroOrderConsequentLayer):
        old_bias = self.consequent_layer.bias.data[surviving_rules].clone()
        old_theta = self.consequent_layer.theta_gates.data[surviving_rules].clone()
        new_cons: GatedRegressionZeroOrderConsequentLayer | GatedRegressionConsequentLayer
        new_cons = GatedRegressionZeroOrderConsequentLayer(new_n_rules, new_n_features, gate_fn=existing_gate_fn)
        new_cons.bias.data.copy_(old_bias)
        new_cons.theta_gates.data.copy_(old_theta)
    else:
        old_layer = cast(GatedRegressionConsequentLayer, self.consequent_layer)
        old_mode = old_layer.mode
        old_theta = old_layer.theta_gates.data[surviving_rules].clone()
        old_bias = old_layer.bias.data[surviving_rules].clone()
        old_weight = old_layer.weight.data[surviving_rules][:, surviving_features].clone()
        old_lam_cons = old_layer.lambda_gates.data[surviving_rules][:, surviving_features].clone()
        new_cons = GatedRegressionConsequentLayer(new_n_rules, new_n_features, gate_fn=existing_gate_fn)
        new_cons.mode = old_mode
        new_cons.theta_gates.data.copy_(old_theta)
        new_cons.bias.data.copy_(old_bias)
        new_cons.weight.data.copy_(old_weight)
        new_cons.lambda_gates.data.copy_(old_lam_cons)
    self.consequent_layer = new_cons

    self.input_names = surviving_names
    self.n_inputs = new_n_features
    self.n_rules = new_n_rules
    self.input_mfs = new_input_mfs

search_thresholds

Search threshold coefficients for feature and rule pruning.

Source code in highfis/models/_dg_aletsk.py
def search_thresholds(
    self,
    x: Tensor,
    y: Tensor,
    zeta_lambda: Sequence[float] | None = None,
    zeta_theta: Sequence[float] | None = None,
    x_val: Tensor | None = None,
    y_val: Tensor | None = None,
    use_lse: bool = True,
    inplace: bool = True,
    verbose: bool = False,
    structural: bool = True,
) -> dict[str, Any]:
    """Search threshold coefficients for feature and rule pruning."""
    if zeta_lambda is None:
        zeta_lambda = [0.0, 0.25, 0.5, 0.75, 1.0]
    if zeta_theta is None:
        zeta_theta = [0.0, 0.25, 0.5, 0.75, 1.0]

    x_eval = x_val if x_val is not None else x
    y_eval = y_val if y_val is not None else y

    (
        best_score,
        best_state,
        best_tau_lambda,
        best_tau_theta,
        best_zeta_lambda,
        best_zeta_theta,
    ) = _run_threshold_grid_search(self, x, y, x_eval, y_eval, zeta_lambda, zeta_theta, use_lse, verbose)

    result: dict[str, Any] = {
        "best_score": best_score,
        "best_zeta_lambda": best_zeta_lambda,
        "best_zeta_theta": best_zeta_theta,
        "tau_lambda": best_tau_lambda,
        "tau_theta": best_tau_theta,
    }

    result["surviving_feature_indices"] = list(range(self.n_inputs))
    result["surviving_rule_indices"] = list(range(self.n_rules))

    if inplace:
        if structural:
            feature_gate_values = self.get_feature_gate_values()
            rule_gate_values = self.get_rule_gate_values()
            sf = torch.where(feature_gate_values > best_tau_lambda)[0].tolist()
            sr = torch.where(rule_gate_values > best_tau_theta)[0].tolist()
            if not sf:
                sf = list(range(self.n_inputs))
            if not sr:
                sr = list(range(self.n_rules))
        else:
            sf, sr = [], []
        _apply_thresholds_and_pruning_inplace(
            self, x, y, best_tau_lambda, best_tau_theta, best_state, use_lse, structural, sf, sr, result
        )

    return result

DGTSKClassifierModel

Bases: BaseTSKClassifierModel

DG-TSK classifier with M-gate antecedent and point-based FRB (P-FRB).

DG-TSK uses a data-guided M-gate function to automatically select relevant features and rules.

Reference

Guangdong Xue, Jian Wang, Bingjie Zhang, Bin Yuan, Caili Dai, Double groups of gates based Takagi-Sugeno-Kang (DG-TSK) fuzzy system for simultaneous feature selection and rule extraction, Fuzzy Sets and Systems, Volume 469, 2023, 108627, ISSN 0165-0114, https://doi.org/10.1016/j.fss.2023.108627.

Initialise the DG-TSK classifier.

Parameters:

Name Type Description Default
input_mfs Mapping[str, Sequence[MembershipFunction]]

Mapping from feature name to a sequence of :class:~highfis.memberships.MembershipFunction objects.

required
n_classes int

Number of output classes (must be ≥ 2).

required
rule_base str

Rule-base construction strategy. "coco" (default, same-index compact), "cartesian" (all MF combinations), "en" (enhanced FRB; see also use_en_frb), or "custom" (explicit rules via rules).

'coco'
gate_fea str | Callable[[Tensor], Tensor] | None

Gate function for antecedent feature selection. "gate_m" (default) uses the M-gate from the DG-TSK paper. Can also be any callable Tensor → Tensor.

'gate_m'
gate_rule str | Callable[[Tensor], Tensor] | None

Gate function for consequent rule selection. Same options as gate_fea.

'gate_m'
rules Sequence[Sequence[int]] | None

Explicit rule antecedent indices; ignored when use_en_frb=True.

None
defuzzifier nn.Module | None

Custom defuzzifier. Defaults to :class:~highfis.defuzzifiers.SoftmaxLogDefuzzifier.

None
consequent_batch_norm bool

Batch normalisation on consequent inputs.

False
eps float | None

Numerical stability epsilon.

None
use_en_frb bool

Use the Enhanced FRB (P-FRB) rule base.

False
optimizer_type str

Optimizer for all training phases. "sgd" (default, paper) or "adamw".

'sgd'

Raises:

Type Description
ValueError

If n_classes < 2.

Source code in highfis/models/_dg_tsk.py
def __init__(
    self,
    input_mfs: Mapping[str, Sequence[MembershipFunction]],
    n_classes: int,
    rule_base: str = "coco",
    gate_fea: str | Callable[[Tensor], Tensor] | None = "gate_m",
    gate_rule: str | Callable[[Tensor], Tensor] | None = "gate_m",
    rules: Sequence[Sequence[int]] | None = None,
    defuzzifier: nn.Module | None = None,
    consequent_batch_norm: bool = False,
    eps: float | None = None,
    use_en_frb: bool = False,
    optimizer_type: str = "sgd",
) -> None:
    """Initialise the DG-TSK classifier.

    Args:
        input_mfs: Mapping from feature name to a sequence of
            :class:`~highfis.memberships.MembershipFunction` objects.
        n_classes: Number of output classes (must be ≥ 2).
        rule_base: Rule-base construction strategy.  ``"coco"``
            (default, same-index compact), ``"cartesian"`` (all MF
            combinations), ``"en"`` (enhanced FRB; see also
            *use_en_frb*), or ``"custom"`` (explicit rules via *rules*).
        gate_fea: Gate function for antecedent feature selection.
            ``"gate_m"`` (default) uses the M-gate from the DG-TSK paper.
            Can also be any callable ``Tensor → Tensor``.
        gate_rule: Gate function for consequent rule selection.
            Same options as ``gate_fea``.
        rules: Explicit rule antecedent indices; ignored when
            ``use_en_frb=True``.
        defuzzifier: Custom defuzzifier.  Defaults to
            :class:`~highfis.defuzzifiers.SoftmaxLogDefuzzifier`.
        consequent_batch_norm: Batch normalisation on consequent inputs.
        eps: Numerical stability epsilon.
        use_en_frb: Use the Enhanced FRB (P-FRB) rule base.
        optimizer_type: Optimizer for all training phases.  ``"sgd"`` (default,
            paper) or ``"adamw"``.

    Raises:
        ValueError: If ``n_classes < 2``.
    """
    if n_classes < 2:
        raise ValueError("n_classes must be >= 2")

    self.n_classes = int(n_classes)
    self.gate_fea = gate_fea
    self.gate_rule = gate_rule
    self.eps = eps
    self.use_en_frb = bool(use_en_frb)
    self._optimizer_type = str(optimizer_type)

    super().__init__(
        input_mfs,
        rule_base=rule_base,
        t_norm="prod",
        rules=rules,
        defuzzifier=defuzzifier or SoftmaxLogDefuzzifier(),
        consequent_batch_norm=consequent_batch_norm,
    )

    self.rule_layer = DGTSKRuleLayer(
        self.input_names,
        [len(input_mfs[name]) for name in self.input_names],
        rules=rules if not self.use_en_frb else None,
        rule_base="en" if self.use_en_frb else rule_base,
        gate_fea=self.gate_fea,
        eps=self.eps,
    )
    self.n_rules = self.rule_layer.n_rules
    self.consequent_layer = self._build_zero_order_consequent_layer()

apply_thresholds

Prune DG-TSK feature and rule gates using the computed thresholds.

Source code in highfis/models/_dg_tsk.py
def apply_thresholds(self, tau_lambda: float, tau_theta: float) -> None:
    """Prune DG-TSK feature and rule gates using the computed thresholds."""
    if not torch.isfinite(torch.tensor(tau_lambda)) or not torch.isfinite(torch.tensor(tau_theta)):
        raise ValueError("thresholds must be finite")

    feature_gate_values = self.get_feature_gate_values()
    pruned_features = feature_gate_values <= tau_lambda
    cast(Tensor, self.rule_layer.lambda_gates.data)[pruned_features] = 0.0

    rule_gate_values = self.get_rule_gate_values()
    pruned_rules = rule_gate_values <= tau_theta
    cast(Tensor, self.consequent_layer.theta_gates.data)[pruned_rules] = 0.0

compute_thresholds

Compute DG-TSK pruning thresholds from gate values and zeta parameters.

Source code in highfis/models/_dg_tsk.py
def compute_thresholds(self, zeta_lambda: float, zeta_theta: float) -> tuple[float, float]:
    """Compute DG-TSK pruning thresholds from gate values and zeta parameters."""
    tau_lambda = _threshold_from_zeta(self.get_feature_gate_values(), zeta_lambda)
    tau_theta = _threshold_from_zeta(self.get_rule_gate_values(), zeta_theta)
    return tau_lambda, tau_theta

convert_to_first_order

Convert the DG-TSK model from zero-order to first-order consequent.

Source code in highfis/models/_dg_tsk.py
def convert_to_first_order(self) -> None:
    """Convert the DG-TSK model from zero-order to first-order consequent."""
    previous = self.consequent_layer
    new_consequent = self._build_consequent_layer()
    if isinstance(previous, GatedClassificationZeroOrderConsequentLayer):
        new_consequent.theta_gates.data.copy_(previous.theta_gates.data)
    self.consequent_layer = new_consequent

forward

Full forward pass through the TSK pipeline.

Source code in highfis/models/_base.py
def forward(self, x: Tensor) -> Tensor:
    """Full forward pass through the TSK pipeline."""
    output, _ = self._forward_train(x)
    return output

forward_antecedents

Compute normalized rule strengths from model antecedents.

Source code in highfis/models/_base.py
def forward_antecedents(self, x: Tensor) -> Tensor:
    """Compute normalized rule strengths from model antecedents."""
    mu = self.membership_layer(x)
    w = self.rule_layer(mu)
    return self.defuzzifier(w)

get_consequent_weights

Return the consequent layer weights or None when unavailable.

Source code in highfis/models/_base.py
def get_consequent_weights(self) -> Tensor | None:
    """Return the consequent layer weights or ``None`` when unavailable."""
    weight = getattr(self.consequent_layer, "weight", None)
    if isinstance(weight, Tensor):
        return weight.detach()
    return None

get_feature_gate_values

Return normalized DG-TSK feature gate activations from lambda values.

Source code in highfis/models/_dg_tsk.py
def get_feature_gate_values(self) -> Tensor:
    """Return normalized DG-TSK feature gate activations from lambda values."""
    return self.rule_layer.gate_fn(self.rule_layer.lambda_gates)

get_mf_params

Return a serializable description of the model's membership functions.

Source code in highfis/models/_base.py
def get_mf_params(self) -> dict[str, list[dict[str, Any]]]:
    """Return a serializable description of the model's membership functions."""
    return {
        name: [{"type": type(mf).__name__, **mf.inspect_params()} for mf in cast(Sequence[MembershipFunction], mfs)]
        for name, mfs in self.membership_layer.input_mfs.items()
    }

get_rule_gate_values

Return normalized DG-TSK rule gate activations from theta values.

Source code in highfis/models/_dg_tsk.py
def get_rule_gate_values(self) -> Tensor:
    """Return normalized DG-TSK rule gate activations from theta values."""
    return self.consequent_layer.gate_fn(self.consequent_layer.theta_gates)

get_rule_table

Return the rule base as a table of feature-to-MF indices.

Source code in highfis/models/_base.py
def get_rule_table(self) -> list[dict[str, Any]]:
    """Return the rule base as a table of feature-to-MF indices."""
    return [
        dict(
            rule_id=rule_index,
            **dict(zip(self.rule_layer.input_names, rule, strict=False)),
        )
        for rule_index, rule in enumerate(self.rule_layer.rules)
    ]

init_consequents_from_labels

Initialise zero-order consequent biases with one-hot encoded labels (P-FRB paper eq. 24).

Sets consequent_layer.bias[r, c] = 1 when sample r belongs to class c, exactly as in Xue et al. (2023) eq. (24): \(p^0_{r,c} = y_{r,c}\). Should be called before :meth:fit_dg_phase when using P-FRB.

Parameters:

Name Type Description Default
y Tensor

Integer class labels of shape (N,) for the N training samples used to build the P-FRB. Only the first n_rules labels are used.

required

Raises:

Type Description
ValueError

If the model is not in zero-order consequent mode.

Source code in highfis/models/_dg_tsk.py
def init_consequents_from_labels(self, y: Tensor) -> None:
    """Initialise zero-order consequent biases with one-hot encoded labels (P-FRB paper eq. 24).

    Sets ``consequent_layer.bias[r, c] = 1`` when sample *r* belongs to class *c*,
    exactly as in Xue et al. (2023) eq. (24): $p^0_{r,c} = y_{r,c}$.
    Should be called before :meth:`fit_dg_phase` when using P-FRB.

    Args:
        y: Integer class labels of shape ``(N,)`` for the *N* training samples
            used to build the P-FRB. Only the first ``n_rules`` labels are used.

    Raises:
        ValueError: If the model is not in zero-order consequent mode.
    """
    if not isinstance(self.consequent_layer, GatedClassificationZeroOrderConsequentLayer):
        raise ValueError(
            "init_consequents_from_labels() requires a zero-order consequent layer; "
            "call before convert_to_first_order()"
        )
    n = min(len(y), self.n_rules)
    dtype = self.consequent_layer.bias.dtype
    device = self.consequent_layer.bias.device
    one_hot = torch.zeros(self.n_rules, self.n_classes, dtype=dtype, device=device)
    one_hot[:n].scatter_(1, y[:n].to(device=device).unsqueeze(1), 1.0)
    self.consequent_layer.bias.data.copy_(one_hot)

predict

Return predicted class indices.

Source code in highfis/models/_common.py
def predict(self, x: Tensor) -> Tensor:
    """Return predicted class indices."""
    with torch.no_grad():
        return torch.argmax(self.predict_proba(x), dim=1)

predict_proba

Return class probabilities computed with softmax.

Source code in highfis/models/_common.py
def predict_proba(self, x: Tensor) -> Tensor:
    """Return class probabilities computed with softmax."""
    with torch.no_grad():
        if x.dtype == torch.float64:
            was_training = self.training
            self.double()
            try:
                self.eval()
                logits = self.forward(x)
                return torch.softmax(logits, dim=1)
            finally:
                self.float()
                self.train(was_training)
        else:
            was_training = self.training
            try:
                self.eval()
                logits = self.forward(x)
                return torch.softmax(logits, dim=1)
            finally:
                self.train(was_training)

prune_structure

Structurally prune the model to the given surviving feature and rule indices.

Rebuilds :attr:membership_layer, :attr:rule_layer, and :attr:consequent_layer in-place, retaining only the specified features and rules. All associated parameters are copied from the current layers.

Source code in highfis/models/_dg_tsk.py
def prune_structure(
    self,
    surviving_features: list[int],
    surviving_rules: list[int],
) -> None:
    """Structurally prune the model to the given surviving feature and rule indices.

    Rebuilds :attr:`membership_layer`, :attr:`rule_layer`, and
    :attr:`consequent_layer` in-place, retaining only the specified
    features and rules.  All associated parameters are copied from
    the current layers.
    """
    if not surviving_features:
        raise ValueError("surviving_features must not be empty")
    if not surviving_rules:
        raise ValueError("surviving_rules must not be empty")

    surviving_names = [self.input_names[i] for i in surviving_features]

    # For each surviving feature collect the MF indices used by the surviving
    # rules and build a remapping to a compressed 0-based index.
    new_input_mfs: dict[str, list[MembershipFunction]] = {}
    mf_remap: dict[int, dict[int, int]] = {}
    for orig_fi in surviving_features:
        name = self.input_names[orig_fi]
        all_mfs = list(cast(Any, self.membership_layer.input_mfs[name]))
        used = sorted({self.rule_layer.rules[r][orig_fi] for r in surviving_rules})
        new_input_mfs[name] = [all_mfs[mf_idx] for mf_idx in used]
        mf_remap[orig_fi] = {old: new for new, old in enumerate(used)}

    new_n_features = len(surviving_features)
    new_n_rules = len(surviving_rules)
    new_mf_per_input = [len(new_input_mfs[self.input_names[fi]]) for fi in surviving_features]
    new_rules = [
        tuple(mf_remap[fi][self.rule_layer.rules[r][fi]] for fi in surviving_features) for r in surviving_rules
    ]

    # Rebuild membership layer.
    self.membership_layer = MembershipLayer(new_input_mfs)

    # Rebuild rule layer, copying lambda_gates for surviving features.
    old_lambda = self.rule_layer.lambda_gates.data[surviving_features].clone()
    new_rule_layer = DGTSKRuleLayer(
        surviving_names,
        new_mf_per_input,
        rules=new_rules,
        gate_fea=self.gate_fea,
        eps=self.eps,
    )
    new_rule_layer.lambda_gates.data.copy_(old_lambda)
    self.rule_layer = new_rule_layer

    # Rebuild consequent layer, copying surviving parameter slices.
    if isinstance(self.consequent_layer, GatedClassificationZeroOrderConsequentLayer):
        old_bias = self.consequent_layer.bias.data[surviving_rules].clone()
        old_theta = self.consequent_layer.theta_gates.data[surviving_rules].clone()
        new_cons: GatedClassificationZeroOrderConsequentLayer | GatedClassificationConsequentLayer
        new_cons = GatedClassificationZeroOrderConsequentLayer(
            new_n_rules, new_n_features, self.n_classes, gate_fn=self.gate_rule
        )
        new_cons.bias.data.copy_(old_bias)
        new_cons.theta_gates.data.copy_(old_theta)
    else:
        old_layer = cast(GatedClassificationConsequentLayer, self.consequent_layer)
        old_mode = old_layer.mode
        old_theta = old_layer.theta_gates.data[surviving_rules].clone()
        old_bias = old_layer.bias.data[surviving_rules].clone()
        old_weight = old_layer.weight.data[surviving_rules][:, :, surviving_features].clone()
        old_lam_cons = old_layer.lambda_gates.data[surviving_rules][:, surviving_features].clone()
        new_cons = GatedClassificationConsequentLayer(
            new_n_rules, new_n_features, self.n_classes, gate_fn=self.gate_rule
        )
        new_cons.mode = old_mode
        new_cons.theta_gates.data.copy_(old_theta)
        new_cons.bias.data.copy_(old_bias)
        new_cons.weight.data.copy_(old_weight)
        new_cons.lambda_gates.data.copy_(old_lam_cons)
    self.consequent_layer = new_cons

    # Update model-level bookkeeping.
    self.input_names = surviving_names
    self.n_inputs = new_n_features
    self.n_rules = new_n_rules
    self.input_mfs = new_input_mfs

search_thresholds

Search DG-TSK threshold combinations and optionally apply the best candidate.

Source code in highfis/models/_dg_tsk.py
def search_thresholds(
    self,
    x: Tensor,
    y: Tensor,
    zeta_lambda: Sequence[float] | None = None,
    zeta_theta: Sequence[float] | None = None,
    x_val: Tensor | None = None,
    y_val: Tensor | None = None,
    use_lse: bool = True,
    inplace: bool = True,
    verbose: bool = False,
    structural: bool = True,
) -> dict[str, Any]:
    """Search DG-TSK threshold combinations and optionally apply the best candidate."""
    if zeta_lambda is None:
        zeta_lambda = [0.0, 0.25, 0.5, 0.75, 1.0]
    if zeta_theta is None:
        zeta_theta = [0.0, 0.25, 0.5, 0.75, 1.0]

    x_eval = x_val if x_val is not None else x
    y_eval = y_val if y_val is not None else y

    (
        best_score,
        best_state,
        best_tau_lambda,
        best_tau_theta,
        best_zeta_lambda,
        best_zeta_theta,
    ) = _run_threshold_grid_search(self, x, y, x_eval, y_eval, zeta_lambda, zeta_theta, use_lse, verbose)

    result: dict[str, Any] = {
        "best_score": best_score,
        "best_zeta_lambda": best_zeta_lambda,
        "best_zeta_theta": best_zeta_theta,
        "tau_lambda": best_tau_lambda,
        "tau_theta": best_tau_theta,
    }

    result["surviving_feature_indices"] = list(range(self.n_inputs))
    result["surviving_rule_indices"] = list(range(self.n_rules))

    if inplace:
        if structural:
            # Compute surviving indices before zeroing gate params.
            feature_gate_values = self.get_feature_gate_values()
            rule_gate_values = self.get_rule_gate_values()
            sf = torch.where(feature_gate_values > best_tau_lambda)[0].tolist()
            sr = torch.where(rule_gate_values > best_tau_theta)[0].tolist()
            if not sf:
                sf = list(range(self.n_inputs))
            if len(sr) < self.n_classes:
                top_k = min(self.n_classes, self.n_rules)
                top_rules = torch.topk(rule_gate_values, k=top_k).indices.tolist()
                sr = sorted(set(sr) | set(top_rules))
        else:
            sf, sr = [], []
        _apply_thresholds_and_pruning_inplace(
            self, x, y, best_tau_lambda, best_tau_theta, best_state, use_lse, structural, sf, sr, result
        )

    return result

DGTSKRegressorModel

Bases: BaseTSKRegressorModel

DG-TSK regressor with M-gate antecedent and point-based FRB (P-FRB).

DG-TSK uses a data-guided M-gate function to automatically select relevant features and rules.

Reference

Guangdong Xue, Jian Wang, Bingjie Zhang, Bin Yuan, Caili Dai, Double groups of gates based Takagi-Sugeno-Kang (DG-TSK) fuzzy system for simultaneous feature selection and rule extraction, Fuzzy Sets and Systems, Volume 469, 2023, 108627, ISSN 0165-0114, https://doi.org/10.1016/j.fss.2023.108627.

Initialise the DG-TSK regressor.

Parameters:

Name Type Description Default
input_mfs Mapping[str, Sequence[MembershipFunction]]

Mapping from feature name to a sequence of :class:~highfis.memberships.MembershipFunction objects.

required
rule_base str

Rule-base construction strategy. "coco" (default, same-index compact), "cartesian" (all MF combinations), "en" (enhanced FRB; see also use_en_frb), or "custom" (explicit rules via rules).

'coco'
gate_fea str | Callable[[Tensor], Tensor] | None

Gate function for antecedent feature selection (default "gate_m")).

'gate_m'
gate_rule str | Callable[[Tensor], Tensor] | None

Gate function for consequent rule selection (default "gate_m").

'gate_m'
rules Sequence[Sequence[int]] | None

Explicit rule antecedent indices; ignored when use_en_frb=True.

None
defuzzifier nn.Module | None

Custom defuzzifier. Defaults to :class:~highfis.defuzzifiers.SoftmaxLogDefuzzifier.

None
consequent_batch_norm bool

Batch normalisation on consequent inputs.

False
eps float | None

Numerical stability epsilon.

None
use_en_frb bool

Use the Enhanced FRB (P-FRB) rule base.

False
optimizer_type str

Optimizer for all training phases. "sgd" (default, paper) or "adamw".

'sgd'
Source code in highfis/models/_dg_tsk.py
def __init__(
    self,
    input_mfs: Mapping[str, Sequence[MembershipFunction]],
    rule_base: str = "coco",
    gate_fea: str | Callable[[Tensor], Tensor] | None = "gate_m",
    gate_rule: str | Callable[[Tensor], Tensor] | None = "gate_m",
    rules: Sequence[Sequence[int]] | None = None,
    defuzzifier: nn.Module | None = None,
    consequent_batch_norm: bool = False,
    eps: float | None = None,
    use_en_frb: bool = False,
    optimizer_type: str = "sgd",
) -> None:
    """Initialise the DG-TSK regressor.

    Args:
        input_mfs: Mapping from feature name to a sequence of
            :class:`~highfis.memberships.MembershipFunction` objects.
        rule_base: Rule-base construction strategy.  ``"coco"``
            (default, same-index compact), ``"cartesian"`` (all MF
            combinations), ``"en"`` (enhanced FRB; see also
            *use_en_frb*), or ``"custom"`` (explicit rules via *rules*).
        gate_fea: Gate function for antecedent feature selection
            (default ``"gate_m"``)).
        gate_rule: Gate function for consequent rule selection
            (default ``"gate_m"``).
        rules: Explicit rule antecedent indices; ignored when
            ``use_en_frb=True``.
        defuzzifier: Custom defuzzifier.  Defaults to
            :class:`~highfis.defuzzifiers.SoftmaxLogDefuzzifier`.
        consequent_batch_norm: Batch normalisation on consequent inputs.
        eps: Numerical stability epsilon.
        use_en_frb: Use the Enhanced FRB (P-FRB) rule base.
        optimizer_type: Optimizer for all training phases.  ``"sgd"`` (default,
            paper) or ``"adamw"``.
    """
    self.gate_fea = gate_fea
    self.gate_rule = gate_rule
    self.eps = eps
    self.use_en_frb = bool(use_en_frb)
    self._optimizer_type = str(optimizer_type)

    super().__init__(
        input_mfs,
        rule_base=rule_base,
        t_norm="prod",
        rules=rules,
        defuzzifier=defuzzifier or SoftmaxLogDefuzzifier(),
        consequent_batch_norm=consequent_batch_norm,
    )

    self.rule_layer = DGTSKRuleLayer(
        self.input_names,
        [len(input_mfs[name]) for name in self.input_names],
        rules=rules if not self.use_en_frb else None,
        rule_base="en" if self.use_en_frb else rule_base,
        gate_fea=self.gate_fea,
        eps=self.eps,
    )
    self.n_rules = self.rule_layer.n_rules
    self.consequent_layer = self._build_zero_order_consequent_layer()

apply_thresholds

Prune DG-TSK feature and rule gates using the computed thresholds.

Source code in highfis/models/_dg_tsk.py
def apply_thresholds(self, tau_lambda: float, tau_theta: float) -> None:
    """Prune DG-TSK feature and rule gates using the computed thresholds."""
    if not torch.isfinite(torch.tensor(tau_lambda)) or not torch.isfinite(torch.tensor(tau_theta)):
        raise ValueError("thresholds must be finite")

    feature_gate_values = self.get_feature_gate_values()
    pruned_features = feature_gate_values <= tau_lambda
    cast(Tensor, self.rule_layer.lambda_gates.data)[pruned_features] = 0.0

    rule_gate_values = self.get_rule_gate_values()
    pruned_rules = rule_gate_values <= tau_theta
    cast(Tensor, self.consequent_layer.theta_gates.data)[pruned_rules] = 0.0

compute_thresholds

Compute DG-TSK pruning thresholds from gate values and zeta parameters.

Source code in highfis/models/_dg_tsk.py
def compute_thresholds(self, zeta_lambda: float, zeta_theta: float) -> tuple[float, float]:
    """Compute DG-TSK pruning thresholds from gate values and zeta parameters."""
    tau_lambda = _threshold_from_zeta(self.get_feature_gate_values(), zeta_lambda)
    tau_theta = _threshold_from_zeta(self.get_rule_gate_values(), zeta_theta)
    return tau_lambda, tau_theta

convert_to_first_order

Convert the DG-TSK regressor from zero-order to first-order consequent.

Source code in highfis/models/_dg_tsk.py
def convert_to_first_order(self) -> None:
    """Convert the DG-TSK regressor from zero-order to first-order consequent."""
    previous = self.consequent_layer
    new_consequent = self._build_consequent_layer()
    if isinstance(previous, GatedRegressionZeroOrderConsequentLayer):
        new_consequent.theta_gates.data.copy_(previous.theta_gates.data)
    self.consequent_layer = new_consequent

forward

Full forward pass through the TSK pipeline.

Source code in highfis/models/_base.py
def forward(self, x: Tensor) -> Tensor:
    """Full forward pass through the TSK pipeline."""
    output, _ = self._forward_train(x)
    return output

forward_antecedents

Compute normalized rule strengths from model antecedents.

Source code in highfis/models/_base.py
def forward_antecedents(self, x: Tensor) -> Tensor:
    """Compute normalized rule strengths from model antecedents."""
    mu = self.membership_layer(x)
    w = self.rule_layer(mu)
    return self.defuzzifier(w)

get_consequent_weights

Return the consequent layer weights or None when unavailable.

Source code in highfis/models/_base.py
def get_consequent_weights(self) -> Tensor | None:
    """Return the consequent layer weights or ``None`` when unavailable."""
    weight = getattr(self.consequent_layer, "weight", None)
    if isinstance(weight, Tensor):
        return weight.detach()
    return None

get_feature_gate_values

Return normalized DG-TSK feature gate activations from lambda values.

Source code in highfis/models/_dg_tsk.py
def get_feature_gate_values(self) -> Tensor:
    """Return normalized DG-TSK feature gate activations from lambda values."""
    return self.rule_layer.gate_fn(self.rule_layer.lambda_gates)

get_mf_params

Return a serializable description of the model's membership functions.

Source code in highfis/models/_base.py
def get_mf_params(self) -> dict[str, list[dict[str, Any]]]:
    """Return a serializable description of the model's membership functions."""
    return {
        name: [{"type": type(mf).__name__, **mf.inspect_params()} for mf in cast(Sequence[MembershipFunction], mfs)]
        for name, mfs in self.membership_layer.input_mfs.items()
    }

get_rule_gate_values

Return normalized DG-TSK rule gate activations from theta values.

Source code in highfis/models/_dg_tsk.py
def get_rule_gate_values(self) -> Tensor:
    """Return normalized DG-TSK rule gate activations from theta values."""
    return self.consequent_layer.gate_fn(self.consequent_layer.theta_gates)

get_rule_table

Return the rule base as a table of feature-to-MF indices.

Source code in highfis/models/_base.py
def get_rule_table(self) -> list[dict[str, Any]]:
    """Return the rule base as a table of feature-to-MF indices."""
    return [
        dict(
            rule_id=rule_index,
            **dict(zip(self.rule_layer.input_names, rule, strict=False)),
        )
        for rule_index, rule in enumerate(self.rule_layer.rules)
    ]

predict

Return predicted values as a 1-D tensor.

Source code in highfis/models/_common.py
def predict(self, x: Tensor) -> Tensor:
    """Return predicted values as a 1-D tensor."""
    with torch.no_grad():
        if x.dtype == torch.float64:
            was_training = self.training
            self.double()
            try:
                self.eval()
                return self.forward(x).squeeze(1)
            finally:
                self.float()
                self.train(was_training)
        else:
            was_training = self.training
            try:
                self.eval()
                return self.forward(x).squeeze(1)
            finally:
                self.train(was_training)

prune_structure

Structurally prune the model to the given surviving feature and rule indices.

Rebuilds :attr:membership_layer, :attr:rule_layer, and :attr:consequent_layer in-place, retaining only the specified features and rules. All associated parameters are copied from the current layers.

Source code in highfis/models/_dg_tsk.py
def prune_structure(
    self,
    surviving_features: list[int],
    surviving_rules: list[int],
) -> None:
    """Structurally prune the model to the given surviving feature and rule indices.

    Rebuilds :attr:`membership_layer`, :attr:`rule_layer`, and
    :attr:`consequent_layer` in-place, retaining only the specified
    features and rules.  All associated parameters are copied from
    the current layers.
    """
    if not surviving_features:
        raise ValueError("surviving_features must not be empty")
    if not surviving_rules:
        raise ValueError("surviving_rules must not be empty")

    surviving_names = [self.input_names[i] for i in surviving_features]

    new_input_mfs: dict[str, list[MembershipFunction]] = {}
    mf_remap: dict[int, dict[int, int]] = {}
    for orig_fi in surviving_features:
        name = self.input_names[orig_fi]
        all_mfs = list(cast(Any, self.membership_layer.input_mfs[name]))
        used = sorted({self.rule_layer.rules[r][orig_fi] for r in surviving_rules})
        new_input_mfs[name] = [all_mfs[mf_idx] for mf_idx in used]
        mf_remap[orig_fi] = {old: new for new, old in enumerate(used)}

    new_n_features = len(surviving_features)
    new_n_rules = len(surviving_rules)
    new_mf_per_input = [len(new_input_mfs[self.input_names[fi]]) for fi in surviving_features]
    new_rules = [
        tuple(mf_remap[fi][self.rule_layer.rules[r][fi]] for fi in surviving_features) for r in surviving_rules
    ]

    self.membership_layer = MembershipLayer(new_input_mfs)

    old_lambda = self.rule_layer.lambda_gates.data[surviving_features].clone()
    new_rule_layer = DGTSKRuleLayer(
        surviving_names,
        new_mf_per_input,
        rules=new_rules,
        gate_fea=self.gate_fea,
        eps=self.eps,
    )
    new_rule_layer.lambda_gates.data.copy_(old_lambda)
    self.rule_layer = new_rule_layer

    if isinstance(self.consequent_layer, GatedRegressionZeroOrderConsequentLayer):
        old_bias = self.consequent_layer.bias.data[surviving_rules].clone()
        old_theta = self.consequent_layer.theta_gates.data[surviving_rules].clone()
        new_cons: GatedRegressionZeroOrderConsequentLayer | GatedRegressionConsequentLayer
        new_cons = GatedRegressionZeroOrderConsequentLayer(new_n_rules, new_n_features, gate_fn=self.gate_rule)
        new_cons.bias.data.copy_(old_bias)
        new_cons.theta_gates.data.copy_(old_theta)
    else:
        old_layer = cast(GatedRegressionConsequentLayer, self.consequent_layer)
        old_mode = old_layer.mode
        old_theta = old_layer.theta_gates.data[surviving_rules].clone()
        old_bias = old_layer.bias.data[surviving_rules].clone()
        old_weight = old_layer.weight.data[surviving_rules][:, surviving_features].clone()
        old_lam_cons = old_layer.lambda_gates.data[surviving_rules][:, surviving_features].clone()
        new_cons = GatedRegressionConsequentLayer(new_n_rules, new_n_features, gate_fn=self.gate_rule)
        new_cons.mode = old_mode
        new_cons.theta_gates.data.copy_(old_theta)
        new_cons.bias.data.copy_(old_bias)
        new_cons.weight.data.copy_(old_weight)
        new_cons.lambda_gates.data.copy_(old_lam_cons)
    self.consequent_layer = new_cons

    self.input_names = surviving_names
    self.n_inputs = new_n_features
    self.n_rules = new_n_rules
    self.input_mfs = new_input_mfs

search_thresholds

Search DG-TSK regression threshold combinations and optionally apply the best candidate.

Source code in highfis/models/_dg_tsk.py
def search_thresholds(
    self,
    x: Tensor,
    y: Tensor,
    zeta_lambda: Sequence[float] | None = None,
    zeta_theta: Sequence[float] | None = None,
    x_val: Tensor | None = None,
    y_val: Tensor | None = None,
    use_lse: bool = True,
    inplace: bool = True,
    verbose: bool = False,
    structural: bool = True,
) -> dict[str, Any]:
    """Search DG-TSK regression threshold combinations and optionally apply the best candidate."""
    if zeta_lambda is None:
        zeta_lambda = [0.0, 0.25, 0.5, 0.75, 1.0]
    if zeta_theta is None:
        zeta_theta = [0.0, 0.25, 0.5, 0.75, 1.0]

    x_eval = x_val if x_val is not None else x
    y_eval = y_val if y_val is not None else y

    (
        best_score,
        best_state,
        best_tau_lambda,
        best_tau_theta,
        best_zeta_lambda,
        best_zeta_theta,
    ) = _run_threshold_grid_search(self, x, y, x_eval, y_eval, zeta_lambda, zeta_theta, use_lse, verbose)

    result: dict[str, Any] = {
        "best_score": best_score,
        "best_zeta_lambda": best_zeta_lambda,
        "best_zeta_theta": best_zeta_theta,
        "tau_lambda": best_tau_lambda,
        "tau_theta": best_tau_theta,
    }

    result["surviving_feature_indices"] = list(range(self.n_inputs))
    result["surviving_rule_indices"] = list(range(self.n_rules))

    if inplace:
        if structural:
            feature_gate_values = self.get_feature_gate_values()
            rule_gate_values = self.get_rule_gate_values()
            sf = torch.where(feature_gate_values > best_tau_lambda)[0].tolist()
            sr = torch.where(rule_gate_values > best_tau_theta)[0].tolist()
            if not sf:
                sf = list(range(self.n_inputs))
            if not sr:
                sr = list(range(self.n_rules))
        else:
            sf, sr = [], []
        _apply_thresholds_and_pruning_inplace(
            self, x, y, best_tau_lambda, best_tau_theta, best_state, use_lse, structural, sf, sr, result
        )

    return result

DombiTSKClassifierModel

Bases: BaseTSKClassifierModel

TSK classifier with a fixed Dombi T-norm in the antecedent.

DombiTSK extends TSK fuzzy inference by using a Dombi t-norm aggregation in antecedent evaluation while keeping first-order linear consequents.

Reference

G. Xue, L. Hu, J. Wang and S. Ablameyko, "ADMTSK: A High-Dimensional Takagi-Sugeno-Kang Fuzzy System Based on Adaptive Dombi T-Norm," in IEEE Transactions on Fuzzy Systems, vol. 33, no. 6, pp. 1767-1780, June 2025, doi: 10.1109/TFUZZ.2025.3535640.

Initialise the Dombi TSK classifier.

Parameters:

Name Type Description Default
input_mfs Mapping[str, Sequence[MembershipFunction]]

Mapping from feature name to a sequence of :class:~highfis.memberships.MembershipFunction objects.

required
n_classes int

Number of output classes (must be ≥ 2).

required
rule_base str

Rule-base construction strategy. "cartesian" (default, all MF combinations), "coco" (same-index compact), "en" (enhanced FRB), or "custom" (explicit rules via rules).

'cartesian'
t_norm str | TNormFn

T-norm name or callable (default "dombi"). When a string, a DombiTNorm with lambda_ is used.

'dombi'
lambda_ float

Dombi parameter λ > 0. λ = 1 gives the algebraic product. Ignored when t_norm is callable.

1.0
rules Sequence[Sequence[int]] | None

Explicit rule antecedent indices.

None
defuzzifier nn.Module | None

Custom defuzzifier. Defaults to :class:~highfis.defuzzifiers.SumBasedDefuzzifier.

None
consequent_batch_norm bool

Batch normalisation on consequent inputs.

False
zero_consequent_init bool

If True (default), initialize consequent weights and biases to zero.

True

Raises:

Type Description
ValueError

If n_classes < 2 or lambda_ <= 0.

Source code in highfis/models/_dombi.py
def __init__(
    self,
    input_mfs: Mapping[str, Sequence[MembershipFunction]],
    n_classes: int,
    rule_base: str = "cartesian",
    t_norm: str | TNormFn = "dombi",
    lambda_: float = 1.0,
    rules: Sequence[Sequence[int]] | None = None,
    defuzzifier: nn.Module | None = None,
    consequent_batch_norm: bool = False,
    zero_consequent_init: bool = True,
) -> None:
    """Initialise the Dombi TSK classifier.

    Args:
        input_mfs: Mapping from feature name to a sequence of
            :class:`~highfis.memberships.MembershipFunction` objects.
        n_classes: Number of output classes (must be ≥ 2).
        rule_base: Rule-base construction strategy.  ``"cartesian"``
            (default, all MF combinations), ``"coco"`` (same-index
            compact), ``"en"`` (enhanced FRB), or ``"custom"``
            (explicit rules via *rules*).
        t_norm: T-norm name or callable (default ``"dombi"``). When a
            string, a ``DombiTNorm`` with *lambda_* is used.
        lambda_: Dombi parameter ``λ > 0``.  ``λ = 1`` gives the
            algebraic product.  Ignored when *t_norm* is callable.
        rules: Explicit rule antecedent indices.
        defuzzifier: Custom defuzzifier.  Defaults to
            :class:`~highfis.defuzzifiers.SumBasedDefuzzifier`.
        consequent_batch_norm: Batch normalisation on consequent inputs.
        zero_consequent_init: If ``True`` (default), initialize
            consequent weights and biases to zero.

    Raises:
        ValueError: If ``n_classes < 2`` or ``lambda_ <= 0``.
    """
    if n_classes < 2:
        raise ValueError("n_classes must be >= 2")
    if lambda_ <= 0.0:
        raise ValueError("lambda_ must be > 0")

    self.n_classes = int(n_classes)
    self.lambda_ = float(lambda_)
    self.zero_consequent_init = bool(zero_consequent_init)
    if not callable(t_norm):
        t_norm = DombiTNorm(lambda_=self.lambda_)

    super().__init__(
        input_mfs,
        rule_base=rule_base,
        t_norm=t_norm,
        rules=rules,
        defuzzifier=defuzzifier or SumBasedDefuzzifier(),
        consequent_batch_norm=consequent_batch_norm,
    )
    if self.zero_consequent_init:
        self._zero_initialize_consequents()

forward

Full forward pass through the TSK pipeline.

Source code in highfis/models/_base.py
def forward(self, x: Tensor) -> Tensor:
    """Full forward pass through the TSK pipeline."""
    output, _ = self._forward_train(x)
    return output

forward_antecedents

Compute normalized rule strengths from model antecedents.

Source code in highfis/models/_base.py
def forward_antecedents(self, x: Tensor) -> Tensor:
    """Compute normalized rule strengths from model antecedents."""
    mu = self.membership_layer(x)
    w = self.rule_layer(mu)
    return self.defuzzifier(w)

get_consequent_weights

Return the consequent layer weights or None when unavailable.

Source code in highfis/models/_base.py
def get_consequent_weights(self) -> Tensor | None:
    """Return the consequent layer weights or ``None`` when unavailable."""
    weight = getattr(self.consequent_layer, "weight", None)
    if isinstance(weight, Tensor):
        return weight.detach()
    return None

get_mf_params

Return a serializable description of the model's membership functions.

Source code in highfis/models/_base.py
def get_mf_params(self) -> dict[str, list[dict[str, Any]]]:
    """Return a serializable description of the model's membership functions."""
    return {
        name: [{"type": type(mf).__name__, **mf.inspect_params()} for mf in cast(Sequence[MembershipFunction], mfs)]
        for name, mfs in self.membership_layer.input_mfs.items()
    }

get_rule_table

Return the rule base as a table of feature-to-MF indices.

Source code in highfis/models/_base.py
def get_rule_table(self) -> list[dict[str, Any]]:
    """Return the rule base as a table of feature-to-MF indices."""
    return [
        dict(
            rule_id=rule_index,
            **dict(zip(self.rule_layer.input_names, rule, strict=False)),
        )
        for rule_index, rule in enumerate(self.rule_layer.rules)
    ]

predict

Return predicted class indices.

Source code in highfis/models/_common.py
def predict(self, x: Tensor) -> Tensor:
    """Return predicted class indices."""
    with torch.no_grad():
        return torch.argmax(self.predict_proba(x), dim=1)

predict_proba

Return class probabilities computed with softmax.

Source code in highfis/models/_common.py
def predict_proba(self, x: Tensor) -> Tensor:
    """Return class probabilities computed with softmax."""
    with torch.no_grad():
        if x.dtype == torch.float64:
            was_training = self.training
            self.double()
            try:
                self.eval()
                logits = self.forward(x)
                return torch.softmax(logits, dim=1)
            finally:
                self.float()
                self.train(was_training)
        else:
            was_training = self.training
            try:
                self.eval()
                logits = self.forward(x)
                return torch.softmax(logits, dim=1)
            finally:
                self.train(was_training)

DombiTSKRegressorModel

Bases: BaseTSKRegressorModel

TSK regressor with a fixed Dombi T-norm in the antecedent.

DombiTSK extends TSK fuzzy inference by using a Dombi t-norm aggregation in antecedent evaluation while keeping first-order linear consequents.

Reference

G. Xue, L. Hu, J. Wang and S. Ablameyko, "ADMTSK: A High-Dimensional Takagi-Sugeno-Kang Fuzzy System Based on Adaptive Dombi T-Norm," in IEEE Transactions on Fuzzy Systems, vol. 33, no. 6, pp. 1767-1780, June 2025, doi: 10.1109/TFUZZ.2025.3535640.

Initialise the Dombi TSK regressor.

Parameters:

Name Type Description Default
input_mfs Mapping[str, Sequence[MembershipFunction]]

Mapping from feature name to a sequence of :class:~highfis.memberships.MembershipFunction objects.

required
rule_base str

Rule-base construction strategy. "cartesian" (default, all MF combinations), "coco" (same-index compact), "en" (enhanced FRB), or "custom" (explicit rules via rules).

'cartesian'
t_norm str | TNormFn

T-norm name or callable (default "dombi"). When a string, a DombiTNorm with lambda_ is used.

'dombi'
lambda_ float

Dombi parameter λ > 0. Ignored when t_norm is callable.

1.0
rules Sequence[Sequence[int]] | None

Explicit rule antecedent indices.

None
defuzzifier nn.Module | None

Custom defuzzifier. Defaults to :class:~highfis.defuzzifiers.SumBasedDefuzzifier.

None
consequent_batch_norm bool

Batch normalisation on consequent inputs.

False

Raises:

Type Description
ValueError

If lambda_ <= 0.

Source code in highfis/models/_dombi.py
def __init__(
    self,
    input_mfs: Mapping[str, Sequence[MembershipFunction]],
    rule_base: str = "cartesian",
    t_norm: str | TNormFn = "dombi",
    lambda_: float = 1.0,
    rules: Sequence[Sequence[int]] | None = None,
    defuzzifier: nn.Module | None = None,
    consequent_batch_norm: bool = False,
) -> None:
    """Initialise the Dombi TSK regressor.

    Args:
        input_mfs: Mapping from feature name to a sequence of
            :class:`~highfis.memberships.MembershipFunction` objects.
        rule_base: Rule-base construction strategy.  ``"cartesian"``
            (default, all MF combinations), ``"coco"`` (same-index
            compact), ``"en"`` (enhanced FRB), or ``"custom"``
            (explicit rules via *rules*).
        t_norm: T-norm name or callable (default ``"dombi"``). When a
            string, a ``DombiTNorm`` with *lambda_* is used.
        lambda_: Dombi parameter ``λ > 0``.  Ignored when *t_norm* is callable.
        rules: Explicit rule antecedent indices.
        defuzzifier: Custom defuzzifier.  Defaults to
            :class:`~highfis.defuzzifiers.SumBasedDefuzzifier`.
        consequent_batch_norm: Batch normalisation on consequent inputs.

    Raises:
        ValueError: If ``lambda_ <= 0``.
    """
    if lambda_ <= 0.0:
        raise ValueError("lambda_ must be > 0")

    self.lambda_ = float(lambda_)
    if not callable(t_norm):
        t_norm = DombiTNorm(lambda_=self.lambda_)

    super().__init__(
        input_mfs,
        rule_base=rule_base,
        t_norm=t_norm,
        rules=rules,
        defuzzifier=defuzzifier or SumBasedDefuzzifier(),
        consequent_batch_norm=consequent_batch_norm,
    )

forward

Full forward pass through the TSK pipeline.

Source code in highfis/models/_base.py
def forward(self, x: Tensor) -> Tensor:
    """Full forward pass through the TSK pipeline."""
    output, _ = self._forward_train(x)
    return output

forward_antecedents

Compute normalized rule strengths from model antecedents.

Source code in highfis/models/_base.py
def forward_antecedents(self, x: Tensor) -> Tensor:
    """Compute normalized rule strengths from model antecedents."""
    mu = self.membership_layer(x)
    w = self.rule_layer(mu)
    return self.defuzzifier(w)

get_consequent_weights

Return the consequent layer weights or None when unavailable.

Source code in highfis/models/_base.py
def get_consequent_weights(self) -> Tensor | None:
    """Return the consequent layer weights or ``None`` when unavailable."""
    weight = getattr(self.consequent_layer, "weight", None)
    if isinstance(weight, Tensor):
        return weight.detach()
    return None

get_mf_params

Return a serializable description of the model's membership functions.

Source code in highfis/models/_base.py
def get_mf_params(self) -> dict[str, list[dict[str, Any]]]:
    """Return a serializable description of the model's membership functions."""
    return {
        name: [{"type": type(mf).__name__, **mf.inspect_params()} for mf in cast(Sequence[MembershipFunction], mfs)]
        for name, mfs in self.membership_layer.input_mfs.items()
    }

get_rule_table

Return the rule base as a table of feature-to-MF indices.

Source code in highfis/models/_base.py
def get_rule_table(self) -> list[dict[str, Any]]:
    """Return the rule base as a table of feature-to-MF indices."""
    return [
        dict(
            rule_id=rule_index,
            **dict(zip(self.rule_layer.input_names, rule, strict=False)),
        )
        for rule_index, rule in enumerate(self.rule_layer.rules)
    ]

predict

Return predicted values as a 1-D tensor.

Source code in highfis/models/_common.py
def predict(self, x: Tensor) -> Tensor:
    """Return predicted values as a 1-D tensor."""
    with torch.no_grad():
        if x.dtype == torch.float64:
            was_training = self.training
            self.double()
            try:
                self.eval()
                return self.forward(x).squeeze(1)
            finally:
                self.float()
                self.train(was_training)
        else:
            was_training = self.training
            try:
                self.eval()
                return self.forward(x).squeeze(1)
            finally:
                self.train(was_training)

FSREADATSKClassifierModel

Bases: BaseTSKClassifierModel

FSRE-ADATSK classifier with adaptive softmin antecedent and gated consequents.

FSRE-ADATSK (Feature Selection and Rule Extraction) extends ADATSK.

Reference

G. Xue, Q. Chang, J. Wang, K. Zhang and N. R. Pal, "An Adaptive Neuro-Fuzzy System With Integrated Feature Selection and Rule Extraction for High-Dimensional Classification Problems," in IEEE Transactions on Fuzzy Systems, vol. 31, no. 7, pp. 2167-2181, July 2023, doi: 10.1109/TFUZZ.2022.3220950.

Initialise the FSRE-ADATSK classifier.

Parameters:

Name Type Description Default
input_mfs Mapping[str, Sequence[MembershipFunction]]

Mapping from feature name to a sequence of MembershipFunction objects.

required
n_classes int

Number of output classes (must be ≥ 2).

required
rule_base str

Rule-base construction strategy. "coco" (default, same-index compact), "cartesian" (all MF combinations), "en" (enhanced FRB; see also use_en_frb), or "custom" (explicit rules via rules).

'coco'
rules Sequence[Sequence[int]] | None

Explicit rule antecedent indices; ignored when use_en_frb=True.

None
defuzzifier nn.Module | None

Custom defuzzifier. Defaults to SoftmaxLogDefuzzifier.

None
consequent_batch_norm bool

Batch normalisation on consequent inputs.

False
eps float | None

Numerical stability epsilon for the Ada-softmin operator.

None
use_en_frb bool

Start directly from the Enhanced FRB (En-FRB) instead of CoCo-FRB.

False

Raises:

Type Description
ValueError

If n_classes < 2.

Source code in highfis/models/_fsre.py
def __init__(
    self,
    input_mfs: Mapping[str, Sequence[MembershipFunction]],
    n_classes: int,
    rule_base: str = "coco",
    rules: Sequence[Sequence[int]] | None = None,
    defuzzifier: nn.Module | None = None,
    consequent_batch_norm: bool = False,
    eps: float | None = None,
    use_en_frb: bool = False,
) -> None:
    """Initialise the FSRE-ADATSK classifier.

    Args:
        input_mfs: Mapping from feature name to a sequence of
            MembershipFunction objects.
        n_classes: Number of output classes (must be ≥ 2).
        rule_base: Rule-base construction strategy.  ``"coco"``
            (default, same-index compact), ``"cartesian"`` (all MF
            combinations), ``"en"`` (enhanced FRB; see also
            *use_en_frb*), or ``"custom"`` (explicit rules via *rules*).
        rules: Explicit rule antecedent indices; ignored when
            ``use_en_frb=True``.
        defuzzifier: Custom defuzzifier. Defaults to
            SoftmaxLogDefuzzifier.
        consequent_batch_norm: Batch normalisation on consequent inputs.
        eps: Numerical stability epsilon for the Ada-softmin operator.
        use_en_frb: Start directly from the Enhanced FRB (En-FRB)
            instead of CoCo-FRB.

    Raises:
        ValueError: If ``n_classes < 2``.
    """
    if n_classes < 2:
        raise ValueError("n_classes must be >= 2")

    self.n_classes = int(n_classes)
    self.eps = eps
    self.use_en_frb = bool(use_en_frb)

    super().__init__(
        input_mfs,
        rule_base=rule_base,
        t_norm="prod",
        rules=rules,
        defuzzifier=defuzzifier or SoftmaxLogDefuzzifier(),
        consequent_batch_norm=consequent_batch_norm,
    )

    self.rule_layer = AdaSoftminRuleLayer(
        self.input_names,
        [len(input_mfs[name]) for name in self.input_names],
        rules=rules if not self.use_en_frb else None,
        rule_base="en" if self.use_en_frb else rule_base,
        eps=self.eps,
    )
    self.n_rules = self.rule_layer.n_rules
    self.consequent_layer = self._build_consequent_layer()

expand_to_en_frb

Switch the rule layer to an Enhanced Fuzzy Rule Base for RE phase.

Source code in highfis/models/_fsre.py
def expand_to_en_frb(self) -> None:
    """Switch the rule layer to an Enhanced Fuzzy Rule Base for RE phase."""
    self.rule_layer = AdaSoftminRuleLayer(
        self.input_names,
        [len(self.input_mfs[name]) for name in self.input_names],
        rule_base="en",
        eps=self.eps,
    )
    self.n_rules = self.rule_layer.n_rules
    self.consequent_layer = self._build_consequent_layer()

forward

Full forward pass through the TSK pipeline.

Source code in highfis/models/_base.py
def forward(self, x: Tensor) -> Tensor:
    """Full forward pass through the TSK pipeline."""
    output, _ = self._forward_train(x)
    return output

forward_antecedents

Compute normalized rule strengths from model antecedents.

Source code in highfis/models/_base.py
def forward_antecedents(self, x: Tensor) -> Tensor:
    """Compute normalized rule strengths from model antecedents."""
    mu = self.membership_layer(x)
    w = self.rule_layer(mu)
    return self.defuzzifier(w)

get_consequent_weights

Return the consequent layer weights or None when unavailable.

Source code in highfis/models/_base.py
def get_consequent_weights(self) -> Tensor | None:
    """Return the consequent layer weights or ``None`` when unavailable."""
    weight = getattr(self.consequent_layer, "weight", None)
    if isinstance(weight, Tensor):
        return weight.detach()
    return None

get_feature_gate_values

Return M(λ_d) gate activations for all input features.

Returns:

Type Description
Tensor

Detached tensor of shape (n_inputs,) with the gate

Tensor

activation value for each feature after the FS phase.

Source code in highfis/models/_fsre.py
def get_feature_gate_values(self) -> Tensor:
    """Return M(λ_d) gate activations for all input features.

    Returns:
        Detached tensor of shape ``(n_inputs,)`` with the gate
        activation value for each feature after the FS phase.
    """
    return self.consequent_layer.gate_fn(self.consequent_layer.lambda_gates).detach()

get_mf_params

Return a serializable description of the model's membership functions.

Source code in highfis/models/_base.py
def get_mf_params(self) -> dict[str, list[dict[str, Any]]]:
    """Return a serializable description of the model's membership functions."""
    return {
        name: [{"type": type(mf).__name__, **mf.inspect_params()} for mf in cast(Sequence[MembershipFunction], mfs)]
        for name, mfs in self.membership_layer.input_mfs.items()
    }

get_rule_gate_values

Return M(θ_r) gate activations for all rules.

Returns:

Type Description
Tensor

Detached tensor of shape (n_rules,) with the gate

Tensor

activation value for each rule after the RE phase.

Source code in highfis/models/_fsre.py
def get_rule_gate_values(self) -> Tensor:
    """Return M(θ_r) gate activations for all rules.

    Returns:
        Detached tensor of shape ``(n_rules,)`` with the gate
        activation value for each rule after the RE phase.
    """
    return self.consequent_layer.gate_fn(self.consequent_layer.theta_gates).detach()

get_rule_table

Return the rule base as a table of feature-to-MF indices.

Source code in highfis/models/_base.py
def get_rule_table(self) -> list[dict[str, Any]]:
    """Return the rule base as a table of feature-to-MF indices."""
    return [
        dict(
            rule_id=rule_index,
            **dict(zip(self.rule_layer.input_names, rule, strict=False)),
        )
        for rule_index, rule in enumerate(self.rule_layer.rules)
    ]

predict

Return predicted class indices.

Source code in highfis/models/_common.py
def predict(self, x: Tensor) -> Tensor:
    """Return predicted class indices."""
    with torch.no_grad():
        return torch.argmax(self.predict_proba(x), dim=1)

predict_proba

Return class probabilities computed with softmax.

Source code in highfis/models/_common.py
def predict_proba(self, x: Tensor) -> Tensor:
    """Return class probabilities computed with softmax."""
    with torch.no_grad():
        if x.dtype == torch.float64:
            was_training = self.training
            self.double()
            try:
                self.eval()
                logits = self.forward(x)
                return torch.softmax(logits, dim=1)
            finally:
                self.float()
                self.train(was_training)
        else:
            was_training = self.training
            try:
                self.eval()
                logits = self.forward(x)
                return torch.softmax(logits, dim=1)
            finally:
                self.train(was_training)

prune_to_features

Structurally prune the model to the given feature subset (paper step 2).

Updates input_names, input_mfs, n_inputs, membership_layer, and optionally consequent_bn in-place. The rule layer and consequent layer are intentionally left unchanged here; they will be rebuilt from the updated feature set when fit_re() calls expand_to_en_frb().

Parameters:

Name Type Description Default
surviving_features list[int]

Indices of features to retain.

required

Raises:

Type Description
ValueError

If surviving_features is empty.

Source code in highfis/models/_fsre.py
def prune_to_features(self, surviving_features: list[int]) -> None:
    """Structurally prune the model to the given feature subset (paper step 2).

    Updates input_names, input_mfs, n_inputs, membership_layer, and
    optionally consequent_bn in-place.
    The rule layer and consequent layer are intentionally left unchanged
    here; they will be rebuilt from the updated feature set when
    fit_re() calls expand_to_en_frb().

    Args:
        surviving_features: Indices of features to retain.

    Raises:
        ValueError: If *surviving_features* is empty.
    """
    if not surviving_features:
        raise ValueError("surviving_features must not be empty")
    surviving_names = [self.input_names[i] for i in surviving_features]
    new_input_mfs = {name: self.input_mfs[name] for name in surviving_names}
    self.input_names = surviving_names
    self.input_mfs = new_input_mfs
    self.n_inputs = len(surviving_features)
    self.membership_layer = MembershipLayer(new_input_mfs)
    if self.consequent_batch_norm:
        self.consequent_bn = nn.BatchNorm1d(self.n_inputs)

prune_to_rules

Structurally prune the model to the given rule subset (paper step 4).

Rebuilds rule_layer and consequent_layer in-place, retaining only the specified rules. Consequent weights, bias, and gate parameters for the surviving rules are copied from the current layers. The new consequent layer is set to mode="finetune" ready for phase 3.

Parameters:

Name Type Description Default
surviving_rules list[int]

Indices of rules to retain.

required

Raises:

Type Description
ValueError

If surviving_rules is empty.

Source code in highfis/models/_fsre.py
def prune_to_rules(self, surviving_rules: list[int]) -> None:
    """Structurally prune the model to the given rule subset (paper step 4).

    Rebuilds rule_layer and consequent_layer in-place,
    retaining only the specified rules.  Consequent weights, bias, and
    gate parameters for the surviving rules are copied from the current
    layers.  The new consequent layer is set to ``mode="finetune"``
    ready for phase 3.

    Args:
        surviving_rules: Indices of rules to retain.

    Raises:
        ValueError: If *surviving_rules* is empty.
    """
    if not surviving_rules:
        raise ValueError("surviving_rules must not be empty")
    mf_per_input = [len(self.input_mfs[name]) for name in self.input_names]
    new_rules = [self.rule_layer.rules[r] for r in surviving_rules]
    new_n_rules = len(surviving_rules)

    self.rule_layer = AdaSoftminRuleLayer(
        self.input_names,
        mf_per_input,
        rules=new_rules,
        eps=self.eps,
    )
    self.n_rules = new_n_rules

    old_cons = self.consequent_layer
    new_cons = GatedClassificationConsequentLayer(new_n_rules, self.n_inputs, self.n_classes, shared_lambda=True)
    new_cons.mode = "finetune"
    cast(Tensor, new_cons.theta_gates.data).copy_(old_cons.theta_gates.data[surviving_rules])
    cast(Tensor, new_cons.lambda_gates.data).copy_(old_cons.lambda_gates.data)
    cast(Tensor, new_cons.weight.data).copy_(old_cons.weight.data[surviving_rules])
    cast(Tensor, new_cons.bias.data).copy_(old_cons.bias.data[surviving_rules])
    self.consequent_layer = new_cons

set_consequent_mode

Set training mode for the consequent layer.

Source code in highfis/models/_fsre.py
def set_consequent_mode(self, mode: str) -> None:
    """Set training mode for the consequent layer."""
    self.consequent_layer.mode = mode

FSREADATSKRegressorModel

Bases: BaseTSKRegressorModel

FSRE-ADATSK regressor with adaptive softmin antecedent and gated consequents.

FSRE-ADATSK (Feature Selection and Rule Extraction) extends ADATSK.

Reference

G. Xue, Q. Chang, J. Wang, K. Zhang and N. R. Pal, "An Adaptive Neuro-Fuzzy System With Integrated Feature Selection and Rule Extraction for High-Dimensional Classification Problems," in IEEE Transactions on Fuzzy Systems, vol. 31, no. 7, pp. 2167-2181, July 2023, doi: 10.1109/TFUZZ.2022.3220950.

Initialise the FSRE-ADATSK regressor.

Parameters:

Name Type Description Default
input_mfs Mapping[str, Sequence[MembershipFunction]]

Mapping from feature name to a sequence of MembershipFunction objects.

required
rule_base str

Rule-base construction strategy. "coco" (default, same-index compact), "cartesian" (all MF combinations), "en" (enhanced FRB; see also use_en_frb), or "custom" (explicit rules via rules).

'coco'
rules Sequence[Sequence[int]] | None

Explicit rule antecedent indices; ignored when use_en_frb=True.

None
defuzzifier nn.Module | None

Custom defuzzifier. Defaults to SoftmaxLogDefuzzifier.

None
consequent_batch_norm bool

Batch normalisation on consequent inputs.

False
eps float | None

Numerical stability epsilon for the Ada-softmin operator.

None
use_en_frb bool

Start directly from the Enhanced FRB (En-FRB).

False
Source code in highfis/models/_fsre.py
def __init__(
    self,
    input_mfs: Mapping[str, Sequence[MembershipFunction]],
    rule_base: str = "coco",
    rules: Sequence[Sequence[int]] | None = None,
    defuzzifier: nn.Module | None = None,
    consequent_batch_norm: bool = False,
    eps: float | None = None,
    use_en_frb: bool = False,
) -> None:
    """Initialise the FSRE-ADATSK regressor.

    Args:
        input_mfs: Mapping from feature name to a sequence of
            MembershipFunction objects.
        rule_base: Rule-base construction strategy.  ``"coco"``
            (default, same-index compact), ``"cartesian"`` (all MF
            combinations), ``"en"`` (enhanced FRB; see also
            *use_en_frb*), or ``"custom"`` (explicit rules via *rules*).
        rules: Explicit rule antecedent indices; ignored when
            ``use_en_frb=True``.
        defuzzifier: Custom defuzzifier. Defaults to
            SoftmaxLogDefuzzifier.
        consequent_batch_norm: Batch normalisation on consequent inputs.
        eps: Numerical stability epsilon for the Ada-softmin operator.
        use_en_frb: Start directly from the Enhanced FRB (En-FRB).
    """
    self.eps = eps
    self.use_en_frb = bool(use_en_frb)

    super().__init__(
        input_mfs,
        rule_base=rule_base,
        t_norm="prod",
        rules=rules,
        defuzzifier=defuzzifier or SoftmaxLogDefuzzifier(),
        consequent_batch_norm=consequent_batch_norm,
    )

    self.rule_layer = AdaSoftminRuleLayer(
        self.input_names,
        [len(input_mfs[name]) for name in self.input_names],
        rules=rules if not self.use_en_frb else None,
        rule_base="en" if self.use_en_frb else rule_base,
        eps=self.eps,
    )
    self.n_rules = self.rule_layer.n_rules
    self.consequent_layer = self._build_consequent_layer()

expand_to_en_frb

Switch the rule layer to an Enhanced Fuzzy Rule Base for RE phase.

Source code in highfis/models/_fsre.py
def expand_to_en_frb(self) -> None:
    """Switch the rule layer to an Enhanced Fuzzy Rule Base for RE phase."""
    self.rule_layer = AdaSoftminRuleLayer(
        self.input_names,
        [len(self.input_mfs[name]) for name in self.input_names],
        rule_base="en",
        eps=self.eps,
    )
    self.n_rules = self.rule_layer.n_rules
    self.consequent_layer = self._build_consequent_layer()

forward

Full forward pass through the TSK pipeline.

Source code in highfis/models/_base.py
def forward(self, x: Tensor) -> Tensor:
    """Full forward pass through the TSK pipeline."""
    output, _ = self._forward_train(x)
    return output

forward_antecedents

Compute normalized rule strengths from model antecedents.

Source code in highfis/models/_base.py
def forward_antecedents(self, x: Tensor) -> Tensor:
    """Compute normalized rule strengths from model antecedents."""
    mu = self.membership_layer(x)
    w = self.rule_layer(mu)
    return self.defuzzifier(w)

get_consequent_weights

Return the consequent layer weights or None when unavailable.

Source code in highfis/models/_base.py
def get_consequent_weights(self) -> Tensor | None:
    """Return the consequent layer weights or ``None`` when unavailable."""
    weight = getattr(self.consequent_layer, "weight", None)
    if isinstance(weight, Tensor):
        return weight.detach()
    return None

get_feature_gate_values

Return M(λ_d) gate activations for all input features.

Returns:

Type Description
Tensor

Detached tensor of shape (n_inputs,) with the gate

Tensor

activation value for each feature after the FS phase.

Source code in highfis/models/_fsre.py
def get_feature_gate_values(self) -> Tensor:
    """Return M(λ_d) gate activations for all input features.

    Returns:
        Detached tensor of shape ``(n_inputs,)`` with the gate
        activation value for each feature after the FS phase.
    """
    return self.consequent_layer.gate_fn(self.consequent_layer.lambda_gates).detach()

get_mf_params

Return a serializable description of the model's membership functions.

Source code in highfis/models/_base.py
def get_mf_params(self) -> dict[str, list[dict[str, Any]]]:
    """Return a serializable description of the model's membership functions."""
    return {
        name: [{"type": type(mf).__name__, **mf.inspect_params()} for mf in cast(Sequence[MembershipFunction], mfs)]
        for name, mfs in self.membership_layer.input_mfs.items()
    }

get_rule_gate_values

Return M(θ_r) gate activations for all rules.

Returns:

Type Description
Tensor

Detached tensor of shape (n_rules,) with the gate

Tensor

activation value for each rule after the RE phase.

Source code in highfis/models/_fsre.py
def get_rule_gate_values(self) -> Tensor:
    """Return M(θ_r) gate activations for all rules.

    Returns:
        Detached tensor of shape ``(n_rules,)`` with the gate
        activation value for each rule after the RE phase.
    """
    return self.consequent_layer.gate_fn(self.consequent_layer.theta_gates).detach()

get_rule_table

Return the rule base as a table of feature-to-MF indices.

Source code in highfis/models/_base.py
def get_rule_table(self) -> list[dict[str, Any]]:
    """Return the rule base as a table of feature-to-MF indices."""
    return [
        dict(
            rule_id=rule_index,
            **dict(zip(self.rule_layer.input_names, rule, strict=False)),
        )
        for rule_index, rule in enumerate(self.rule_layer.rules)
    ]

predict

Return predicted values as a 1-D tensor.

Source code in highfis/models/_common.py
def predict(self, x: Tensor) -> Tensor:
    """Return predicted values as a 1-D tensor."""
    with torch.no_grad():
        if x.dtype == torch.float64:
            was_training = self.training
            self.double()
            try:
                self.eval()
                return self.forward(x).squeeze(1)
            finally:
                self.float()
                self.train(was_training)
        else:
            was_training = self.training
            try:
                self.eval()
                return self.forward(x).squeeze(1)
            finally:
                self.train(was_training)

prune_to_features

Structurally prune the model to the given feature subset (paper step 2).

Updates input_names, input_mfs, n_inputs, membership_layer, and optionally consequent_bn in-place. The rule layer and consequent layer are intentionally left unchanged here; they will be rebuilt from the updated feature set when fit_re() calls expand_to_en_frb().

Parameters:

Name Type Description Default
surviving_features list[int]

Indices of features to retain.

required

Raises:

Type Description
ValueError

If surviving_features is empty.

Source code in highfis/models/_fsre.py
def prune_to_features(self, surviving_features: list[int]) -> None:
    """Structurally prune the model to the given feature subset (paper step 2).

    Updates input_names, input_mfs, n_inputs, membership_layer, and
    optionally consequent_bn in-place.
    The rule layer and consequent layer are intentionally left unchanged
    here; they will be rebuilt from the updated feature set when
    fit_re() calls expand_to_en_frb().

    Args:
        surviving_features: Indices of features to retain.

    Raises:
        ValueError: If *surviving_features* is empty.
    """
    if not surviving_features:
        raise ValueError("surviving_features must not be empty")
    surviving_names = [self.input_names[i] for i in surviving_features]
    new_input_mfs = {name: self.input_mfs[name] for name in surviving_names}
    self.input_names = surviving_names
    self.input_mfs = new_input_mfs
    self.n_inputs = len(surviving_features)
    self.membership_layer = MembershipLayer(new_input_mfs)
    if self.consequent_batch_norm:
        self.consequent_bn = nn.BatchNorm1d(self.n_inputs)

prune_to_rules

Structurally prune the model to the given rule subset (paper step 4).

Rebuilds rule_layer and consequent_layer in-place, retaining only the specified rules. Consequent weights, bias, and gate parameters for the surviving rules are copied from the current layers. The new consequent layer is set to mode="finetune" ready for phase 3.

Parameters:

Name Type Description Default
surviving_rules list[int]

Indices of rules to retain.

required

Raises:

Type Description
ValueError

If surviving_rules is empty.

Source code in highfis/models/_fsre.py
def prune_to_rules(self, surviving_rules: list[int]) -> None:
    """Structurally prune the model to the given rule subset (paper step 4).

    Rebuilds rule_layer and consequent_layer in-place,
    retaining only the specified rules.  Consequent weights, bias, and
    gate parameters for the surviving rules are copied from the current
    layers.  The new consequent layer is set to ``mode="finetune"``
    ready for phase 3.

    Args:
        surviving_rules: Indices of rules to retain.

    Raises:
        ValueError: If *surviving_rules* is empty.
    """
    if not surviving_rules:
        raise ValueError("surviving_rules must not be empty")
    mf_per_input = [len(self.input_mfs[name]) for name in self.input_names]
    new_rules = [self.rule_layer.rules[r] for r in surviving_rules]
    new_n_rules = len(surviving_rules)

    self.rule_layer = AdaSoftminRuleLayer(
        self.input_names,
        mf_per_input,
        rules=new_rules,
        eps=self.eps,
    )
    self.n_rules = new_n_rules

    old_cons = self.consequent_layer
    new_cons = GatedRegressionConsequentLayer(new_n_rules, self.n_inputs, shared_lambda=True)
    new_cons.mode = "finetune"
    cast(Tensor, new_cons.theta_gates.data).copy_(old_cons.theta_gates.data[surviving_rules])
    cast(Tensor, new_cons.lambda_gates.data).copy_(old_cons.lambda_gates.data)
    cast(Tensor, new_cons.weight.data).copy_(old_cons.weight.data[surviving_rules])
    cast(Tensor, new_cons.bias.data).copy_(old_cons.bias.data[surviving_rules])
    self.consequent_layer = new_cons

set_consequent_mode

Set training mode for the consequent layer.

Source code in highfis/models/_fsre.py
def set_consequent_mode(self, mode: str) -> None:
    """Set training mode for the consequent layer."""
    self.consequent_layer.mode = mode

HDFISMinClassifierModel

Bases: BaseTSKClassifierModel

HDFIS-min classifier with frozen antecedents and minimum aggregation.

HDFIS-min uses the minimum T-norm in the antecedent and only optimizes consequent parameters, which avoids the nondifferentiability of the minimum operator during training.

References

G. Xue, J. Wang, K. Zhang and N. R. Pal, "High-Dimensional Fuzzy Inference Systems," in IEEE Transactions on Systems, Man, and Cybernetics: Systems, vol. 54, no. 1, pp. 507-519, Jan. 2024, doi: 10.1109/TSMC.2023.3311475.

Initialize the HDFIS-min classifier.

Source code in highfis/models/_hdfis.py
def __init__(
    self,
    input_mfs: Mapping[str, Sequence[MembershipFunction]],
    n_classes: int,
    rule_base: str = "cartesian",
    t_norm: str | TNormFn = "min",
    rules: Sequence[Sequence[int]] | None = None,
    defuzzifier: nn.Module | None = None,
    consequent_batch_norm: bool = False,
    zero_consequent_init: bool = False,
) -> None:
    """Initialize the HDFIS-min classifier."""
    if n_classes < 2:
        raise ValueError("n_classes must be >= 2")
    self.n_classes = int(n_classes)
    super().__init__(
        input_mfs,
        rule_base=rule_base,
        t_norm=t_norm,
        rules=rules,
        defuzzifier=defuzzifier or SumBasedDefuzzifier(),
        consequent_batch_norm=consequent_batch_norm,
    )
    if zero_consequent_init:
        _zero_initialize_consequents(self.consequent_layer)
    for param in self.membership_layer.parameters():
        param.requires_grad = False

forward

Full forward pass through the TSK pipeline.

Source code in highfis/models/_base.py
def forward(self, x: Tensor) -> Tensor:
    """Full forward pass through the TSK pipeline."""
    output, _ = self._forward_train(x)
    return output

forward_antecedents

Compute normalized rule strengths from model antecedents.

Source code in highfis/models/_base.py
def forward_antecedents(self, x: Tensor) -> Tensor:
    """Compute normalized rule strengths from model antecedents."""
    mu = self.membership_layer(x)
    w = self.rule_layer(mu)
    return self.defuzzifier(w)

get_consequent_weights

Return the consequent layer weights or None when unavailable.

Source code in highfis/models/_base.py
def get_consequent_weights(self) -> Tensor | None:
    """Return the consequent layer weights or ``None`` when unavailable."""
    weight = getattr(self.consequent_layer, "weight", None)
    if isinstance(weight, Tensor):
        return weight.detach()
    return None

get_mf_params

Return a serializable description of the model's membership functions.

Source code in highfis/models/_base.py
def get_mf_params(self) -> dict[str, list[dict[str, Any]]]:
    """Return a serializable description of the model's membership functions."""
    return {
        name: [{"type": type(mf).__name__, **mf.inspect_params()} for mf in cast(Sequence[MembershipFunction], mfs)]
        for name, mfs in self.membership_layer.input_mfs.items()
    }

get_rule_table

Return the rule base as a table of feature-to-MF indices.

Source code in highfis/models/_base.py
def get_rule_table(self) -> list[dict[str, Any]]:
    """Return the rule base as a table of feature-to-MF indices."""
    return [
        dict(
            rule_id=rule_index,
            **dict(zip(self.rule_layer.input_names, rule, strict=False)),
        )
        for rule_index, rule in enumerate(self.rule_layer.rules)
    ]

predict

Return predicted class indices.

Source code in highfis/models/_common.py
def predict(self, x: Tensor) -> Tensor:
    """Return predicted class indices."""
    with torch.no_grad():
        return torch.argmax(self.predict_proba(x), dim=1)

predict_proba

Return class probabilities computed with softmax.

Source code in highfis/models/_common.py
def predict_proba(self, x: Tensor) -> Tensor:
    """Return class probabilities computed with softmax."""
    with torch.no_grad():
        if x.dtype == torch.float64:
            was_training = self.training
            self.double()
            try:
                self.eval()
                logits = self.forward(x)
                return torch.softmax(logits, dim=1)
            finally:
                self.float()
                self.train(was_training)
        else:
            was_training = self.training
            try:
                self.eval()
                logits = self.forward(x)
                return torch.softmax(logits, dim=1)
            finally:
                self.train(was_training)

HDFISMinRegressorModel

Bases: BaseTSKRegressorModel

HDFIS-min regressor with frozen antecedents and minimum aggregation.

HDFIS-min uses the minimum T-norm in the antecedent and only optimizes consequent parameters, which avoids the nondifferentiability of the minimum operator during training.

References

G. Xue, J. Wang, K. Zhang and N. R. Pal, "High-Dimensional Fuzzy Inference Systems," in IEEE Transactions on Systems, Man, and Cybernetics: Systems, vol. 54, no. 1, pp. 507-519, Jan. 2024, doi: 10.1109/TSMC.2023.3311475.

Initialize the HDFIS-min regressor.

Source code in highfis/models/_hdfis.py
def __init__(
    self,
    input_mfs: Mapping[str, Sequence[MembershipFunction]],
    rule_base: str = "cartesian",
    t_norm: str | TNormFn = "min",
    rules: Sequence[Sequence[int]] | None = None,
    defuzzifier: nn.Module | None = None,
    consequent_batch_norm: bool = False,
    zero_consequent_init: bool = False,
) -> None:
    """Initialize the HDFIS-min regressor."""
    super().__init__(
        input_mfs,
        rule_base=rule_base,
        t_norm=t_norm,
        rules=rules,
        defuzzifier=defuzzifier or SumBasedDefuzzifier(),
        consequent_batch_norm=consequent_batch_norm,
    )
    if zero_consequent_init:
        _zero_initialize_consequents(self.consequent_layer)
    for param in self.membership_layer.parameters():
        param.requires_grad = False

forward

Full forward pass through the TSK pipeline.

Source code in highfis/models/_base.py
def forward(self, x: Tensor) -> Tensor:
    """Full forward pass through the TSK pipeline."""
    output, _ = self._forward_train(x)
    return output

forward_antecedents

Compute normalized rule strengths from model antecedents.

Source code in highfis/models/_base.py
def forward_antecedents(self, x: Tensor) -> Tensor:
    """Compute normalized rule strengths from model antecedents."""
    mu = self.membership_layer(x)
    w = self.rule_layer(mu)
    return self.defuzzifier(w)

get_consequent_weights

Return the consequent layer weights or None when unavailable.

Source code in highfis/models/_base.py
def get_consequent_weights(self) -> Tensor | None:
    """Return the consequent layer weights or ``None`` when unavailable."""
    weight = getattr(self.consequent_layer, "weight", None)
    if isinstance(weight, Tensor):
        return weight.detach()
    return None

get_mf_params

Return a serializable description of the model's membership functions.

Source code in highfis/models/_base.py
def get_mf_params(self) -> dict[str, list[dict[str, Any]]]:
    """Return a serializable description of the model's membership functions."""
    return {
        name: [{"type": type(mf).__name__, **mf.inspect_params()} for mf in cast(Sequence[MembershipFunction], mfs)]
        for name, mfs in self.membership_layer.input_mfs.items()
    }

get_rule_table

Return the rule base as a table of feature-to-MF indices.

Source code in highfis/models/_base.py
def get_rule_table(self) -> list[dict[str, Any]]:
    """Return the rule base as a table of feature-to-MF indices."""
    return [
        dict(
            rule_id=rule_index,
            **dict(zip(self.rule_layer.input_names, rule, strict=False)),
        )
        for rule_index, rule in enumerate(self.rule_layer.rules)
    ]

predict

Return predicted values as a 1-D tensor.

Source code in highfis/models/_common.py
def predict(self, x: Tensor) -> Tensor:
    """Return predicted values as a 1-D tensor."""
    with torch.no_grad():
        if x.dtype == torch.float64:
            was_training = self.training
            self.double()
            try:
                self.eval()
                return self.forward(x).squeeze(1)
            finally:
                self.float()
                self.train(was_training)
        else:
            was_training = self.training
            try:
                self.eval()
                return self.forward(x).squeeze(1)
            finally:
                self.train(was_training)

HDFISProdClassifierModel

Bases: BaseTSKClassifierModel

HDFIS-prod classifier with dimension-dependent Gaussian MFs.

HDFIS-prod combines the standard product T-norm with a dimension-dependent Gaussian membership function (DMF) to avoid numeric underflow in very high-dimensional feature spaces while preserving first-order TSK consequents.

References

G. Xue, J. Wang, K. Zhang and N. R. Pal, "High-Dimensional Fuzzy Inference Systems," in IEEE Transactions on Systems, Man, and Cybernetics: Systems, vol. 54, no. 1, pp. 507-519, Jan. 2024, doi: 10.1109/TSMC.2023.3311475.

Initialize the HDFIS-prod classifier.

Source code in highfis/models/_hdfis.py
def __init__(
    self,
    input_mfs: Mapping[str, Sequence[MembershipFunction]],
    n_classes: int,
    rule_base: str = "cartesian",
    t_norm: str | TNormFn = "prod",
    rules: Sequence[Sequence[int]] | None = None,
    defuzzifier: nn.Module | None = None,
    consequent_batch_norm: bool = False,
    zero_consequent_init: bool = False,
) -> None:
    """Initialize the HDFIS-prod classifier."""
    if n_classes < 2:
        raise ValueError("n_classes must be >= 2")
    self.n_classes = int(n_classes)
    super().__init__(
        input_mfs,
        rule_base=rule_base,
        t_norm=t_norm,
        rules=rules,
        defuzzifier=defuzzifier or SumBasedDefuzzifier(),
        consequent_batch_norm=consequent_batch_norm,
    )
    if zero_consequent_init:
        _zero_initialize_consequents(self.consequent_layer)

forward

Full forward pass through the TSK pipeline.

Source code in highfis/models/_base.py
def forward(self, x: Tensor) -> Tensor:
    """Full forward pass through the TSK pipeline."""
    output, _ = self._forward_train(x)
    return output

forward_antecedents

Compute normalized rule strengths from model antecedents.

Source code in highfis/models/_base.py
def forward_antecedents(self, x: Tensor) -> Tensor:
    """Compute normalized rule strengths from model antecedents."""
    mu = self.membership_layer(x)
    w = self.rule_layer(mu)
    return self.defuzzifier(w)

get_consequent_weights

Return the consequent layer weights or None when unavailable.

Source code in highfis/models/_base.py
def get_consequent_weights(self) -> Tensor | None:
    """Return the consequent layer weights or ``None`` when unavailable."""
    weight = getattr(self.consequent_layer, "weight", None)
    if isinstance(weight, Tensor):
        return weight.detach()
    return None

get_mf_params

Return a serializable description of the model's membership functions.

Source code in highfis/models/_base.py
def get_mf_params(self) -> dict[str, list[dict[str, Any]]]:
    """Return a serializable description of the model's membership functions."""
    return {
        name: [{"type": type(mf).__name__, **mf.inspect_params()} for mf in cast(Sequence[MembershipFunction], mfs)]
        for name, mfs in self.membership_layer.input_mfs.items()
    }

get_rule_table

Return the rule base as a table of feature-to-MF indices.

Source code in highfis/models/_base.py
def get_rule_table(self) -> list[dict[str, Any]]:
    """Return the rule base as a table of feature-to-MF indices."""
    return [
        dict(
            rule_id=rule_index,
            **dict(zip(self.rule_layer.input_names, rule, strict=False)),
        )
        for rule_index, rule in enumerate(self.rule_layer.rules)
    ]

predict

Return predicted class indices.

Source code in highfis/models/_common.py
def predict(self, x: Tensor) -> Tensor:
    """Return predicted class indices."""
    with torch.no_grad():
        return torch.argmax(self.predict_proba(x), dim=1)

predict_proba

Return class probabilities computed with softmax.

Source code in highfis/models/_common.py
def predict_proba(self, x: Tensor) -> Tensor:
    """Return class probabilities computed with softmax."""
    with torch.no_grad():
        if x.dtype == torch.float64:
            was_training = self.training
            self.double()
            try:
                self.eval()
                logits = self.forward(x)
                return torch.softmax(logits, dim=1)
            finally:
                self.float()
                self.train(was_training)
        else:
            was_training = self.training
            try:
                self.eval()
                logits = self.forward(x)
                return torch.softmax(logits, dim=1)
            finally:
                self.train(was_training)

HDFISProdRegressorModel

Bases: BaseTSKRegressorModel

HDFIS-prod regressor with dimension-dependent Gaussian MFs.

HDFIS-prod combines the standard product T-norm with a dimension-dependent Gaussian membership function (DMF) to avoid numeric underflow in very high-dimensional feature spaces while preserving first-order TSK consequents.

References

G. Xue, J. Wang, K. Zhang and N. R. Pal, "High-Dimensional Fuzzy Inference Systems," in IEEE Transactions on Systems, Man, and Cybernetics: Systems, vol. 54, no. 1, pp. 507-519, Jan. 2024, doi: 10.1109/TSMC.2023.3311475.

Initialize the HDFIS-prod regressor.

Source code in highfis/models/_hdfis.py
def __init__(
    self,
    input_mfs: Mapping[str, Sequence[MembershipFunction]],
    rule_base: str = "cartesian",
    t_norm: str | TNormFn = "prod",
    rules: Sequence[Sequence[int]] | None = None,
    defuzzifier: nn.Module | None = None,
    consequent_batch_norm: bool = False,
    zero_consequent_init: bool = False,
) -> None:
    """Initialize the HDFIS-prod regressor."""
    super().__init__(
        input_mfs,
        rule_base=rule_base,
        t_norm=t_norm,
        rules=rules,
        defuzzifier=defuzzifier or SumBasedDefuzzifier(),
        consequent_batch_norm=consequent_batch_norm,
    )
    if zero_consequent_init:
        _zero_initialize_consequents(self.consequent_layer)

forward

Full forward pass through the TSK pipeline.

Source code in highfis/models/_base.py
def forward(self, x: Tensor) -> Tensor:
    """Full forward pass through the TSK pipeline."""
    output, _ = self._forward_train(x)
    return output

forward_antecedents

Compute normalized rule strengths from model antecedents.

Source code in highfis/models/_base.py
def forward_antecedents(self, x: Tensor) -> Tensor:
    """Compute normalized rule strengths from model antecedents."""
    mu = self.membership_layer(x)
    w = self.rule_layer(mu)
    return self.defuzzifier(w)

get_consequent_weights

Return the consequent layer weights or None when unavailable.

Source code in highfis/models/_base.py
def get_consequent_weights(self) -> Tensor | None:
    """Return the consequent layer weights or ``None`` when unavailable."""
    weight = getattr(self.consequent_layer, "weight", None)
    if isinstance(weight, Tensor):
        return weight.detach()
    return None

get_mf_params

Return a serializable description of the model's membership functions.

Source code in highfis/models/_base.py
def get_mf_params(self) -> dict[str, list[dict[str, Any]]]:
    """Return a serializable description of the model's membership functions."""
    return {
        name: [{"type": type(mf).__name__, **mf.inspect_params()} for mf in cast(Sequence[MembershipFunction], mfs)]
        for name, mfs in self.membership_layer.input_mfs.items()
    }

get_rule_table

Return the rule base as a table of feature-to-MF indices.

Source code in highfis/models/_base.py
def get_rule_table(self) -> list[dict[str, Any]]:
    """Return the rule base as a table of feature-to-MF indices."""
    return [
        dict(
            rule_id=rule_index,
            **dict(zip(self.rule_layer.input_names, rule, strict=False)),
        )
        for rule_index, rule in enumerate(self.rule_layer.rules)
    ]

predict

Return predicted values as a 1-D tensor.

Source code in highfis/models/_common.py
def predict(self, x: Tensor) -> Tensor:
    """Return predicted values as a 1-D tensor."""
    with torch.no_grad():
        if x.dtype == torch.float64:
            was_training = self.training
            self.double()
            try:
                self.eval()
                return self.forward(x).squeeze(1)
            finally:
                self.float()
                self.train(was_training)
        else:
            was_training = self.training
            try:
                self.eval()
                return self.forward(x).squeeze(1)
            finally:
                self.train(was_training)

HTSKClassifierModel

Bases: BaseTSKClassifierModel

HTSK classifier for high-dimensional TSK inference.

HTSK replaces the standard product t-norm with a geometric mean over membership values and performs rule normalization in log-space.

References

Y. Cui, D. Wu and Y. Xu, "Curse of Dimensionality for TSK Fuzzy Neural Networks: Explanation and Solutions," 2021 International Joint Conference on Neural Networks (IJCNN), Shenzhen, China, 2021, pp. 1-8, doi: 10.1109/IJCNN52387.2021.9534265.

Initialise the HTSK classifier.

Parameters:

Name Type Description Default
input_mfs Mapping[str, Sequence[MembershipFunction]]

Mapping from feature name to a sequence of :class:~highfis.memberships.MembershipFunction objects.

required
n_classes int

Number of output classes (must be ≥ 2).

required
rule_base str

Rule-base construction strategy. "cartesian" (default, all MF combinations), "coco" (same-index compact), "en" (enhanced FRB), or "custom" (explicit rules via rules).

'cartesian'
t_norm str | TNormFn

Antecedent aggregation operator name or callable (default "gmean" for HTSK).

'gmean'
rules Sequence[Sequence[int]] | None

Explicit rule antecedent indices. If None, rules are inferred from rule_base.

None
defuzzifier nn.Module | None

Custom defuzzifier module. Defaults to :class:~highfis.defuzzifiers.SoftmaxLogDefuzzifier.

None
consequent_batch_norm bool

Apply batch normalisation to the consequent layer inputs.

False

Raises:

Type Description
ValueError

If n_classes < 2.

Source code in highfis/models/_htsk.py
def __init__(
    self,
    input_mfs: Mapping[str, Sequence[MembershipFunction]],
    n_classes: int,
    rule_base: str = "cartesian",
    t_norm: str | TNormFn = "gmean",
    rules: Sequence[Sequence[int]] | None = None,
    defuzzifier: nn.Module | None = None,
    consequent_batch_norm: bool = False,
) -> None:
    """Initialise the HTSK classifier.

    Args:
        input_mfs: Mapping from feature name to a sequence of
            :class:`~highfis.memberships.MembershipFunction` objects.
        n_classes: Number of output classes (must be ≥ 2).
        rule_base: Rule-base construction strategy.  ``"cartesian"``
            (default, all MF combinations), ``"coco"`` (same-index
            compact), ``"en"`` (enhanced FRB), or ``"custom"``
            (explicit rules via *rules*).
        t_norm: Antecedent aggregation operator name or callable
            (default ``"gmean"`` for HTSK).
        rules: Explicit rule antecedent indices.  If ``None``, rules
            are inferred from ``rule_base``.
        defuzzifier: Custom defuzzifier module.  Defaults to
            :class:`~highfis.defuzzifiers.SoftmaxLogDefuzzifier`.
        consequent_batch_norm: Apply batch normalisation to the
            consequent layer inputs.

    Raises:
        ValueError: If ``n_classes < 2``.
    """
    if n_classes < 2:
        raise ValueError("n_classes must be >= 2")
    self.n_classes = int(n_classes)
    super().__init__(
        input_mfs,
        rule_base=rule_base,
        t_norm=t_norm,
        rules=rules,
        defuzzifier=defuzzifier,
        consequent_batch_norm=consequent_batch_norm,
    )

forward

Full forward pass through the TSK pipeline.

Source code in highfis/models/_base.py
def forward(self, x: Tensor) -> Tensor:
    """Full forward pass through the TSK pipeline."""
    output, _ = self._forward_train(x)
    return output

forward_antecedents

Compute normalized rule strengths from model antecedents.

Source code in highfis/models/_base.py
def forward_antecedents(self, x: Tensor) -> Tensor:
    """Compute normalized rule strengths from model antecedents."""
    mu = self.membership_layer(x)
    w = self.rule_layer(mu)
    return self.defuzzifier(w)

get_consequent_weights

Return the consequent layer weights or None when unavailable.

Source code in highfis/models/_base.py
def get_consequent_weights(self) -> Tensor | None:
    """Return the consequent layer weights or ``None`` when unavailable."""
    weight = getattr(self.consequent_layer, "weight", None)
    if isinstance(weight, Tensor):
        return weight.detach()
    return None

get_mf_params

Return a serializable description of the model's membership functions.

Source code in highfis/models/_base.py
def get_mf_params(self) -> dict[str, list[dict[str, Any]]]:
    """Return a serializable description of the model's membership functions."""
    return {
        name: [{"type": type(mf).__name__, **mf.inspect_params()} for mf in cast(Sequence[MembershipFunction], mfs)]
        for name, mfs in self.membership_layer.input_mfs.items()
    }

get_rule_table

Return the rule base as a table of feature-to-MF indices.

Source code in highfis/models/_base.py
def get_rule_table(self) -> list[dict[str, Any]]:
    """Return the rule base as a table of feature-to-MF indices."""
    return [
        dict(
            rule_id=rule_index,
            **dict(zip(self.rule_layer.input_names, rule, strict=False)),
        )
        for rule_index, rule in enumerate(self.rule_layer.rules)
    ]

predict

Return predicted class indices.

Source code in highfis/models/_common.py
def predict(self, x: Tensor) -> Tensor:
    """Return predicted class indices."""
    with torch.no_grad():
        return torch.argmax(self.predict_proba(x), dim=1)

predict_proba

Return class probabilities computed with softmax.

Source code in highfis/models/_common.py
def predict_proba(self, x: Tensor) -> Tensor:
    """Return class probabilities computed with softmax."""
    with torch.no_grad():
        if x.dtype == torch.float64:
            was_training = self.training
            self.double()
            try:
                self.eval()
                logits = self.forward(x)
                return torch.softmax(logits, dim=1)
            finally:
                self.float()
                self.train(was_training)
        else:
            was_training = self.training
            try:
                self.eval()
                logits = self.forward(x)
                return torch.softmax(logits, dim=1)
            finally:
                self.train(was_training)

HTSKRegressorModel

Bases: BaseTSKRegressorModel

HTSK regressor for high-dimensional TSK inference.

HTSK replaces the standard product t-norm with a geometric mean over membership values and performs rule normalization in log-space.

References

Y. Cui, D. Wu and Y. Xu, "Curse of Dimensionality for TSK Fuzzy Neural Networks: Explanation and Solutions," 2021 International Joint Conference on Neural Networks (IJCNN), Shenzhen, China, 2021, pp. 1-8, doi: 10.1109/IJCNN52387.2021.9534265.

Initialise the HTSK regressor.

Parameters:

Name Type Description Default
input_mfs Mapping[str, Sequence[MembershipFunction]]

Mapping from feature name to a sequence of :class:~highfis.memberships.MembershipFunction objects.

required
rule_base str

Rule-base construction strategy. "cartesian" (default, all MF combinations), "coco" (same-index compact), "en" (enhanced FRB), or "custom" (explicit rules via rules).

'cartesian'
t_norm str | TNormFn

Antecedent aggregation operator (default "gmean").

'gmean'
rules Sequence[Sequence[int]] | None

Explicit rule antecedent indices.

None
defuzzifier nn.Module | None

Custom defuzzifier. Defaults to :class:~highfis.defuzzifiers.SoftmaxLogDefuzzifier.

None
consequent_batch_norm bool

Batch normalisation on consequent inputs.

False
Source code in highfis/models/_htsk.py
def __init__(
    self,
    input_mfs: Mapping[str, Sequence[MembershipFunction]],
    rule_base: str = "cartesian",
    t_norm: str | TNormFn = "gmean",
    rules: Sequence[Sequence[int]] | None = None,
    defuzzifier: nn.Module | None = None,
    consequent_batch_norm: bool = False,
) -> None:
    """Initialise the HTSK regressor.

    Args:
        input_mfs: Mapping from feature name to a sequence of
            :class:`~highfis.memberships.MembershipFunction` objects.
        rule_base: Rule-base construction strategy.  ``"cartesian"``
            (default, all MF combinations), ``"coco"`` (same-index
            compact), ``"en"`` (enhanced FRB), or ``"custom"``
            (explicit rules via *rules*).
        t_norm: Antecedent aggregation operator (default ``"gmean"``).
        rules: Explicit rule antecedent indices.
        defuzzifier: Custom defuzzifier.  Defaults to
            :class:`~highfis.defuzzifiers.SoftmaxLogDefuzzifier`.
        consequent_batch_norm: Batch normalisation on consequent inputs.
    """
    super().__init__(
        input_mfs,
        rule_base=rule_base,
        t_norm=t_norm,
        rules=rules,
        defuzzifier=defuzzifier,
        consequent_batch_norm=consequent_batch_norm,
    )

forward

Full forward pass through the TSK pipeline.

Source code in highfis/models/_base.py
def forward(self, x: Tensor) -> Tensor:
    """Full forward pass through the TSK pipeline."""
    output, _ = self._forward_train(x)
    return output

forward_antecedents

Compute normalized rule strengths from model antecedents.

Source code in highfis/models/_base.py
def forward_antecedents(self, x: Tensor) -> Tensor:
    """Compute normalized rule strengths from model antecedents."""
    mu = self.membership_layer(x)
    w = self.rule_layer(mu)
    return self.defuzzifier(w)

get_consequent_weights

Return the consequent layer weights or None when unavailable.

Source code in highfis/models/_base.py
def get_consequent_weights(self) -> Tensor | None:
    """Return the consequent layer weights or ``None`` when unavailable."""
    weight = getattr(self.consequent_layer, "weight", None)
    if isinstance(weight, Tensor):
        return weight.detach()
    return None

get_mf_params

Return a serializable description of the model's membership functions.

Source code in highfis/models/_base.py
def get_mf_params(self) -> dict[str, list[dict[str, Any]]]:
    """Return a serializable description of the model's membership functions."""
    return {
        name: [{"type": type(mf).__name__, **mf.inspect_params()} for mf in cast(Sequence[MembershipFunction], mfs)]
        for name, mfs in self.membership_layer.input_mfs.items()
    }

get_rule_table

Return the rule base as a table of feature-to-MF indices.

Source code in highfis/models/_base.py
def get_rule_table(self) -> list[dict[str, Any]]:
    """Return the rule base as a table of feature-to-MF indices."""
    return [
        dict(
            rule_id=rule_index,
            **dict(zip(self.rule_layer.input_names, rule, strict=False)),
        )
        for rule_index, rule in enumerate(self.rule_layer.rules)
    ]

predict

Return predicted values as a 1-D tensor.

Source code in highfis/models/_common.py
def predict(self, x: Tensor) -> Tensor:
    """Return predicted values as a 1-D tensor."""
    with torch.no_grad():
        if x.dtype == torch.float64:
            was_training = self.training
            self.double()
            try:
                self.eval()
                return self.forward(x).squeeze(1)
            finally:
                self.float()
                self.train(was_training)
        else:
            was_training = self.training
            try:
                self.eval()
                return self.forward(x).squeeze(1)
            finally:
                self.train(was_training)

LogTSKClassifierModel

Bases: BaseTSKClassifierModel

LogTSK classifier with inverse-log normalization of log-domain rules.

Firing strengths are normalized using the inverse-log formula, which is immune to softmax saturation in high-dimensional input spaces.

References

Y. Cui, D. Wu and Y. Xu, "Curse of Dimensionality for TSK Fuzzy Neural Networks: Explanation and Solutions," 2021 International Joint Conference on Neural Networks (IJCNN), Shenzhen, China, 2021, pp. 1-8, doi: 10.1109/IJCNN52387.2021.9534265.

Initialise the LogTSK classifier.

Parameters:

Name Type Description Default
input_mfs Mapping[str, Sequence[MembershipFunction]]

Mapping from feature name to a sequence of :class:~highfis.memberships.MembershipFunction objects.

required
n_classes int

Number of output classes (must be ≥ 2).

required
rule_base str

Rule-base construction strategy. "cartesian" (default, all MF combinations), "coco" (same-index compact), "en" (enhanced FRB), or "custom" (explicit rules via rules).

'cartesian'
t_norm str | TNormFn

Antecedent aggregation operator (default "gmean"). The geometric-mean t-norm avoids float32 underflow in high-dimensional inputs while preserving the correct L1-normalised inverse-log weights (the dimension factor D cancels in normalisation).

'gmean'
rules Sequence[Sequence[int]] | None

Explicit rule antecedent indices.

None
defuzzifier nn.Module | None

Custom defuzzifier. Defaults to :class:~highfis.defuzzifiers.InvLogDefuzzifier.

None
consequent_batch_norm bool

Batch normalisation on consequent inputs.

False

Raises:

Type Description
ValueError

If n_classes < 2.

Source code in highfis/models/_logtsk.py
def __init__(
    self,
    input_mfs: Mapping[str, Sequence[MembershipFunction]],
    n_classes: int,
    rule_base: str = "cartesian",
    t_norm: str | TNormFn = "gmean",
    rules: Sequence[Sequence[int]] | None = None,
    defuzzifier: nn.Module | None = None,
    consequent_batch_norm: bool = False,
) -> None:
    """Initialise the LogTSK classifier.

    Args:
        input_mfs: Mapping from feature name to a sequence of
            :class:`~highfis.memberships.MembershipFunction` objects.
        n_classes: Number of output classes (must be ≥ 2).
        rule_base: Rule-base construction strategy.  ``"cartesian"``
            (default, all MF combinations), ``"coco"`` (same-index
            compact), ``"en"`` (enhanced FRB), or ``"custom"``
            (explicit rules via *rules*).
        t_norm: Antecedent aggregation operator (default ``"gmean"``).  The
            geometric-mean t-norm avoids float32 underflow in high-dimensional
            inputs while preserving the correct L1-normalised inverse-log
            weights (the dimension factor D cancels in normalisation).
        rules: Explicit rule antecedent indices.
        defuzzifier: Custom defuzzifier.  Defaults to
            :class:`~highfis.defuzzifiers.InvLogDefuzzifier`.
        consequent_batch_norm: Batch normalisation on consequent inputs.

    Raises:
        ValueError: If ``n_classes < 2``.
    """
    if n_classes < 2:
        raise ValueError("n_classes must be >= 2")
    self.n_classes = int(n_classes)
    super().__init__(
        input_mfs,
        rule_base=rule_base,
        t_norm=t_norm,
        rules=rules,
        defuzzifier=defuzzifier or InvLogDefuzzifier(),
        consequent_batch_norm=consequent_batch_norm,
    )

forward

Full forward pass through the TSK pipeline.

Source code in highfis/models/_base.py
def forward(self, x: Tensor) -> Tensor:
    """Full forward pass through the TSK pipeline."""
    output, _ = self._forward_train(x)
    return output

forward_antecedents

Compute normalized rule strengths from model antecedents.

Source code in highfis/models/_base.py
def forward_antecedents(self, x: Tensor) -> Tensor:
    """Compute normalized rule strengths from model antecedents."""
    mu = self.membership_layer(x)
    w = self.rule_layer(mu)
    return self.defuzzifier(w)

get_consequent_weights

Return the consequent layer weights or None when unavailable.

Source code in highfis/models/_base.py
def get_consequent_weights(self) -> Tensor | None:
    """Return the consequent layer weights or ``None`` when unavailable."""
    weight = getattr(self.consequent_layer, "weight", None)
    if isinstance(weight, Tensor):
        return weight.detach()
    return None

get_mf_params

Return a serializable description of the model's membership functions.

Source code in highfis/models/_base.py
def get_mf_params(self) -> dict[str, list[dict[str, Any]]]:
    """Return a serializable description of the model's membership functions."""
    return {
        name: [{"type": type(mf).__name__, **mf.inspect_params()} for mf in cast(Sequence[MembershipFunction], mfs)]
        for name, mfs in self.membership_layer.input_mfs.items()
    }

get_rule_table

Return the rule base as a table of feature-to-MF indices.

Source code in highfis/models/_base.py
def get_rule_table(self) -> list[dict[str, Any]]:
    """Return the rule base as a table of feature-to-MF indices."""
    return [
        dict(
            rule_id=rule_index,
            **dict(zip(self.rule_layer.input_names, rule, strict=False)),
        )
        for rule_index, rule in enumerate(self.rule_layer.rules)
    ]

predict

Return predicted class indices.

Source code in highfis/models/_common.py
def predict(self, x: Tensor) -> Tensor:
    """Return predicted class indices."""
    with torch.no_grad():
        return torch.argmax(self.predict_proba(x), dim=1)

predict_proba

Return class probabilities computed with softmax.

Source code in highfis/models/_common.py
def predict_proba(self, x: Tensor) -> Tensor:
    """Return class probabilities computed with softmax."""
    with torch.no_grad():
        if x.dtype == torch.float64:
            was_training = self.training
            self.double()
            try:
                self.eval()
                logits = self.forward(x)
                return torch.softmax(logits, dim=1)
            finally:
                self.float()
                self.train(was_training)
        else:
            was_training = self.training
            try:
                self.eval()
                logits = self.forward(x)
                return torch.softmax(logits, dim=1)
            finally:
                self.train(was_training)

LogTSKRegressorModel

Bases: BaseTSKRegressorModel

LogTSK regressor with inverse-log normalization of log-domain rules.

Firing strengths are normalized using the inverse-log formula, which is immune to softmax saturation in high-dimensional input spaces.

References

Y. Cui, D. Wu and Y. Xu, "Curse of Dimensionality for TSK Fuzzy Neural Networks: Explanation and Solutions," 2021 International Joint Conference on Neural Networks (IJCNN), Shenzhen, China, 2021, pp. 1-8, doi: 10.1109/IJCNN52387.2021.9534265.

Initialise the LogTSK regressor.

Parameters:

Name Type Description Default
input_mfs Mapping[str, Sequence[MembershipFunction]]

Mapping from feature name to a sequence of :class:~highfis.memberships.MembershipFunction objects.

required
rule_base str

Rule-base construction strategy. "cartesian" (default, all MF combinations), "coco" (same-index compact), "en" (enhanced FRB), or "custom" (explicit rules via rules).

'cartesian'
t_norm str | TNormFn

Antecedent aggregation operator (default "gmean"). The geometric-mean t-norm avoids float32 underflow in high-dimensional inputs while preserving correct LogTSK weights after normalisation.

'gmean'
rules Sequence[Sequence[int]] | None

Explicit rule antecedent indices.

None
defuzzifier nn.Module | None

Custom defuzzifier. Defaults to :class:~highfis.defuzzifiers.InvLogDefuzzifier.

None
consequent_batch_norm bool

Batch normalisation on consequent inputs.

False
Source code in highfis/models/_logtsk.py
def __init__(
    self,
    input_mfs: Mapping[str, Sequence[MembershipFunction]],
    rule_base: str = "cartesian",
    t_norm: str | TNormFn = "gmean",
    rules: Sequence[Sequence[int]] | None = None,
    defuzzifier: nn.Module | None = None,
    consequent_batch_norm: bool = False,
) -> None:
    """Initialise the LogTSK regressor.

    Args:
        input_mfs: Mapping from feature name to a sequence of
            :class:`~highfis.memberships.MembershipFunction` objects.
        rule_base: Rule-base construction strategy.  ``"cartesian"``
            (default, all MF combinations), ``"coco"`` (same-index
            compact), ``"en"`` (enhanced FRB), or ``"custom"``
            (explicit rules via *rules*).
        t_norm: Antecedent aggregation operator (default ``"gmean"``).  The
            geometric-mean t-norm avoids float32 underflow in high-dimensional
            inputs while preserving correct LogTSK weights after normalisation.
        rules: Explicit rule antecedent indices.
        defuzzifier: Custom defuzzifier.  Defaults to
            :class:`~highfis.defuzzifiers.InvLogDefuzzifier`.
        consequent_batch_norm: Batch normalisation on consequent inputs.
    """
    super().__init__(
        input_mfs,
        rule_base=rule_base,
        t_norm=t_norm,
        rules=rules,
        defuzzifier=defuzzifier or InvLogDefuzzifier(),
        consequent_batch_norm=consequent_batch_norm,
    )

forward

Full forward pass through the TSK pipeline.

Source code in highfis/models/_base.py
def forward(self, x: Tensor) -> Tensor:
    """Full forward pass through the TSK pipeline."""
    output, _ = self._forward_train(x)
    return output

forward_antecedents

Compute normalized rule strengths from model antecedents.

Source code in highfis/models/_base.py
def forward_antecedents(self, x: Tensor) -> Tensor:
    """Compute normalized rule strengths from model antecedents."""
    mu = self.membership_layer(x)
    w = self.rule_layer(mu)
    return self.defuzzifier(w)

get_consequent_weights

Return the consequent layer weights or None when unavailable.

Source code in highfis/models/_base.py
def get_consequent_weights(self) -> Tensor | None:
    """Return the consequent layer weights or ``None`` when unavailable."""
    weight = getattr(self.consequent_layer, "weight", None)
    if isinstance(weight, Tensor):
        return weight.detach()
    return None

get_mf_params

Return a serializable description of the model's membership functions.

Source code in highfis/models/_base.py
def get_mf_params(self) -> dict[str, list[dict[str, Any]]]:
    """Return a serializable description of the model's membership functions."""
    return {
        name: [{"type": type(mf).__name__, **mf.inspect_params()} for mf in cast(Sequence[MembershipFunction], mfs)]
        for name, mfs in self.membership_layer.input_mfs.items()
    }

get_rule_table

Return the rule base as a table of feature-to-MF indices.

Source code in highfis/models/_base.py
def get_rule_table(self) -> list[dict[str, Any]]:
    """Return the rule base as a table of feature-to-MF indices."""
    return [
        dict(
            rule_id=rule_index,
            **dict(zip(self.rule_layer.input_names, rule, strict=False)),
        )
        for rule_index, rule in enumerate(self.rule_layer.rules)
    ]

predict

Return predicted values as a 1-D tensor.

Source code in highfis/models/_common.py
def predict(self, x: Tensor) -> Tensor:
    """Return predicted values as a 1-D tensor."""
    with torch.no_grad():
        if x.dtype == torch.float64:
            was_training = self.training
            self.double()
            try:
                self.eval()
                return self.forward(x).squeeze(1)
            finally:
                self.float()
                self.train(was_training)
        else:
            was_training = self.training
            try:
                self.eval()
                return self.forward(x).squeeze(1)
            finally:
                self.train(was_training)

MHTSKClassifierModel

Bases: BaseTSKClassifierModel

Multihead TSK classifier with sparse rule consequents.

MHTSK builds multiple sparse subantecedents from random feature subsets and jointly optimizes their rule consequents.

Reference

Z. Bian, Q. Chang, J. Wang and N. R. Pal, "Multihead Takagi-Sugeno-Kang Fuzzy System," in IEEE Transactions on Fuzzy Systems, vol. 33, no. 8, pp. 2561-2573, Aug. 2025, doi: 10.1109/TFUZZ.2025.3569227.

Initialize the MHTSK classifier.

Parameters:

Name Type Description Default
input_mfs Mapping[str, Sequence[MembershipFunction]]

Mapping from feature name to a sequence of membership functions.

required
rule_feature_mask Tensor

Boolean tensor of shape (n_rules, n_inputs) indicating which features are active for each rule.

required
rules Sequence[Sequence[int]]

Explicit per-rule MF index sequences for all inputs.

required
n_classes int

Number of output classes.

required
t_norm str | TNormFn

Antecedent aggregation operator (default "prod").

'prod'
defuzzifier nn.Module | None

Custom defuzzifier. Defaults to SumBasedDefuzzifier.

None
consequent_batch_norm bool

Batch normalisation on consequent inputs.

False
Source code in highfis/models/_mhtsk.py
def __init__(
    self,
    input_mfs: Mapping[str, Sequence[MembershipFunction]],
    rule_feature_mask: Tensor,
    rules: Sequence[Sequence[int]],
    n_classes: int,
    t_norm: str | TNormFn = "prod",
    defuzzifier: nn.Module | None = None,
    consequent_batch_norm: bool = False,
) -> None:
    """Initialize the MHTSK classifier.

    Args:
        input_mfs: Mapping from feature name to a sequence of membership functions.
        rule_feature_mask: Boolean tensor of shape (n_rules, n_inputs) indicating
            which features are active for each rule.
        rules: Explicit per-rule MF index sequences for all inputs.
        n_classes: Number of output classes.
        t_norm: Antecedent aggregation operator (default ``"prod"``).
        defuzzifier: Custom defuzzifier. Defaults to ``SumBasedDefuzzifier``.
        consequent_batch_norm: Batch normalisation on consequent inputs.
    """
    if n_classes < 2:
        raise ValueError("n_classes must be >= 2")
    self.n_classes = int(n_classes)
    self.rule_feature_mask = rule_feature_mask
    super().__init__(
        input_mfs,
        rule_base="custom",
        t_norm=t_norm,
        rules=rules,
        defuzzifier=defuzzifier or SumBasedDefuzzifier(),
        consequent_batch_norm=consequent_batch_norm,
    )

forward

Full forward pass through the TSK pipeline.

Source code in highfis/models/_base.py
def forward(self, x: Tensor) -> Tensor:
    """Full forward pass through the TSK pipeline."""
    output, _ = self._forward_train(x)
    return output

forward_antecedents

Compute normalized rule strengths from model antecedents.

Source code in highfis/models/_base.py
def forward_antecedents(self, x: Tensor) -> Tensor:
    """Compute normalized rule strengths from model antecedents."""
    mu = self.membership_layer(x)
    w = self.rule_layer(mu)
    return self.defuzzifier(w)

get_consequent_weights

Return the consequent layer weights or None when unavailable.

Source code in highfis/models/_base.py
def get_consequent_weights(self) -> Tensor | None:
    """Return the consequent layer weights or ``None`` when unavailable."""
    weight = getattr(self.consequent_layer, "weight", None)
    if isinstance(weight, Tensor):
        return weight.detach()
    return None

get_mf_params

Return a serializable description of the model's membership functions.

Source code in highfis/models/_base.py
def get_mf_params(self) -> dict[str, list[dict[str, Any]]]:
    """Return a serializable description of the model's membership functions."""
    return {
        name: [{"type": type(mf).__name__, **mf.inspect_params()} for mf in cast(Sequence[MembershipFunction], mfs)]
        for name, mfs in self.membership_layer.input_mfs.items()
    }

get_rule_table

Return the rule base as a table of feature-to-MF indices.

Source code in highfis/models/_base.py
def get_rule_table(self) -> list[dict[str, Any]]:
    """Return the rule base as a table of feature-to-MF indices."""
    return [
        dict(
            rule_id=rule_index,
            **dict(zip(self.rule_layer.input_names, rule, strict=False)),
        )
        for rule_index, rule in enumerate(self.rule_layer.rules)
    ]

predict

Return predicted class indices.

Source code in highfis/models/_common.py
def predict(self, x: Tensor) -> Tensor:
    """Return predicted class indices."""
    with torch.no_grad():
        return torch.argmax(self.predict_proba(x), dim=1)

predict_proba

Return class probabilities computed with softmax.

Source code in highfis/models/_common.py
def predict_proba(self, x: Tensor) -> Tensor:
    """Return class probabilities computed with softmax."""
    with torch.no_grad():
        if x.dtype == torch.float64:
            was_training = self.training
            self.double()
            try:
                self.eval()
                logits = self.forward(x)
                return torch.softmax(logits, dim=1)
            finally:
                self.float()
                self.train(was_training)
        else:
            was_training = self.training
            try:
                self.eval()
                logits = self.forward(x)
                return torch.softmax(logits, dim=1)
            finally:
                self.train(was_training)

MHTSKRegressorModel

Bases: BaseTSKRegressorModel

Multihead TSK regressor with sparse rule consequents.

MHTSK builds multiple sparse subantecedents from random feature subsets and jointly optimizes their rule consequents.

Reference

Z. Bian, Q. Chang, J. Wang and N. R. Pal, "Multihead Takagi-Sugeno-Kang Fuzzy System," in IEEE Transactions on Fuzzy Systems, vol. 33, no. 8, pp. 2561-2573, Aug. 2025, doi: 10.1109/TFUZZ.2025.3569227.

Initialize the MHTSK regressor.

Source code in highfis/models/_mhtsk.py
def __init__(
    self,
    input_mfs: Mapping[str, Sequence[MembershipFunction]],
    rule_feature_mask: Tensor,
    rules: Sequence[Sequence[int]],
    t_norm: str | TNormFn = "prod",
    defuzzifier: nn.Module | None = None,
    consequent_batch_norm: bool = False,
) -> None:
    """Initialize the MHTSK regressor."""
    self.rule_feature_mask = rule_feature_mask
    super().__init__(
        input_mfs,
        rule_base="custom",
        t_norm=t_norm,
        rules=rules,
        defuzzifier=defuzzifier or SumBasedDefuzzifier(),
        consequent_batch_norm=consequent_batch_norm,
    )

forward

Full forward pass through the TSK pipeline.

Source code in highfis/models/_base.py
def forward(self, x: Tensor) -> Tensor:
    """Full forward pass through the TSK pipeline."""
    output, _ = self._forward_train(x)
    return output

forward_antecedents

Compute normalized rule strengths from model antecedents.

Source code in highfis/models/_base.py
def forward_antecedents(self, x: Tensor) -> Tensor:
    """Compute normalized rule strengths from model antecedents."""
    mu = self.membership_layer(x)
    w = self.rule_layer(mu)
    return self.defuzzifier(w)

get_consequent_weights

Return the consequent layer weights or None when unavailable.

Source code in highfis/models/_base.py
def get_consequent_weights(self) -> Tensor | None:
    """Return the consequent layer weights or ``None`` when unavailable."""
    weight = getattr(self.consequent_layer, "weight", None)
    if isinstance(weight, Tensor):
        return weight.detach()
    return None

get_mf_params

Return a serializable description of the model's membership functions.

Source code in highfis/models/_base.py
def get_mf_params(self) -> dict[str, list[dict[str, Any]]]:
    """Return a serializable description of the model's membership functions."""
    return {
        name: [{"type": type(mf).__name__, **mf.inspect_params()} for mf in cast(Sequence[MembershipFunction], mfs)]
        for name, mfs in self.membership_layer.input_mfs.items()
    }

get_rule_table

Return the rule base as a table of feature-to-MF indices.

Source code in highfis/models/_base.py
def get_rule_table(self) -> list[dict[str, Any]]:
    """Return the rule base as a table of feature-to-MF indices."""
    return [
        dict(
            rule_id=rule_index,
            **dict(zip(self.rule_layer.input_names, rule, strict=False)),
        )
        for rule_index, rule in enumerate(self.rule_layer.rules)
    ]

predict

Return predicted values as a 1-D tensor.

Source code in highfis/models/_common.py
def predict(self, x: Tensor) -> Tensor:
    """Return predicted values as a 1-D tensor."""
    with torch.no_grad():
        if x.dtype == torch.float64:
            was_training = self.training
            self.double()
            try:
                self.eval()
                return self.forward(x).squeeze(1)
            finally:
                self.float()
                self.train(was_training)
        else:
            was_training = self.training
            try:
                self.eval()
                return self.forward(x).squeeze(1)
            finally:
                self.train(was_training)

TSKClassifierModel

Bases: BaseTSKClassifierModel

Vanilla TSK classifier with sum-based rule normalization.

The vanilla Takagi-Sugeno-Kang inference computes rule firing strengths with the product t-norm and normalizes them by their total sum.

References

T. Takagi and M. Sugeno, "Fuzzy identification of systems and its applications to modeling and control," in IEEE Transactions on Systems, Man, and Cybernetics, vol. SMC-15, no. 1, pp. 116-132, Jan.-Feb. 1985, doi: 10.1109/TSMC.1985.6313399.

Initialise the vanilla TSK classifier.

Parameters:

Name Type Description Default
input_mfs Mapping[str, Sequence[MembershipFunction]]

Mapping from feature name to a sequence of :class:~highfis.memberships.MembershipFunction objects.

required
n_classes int

Number of output classes (must be ≥ 2).

required
rule_base str

Rule-base construction strategy. "cartesian" (default, all MF combinations), "coco" (same-index compact), "en" (enhanced FRB), or "custom" (explicit rules via rules).

'cartesian'
t_norm str | TNormFn

Antecedent aggregation operator (default "prod").

'prod'
rules Sequence[Sequence[int]] | None

Explicit rule antecedent indices.

None
defuzzifier nn.Module | None

Custom defuzzifier. Defaults to :class:~highfis.defuzzifiers.SumBasedDefuzzifier.

None
consequent_batch_norm bool

Batch normalisation on consequent inputs.

False

Raises:

Type Description
ValueError

If n_classes < 2.

Source code in highfis/models/_htsk.py
def __init__(
    self,
    input_mfs: Mapping[str, Sequence[MembershipFunction]],
    n_classes: int,
    rule_base: str = "cartesian",
    t_norm: str | TNormFn = "prod",
    rules: Sequence[Sequence[int]] | None = None,
    defuzzifier: nn.Module | None = None,
    consequent_batch_norm: bool = False,
) -> None:
    """Initialise the vanilla TSK classifier.

    Args:
        input_mfs: Mapping from feature name to a sequence of
            :class:`~highfis.memberships.MembershipFunction` objects.
        n_classes: Number of output classes (must be ≥ 2).
        rule_base: Rule-base construction strategy.  ``"cartesian"``
            (default, all MF combinations), ``"coco"`` (same-index
            compact), ``"en"`` (enhanced FRB), or ``"custom"``
            (explicit rules via *rules*).
        t_norm: Antecedent aggregation operator (default ``"prod"``).
        rules: Explicit rule antecedent indices.
        defuzzifier: Custom defuzzifier.  Defaults to
            :class:`~highfis.defuzzifiers.SumBasedDefuzzifier`.
        consequent_batch_norm: Batch normalisation on consequent inputs.

    Raises:
        ValueError: If ``n_classes < 2``.
    """
    if n_classes < 2:
        raise ValueError("n_classes must be >= 2")
    self.n_classes = int(n_classes)
    super().__init__(
        input_mfs,
        rule_base=rule_base,
        t_norm=t_norm,
        rules=rules,
        defuzzifier=defuzzifier or SumBasedDefuzzifier(),
        consequent_batch_norm=consequent_batch_norm,
    )

forward

Full forward pass through the TSK pipeline.

Source code in highfis/models/_base.py
def forward(self, x: Tensor) -> Tensor:
    """Full forward pass through the TSK pipeline."""
    output, _ = self._forward_train(x)
    return output

forward_antecedents

Compute normalized rule strengths from model antecedents.

Source code in highfis/models/_base.py
def forward_antecedents(self, x: Tensor) -> Tensor:
    """Compute normalized rule strengths from model antecedents."""
    mu = self.membership_layer(x)
    w = self.rule_layer(mu)
    return self.defuzzifier(w)

get_consequent_weights

Return the consequent layer weights or None when unavailable.

Source code in highfis/models/_base.py
def get_consequent_weights(self) -> Tensor | None:
    """Return the consequent layer weights or ``None`` when unavailable."""
    weight = getattr(self.consequent_layer, "weight", None)
    if isinstance(weight, Tensor):
        return weight.detach()
    return None

get_mf_params

Return a serializable description of the model's membership functions.

Source code in highfis/models/_base.py
def get_mf_params(self) -> dict[str, list[dict[str, Any]]]:
    """Return a serializable description of the model's membership functions."""
    return {
        name: [{"type": type(mf).__name__, **mf.inspect_params()} for mf in cast(Sequence[MembershipFunction], mfs)]
        for name, mfs in self.membership_layer.input_mfs.items()
    }

get_rule_table

Return the rule base as a table of feature-to-MF indices.

Source code in highfis/models/_base.py
def get_rule_table(self) -> list[dict[str, Any]]:
    """Return the rule base as a table of feature-to-MF indices."""
    return [
        dict(
            rule_id=rule_index,
            **dict(zip(self.rule_layer.input_names, rule, strict=False)),
        )
        for rule_index, rule in enumerate(self.rule_layer.rules)
    ]

predict

Return predicted class indices.

Source code in highfis/models/_common.py
def predict(self, x: Tensor) -> Tensor:
    """Return predicted class indices."""
    with torch.no_grad():
        return torch.argmax(self.predict_proba(x), dim=1)

predict_proba

Return class probabilities computed with softmax.

Source code in highfis/models/_common.py
def predict_proba(self, x: Tensor) -> Tensor:
    """Return class probabilities computed with softmax."""
    with torch.no_grad():
        if x.dtype == torch.float64:
            was_training = self.training
            self.double()
            try:
                self.eval()
                logits = self.forward(x)
                return torch.softmax(logits, dim=1)
            finally:
                self.float()
                self.train(was_training)
        else:
            was_training = self.training
            try:
                self.eval()
                logits = self.forward(x)
                return torch.softmax(logits, dim=1)
            finally:
                self.train(was_training)

TSKRegressorModel

Bases: BaseTSKRegressorModel

Vanilla TSK regressor with sum-based rule normalization.

The vanilla Takagi-Sugeno-Kang inference computes rule firing strengths with the product t-norm and normalizes them by their total sum.

References

T. Takagi and M. Sugeno, "Fuzzy identification of systems and its applications to modeling and control," in IEEE Transactions on Systems, Man, and Cybernetics, vol. SMC-15, no. 1, pp. 116-132, Jan.-Feb. 1985, doi: 10.1109/TSMC.1985.6313399.

Initialise the vanilla TSK regressor.

Parameters:

Name Type Description Default
input_mfs Mapping[str, Sequence[MembershipFunction]]

Mapping from feature name to a sequence of :class:~highfis.memberships.MembershipFunction objects.

required
rule_base str

Rule-base construction strategy. "cartesian" (default, all MF combinations), "coco" (same-index compact), "en" (enhanced FRB), or "custom" (explicit rules via rules).

'cartesian'
t_norm str | TNormFn

Antecedent aggregation operator (default "prod".

'prod'
rules Sequence[Sequence[int]] | None

Explicit rule antecedent indices.

None
defuzzifier nn.Module | None

Custom defuzzifier. Defaults to :class:~highfis.defuzzifiers.SumBasedDefuzzifier.

None
consequent_batch_norm bool

Batch normalisation on consequent inputs.

False
Source code in highfis/models/_htsk.py
def __init__(
    self,
    input_mfs: Mapping[str, Sequence[MembershipFunction]],
    rule_base: str = "cartesian",
    t_norm: str | TNormFn = "prod",
    rules: Sequence[Sequence[int]] | None = None,
    defuzzifier: nn.Module | None = None,
    consequent_batch_norm: bool = False,
) -> None:
    """Initialise the vanilla TSK regressor.

    Args:
        input_mfs: Mapping from feature name to a sequence of
            :class:`~highfis.memberships.MembershipFunction` objects.
        rule_base: Rule-base construction strategy.  ``"cartesian"``
            (default, all MF combinations), ``"coco"`` (same-index
            compact), ``"en"`` (enhanced FRB), or ``"custom"``
            (explicit rules via *rules*).
        t_norm: Antecedent aggregation operator (default ``"prod"``.
        rules: Explicit rule antecedent indices.
        defuzzifier: Custom defuzzifier.  Defaults to
            :class:`~highfis.defuzzifiers.SumBasedDefuzzifier`.
        consequent_batch_norm: Batch normalisation on consequent inputs.
    """
    super().__init__(
        input_mfs,
        rule_base=rule_base,
        t_norm=t_norm,
        rules=rules,
        defuzzifier=defuzzifier or SumBasedDefuzzifier(),
        consequent_batch_norm=consequent_batch_norm,
    )

forward

Full forward pass through the TSK pipeline.

Source code in highfis/models/_base.py
def forward(self, x: Tensor) -> Tensor:
    """Full forward pass through the TSK pipeline."""
    output, _ = self._forward_train(x)
    return output

forward_antecedents

Compute normalized rule strengths from model antecedents.

Source code in highfis/models/_base.py
def forward_antecedents(self, x: Tensor) -> Tensor:
    """Compute normalized rule strengths from model antecedents."""
    mu = self.membership_layer(x)
    w = self.rule_layer(mu)
    return self.defuzzifier(w)

get_consequent_weights

Return the consequent layer weights or None when unavailable.

Source code in highfis/models/_base.py
def get_consequent_weights(self) -> Tensor | None:
    """Return the consequent layer weights or ``None`` when unavailable."""
    weight = getattr(self.consequent_layer, "weight", None)
    if isinstance(weight, Tensor):
        return weight.detach()
    return None

get_mf_params

Return a serializable description of the model's membership functions.

Source code in highfis/models/_base.py
def get_mf_params(self) -> dict[str, list[dict[str, Any]]]:
    """Return a serializable description of the model's membership functions."""
    return {
        name: [{"type": type(mf).__name__, **mf.inspect_params()} for mf in cast(Sequence[MembershipFunction], mfs)]
        for name, mfs in self.membership_layer.input_mfs.items()
    }

get_rule_table

Return the rule base as a table of feature-to-MF indices.

Source code in highfis/models/_base.py
def get_rule_table(self) -> list[dict[str, Any]]:
    """Return the rule base as a table of feature-to-MF indices."""
    return [
        dict(
            rule_id=rule_index,
            **dict(zip(self.rule_layer.input_names, rule, strict=False)),
        )
        for rule_index, rule in enumerate(self.rule_layer.rules)
    ]

predict

Return predicted values as a 1-D tensor.

Source code in highfis/models/_common.py
def predict(self, x: Tensor) -> Tensor:
    """Return predicted values as a 1-D tensor."""
    with torch.no_grad():
        if x.dtype == torch.float64:
            was_training = self.training
            self.double()
            try:
                self.eval()
                return self.forward(x).squeeze(1)
            finally:
                self.float()
                self.train(was_training)
        else:
            was_training = self.training
            try:
                self.eval()
                return self.forward(x).squeeze(1)
            finally:
                self.train(was_training)

build_rule_feature_mask

Build a boolean mask indicating which features are active in each rule.

Source code in highfis/models/_common.py
def build_rule_feature_mask(rules: Sequence[Sequence[int]], dont_care_indices: Sequence[int]) -> Tensor:
    """Build a boolean mask indicating which features are active in each rule."""
    if len(rules) == 0:
        raise ValueError("rules must not be empty")
    n_rules = len(rules)
    n_inputs = len(rules[0])
    if len(dont_care_indices) != n_inputs:
        raise ValueError("dont_care_indices must match the rule input dimension")

    mask = torch.ones((n_rules, n_inputs), dtype=torch.bool)
    for r, rule in enumerate(rules):
        if len(rule) != n_inputs:
            raise ValueError("all rules must have the same length")
        for i, mf_idx in enumerate(rule):
            if mf_idx == dont_care_indices[i]:
                mask[r, i] = False
    return mask