core.base

Core abstractions for single-element fuzzy-number implementations.

This module defines FuzznumStrategy, the primary abstract base class that concrete fuzzy-number types (mtype) must inherit from. It provides attribute declaration/validation, change callbacks, per-instance operation caching and a uniform operation dispatch helper that integrates with the operation registry.

Notes

  • Implementations of specific fuzzy types live under axisfuzzy/fuzztype/.

  • See examples in axisfuzzy/fuzztype/qrofs/qrofn.py and axisfuzzy/fuzztype/qrohfs/qrohfn.py for concrete usages.

class axisfuzzy.core.base.FuzznumStrategy(q=None)[source]

Bases: ABC

Abstract base for a single-element fuzzy-number strategy.

The strategy represents the data and behavior of a single fuzzy number. Subclasses declare data attributes (via annotations or class-level defaults) and implement the presentation and operation-specific logic. Key responsibilities include:

  • Enforce a “declared attribute” contract: assignments to attributes not declared in the subclass raise AttributeError (strict mode).

  • Provide attribute validators, transformers and change callbacks.

  • Offer an operation dispatcher with caching via execute_operation().

  • Collect declared attributes at class creation via __init_subclass__.

Parameters:

q (int, optional) – q-rung parameter used by many fuzzy types. If None the library default (from axisfuzzy.config.get_config()) is used.

mtype

Registered fuzzy-number type identifier for the concrete strategy class.

Type:

str

q

Effective q-rung for the instance.

Type:

int or None

Notes

  • Subclasses should call super().__init__() in their __init__.

  • Use add_attribute_validator(), add_attribute_transformer() and add_change_callback() inside subclass initialization to register type-specific rules or reactive behavior.

  • The default q validator enforces an integer in [1, 100]; subclasses may override or refine this by registering a custom validator.

Raises:
  • AttributeError – When assigning to an undeclared attribute (strict mode).

  • ValueError – If a validator rejects a new attribute value.

  • RuntimeError – If a registered change callback raises an unexpected error.

Examples

Minimal subclass pattern and validators. The following examples mirror patterns used in the repository:

# Example (simple numeric attributes)
class MyStrategy(FuzznumStrategy):
    mtype = 'mytype'
    a: float = 0.0
    b: float = 1.0

    def __init__(self, q=None):
        super().__init__(q=q)
        # ensure a and b are in [0, 1]
        self.add_attribute_validator('a', lambda v: isinstance(v, (int, float)) and 0.0 <= v <= 1.0)
        self.add_attribute_validator('b', lambda v: isinstance(v, (int, float)) and 0.0 <= v <= 1.0)

    def report(self) -> str:
        return f"a={self.a}, b={self.b}"

    def str(self) -> str:
        return f"<{self.a},{self.b}>"

Concrete examples from the codebase:

  • q-rung orthopair fuzzy number (QROFN): see axisfuzzy.fuzztype.qrofs.qrofn where membership attributes md and nmd are registered with validators and change callbacks that enforce the q-rung constraint \(\mu^q + \nu^q \leq 1\)

  • q-rung hesitant fuzzy number (QROHFN): see axisfuzzy.fuzztype.qrohfs.qrohfn which demonstrates usage of attribute transformers to coerce inputs into numpy arrays and validators that check elementwise ranges and shapes.

Implementation hints

  • Use transformers to normalise inputs (e.g., convert lists -> np.ndarray) before validation and storage.

  • Register change callbacks when inter-dependent attributes must trigger cross-checks (e.g., enforcing md/nmd constraints whenever either changes).

  • Prefer registering validators/transformers in __init__ so that they are instance-local and do not unintentionally share state across instances.

See also

axisfuzzy.fuzztype.qrofs.qrofn.QROFNStrategy, axisfuzzy.fuzztype.qrohfs.qrohfn.QROHFNStrategy

add_attribute_transformer(attr_name, transformer)[source]

Register a transformer for an attribute value.

Transformers receive the candidate value (after validators pass) and must return the value that will be stored on the instance. Typical uses are type coercion, normalization or conversion (e.g. list -> ndarray).

Parameters:
  • attr_name (str) – Attribute name to transform.

  • transformer (Callable[[Any], Any]) – Callable that takes the incoming value and returns the transformed value to be stored.

Raises:

TypeError – If transformer is not callable.

Return type:

None

Notes

  • Transformers run after validators and before the value is written.

  • If a transformer raises an exception, the assignment fails.

  • Register transformers during __init__ to keep them instance-local.

Examples

# coerce lists to numpy arrays for attribute 'md'
self.add_attribute_transformer('md', lambda v: None if v is None else np.asarray(v, dtype=float))
add_attribute_validator(attr_name, validator)[source]

Register a validator for a specific attribute.

Validators are primarily used to ensure that attribute values meet specific conditions or constraints when they are set. The validator is called whenever the attribute is set via __setattr__. If the validator returns False the assignment is rejected and a ValueError is raised.

Parameters:
  • attr_name (str) – Name of the attribute to validate. Should be one of the names returned by get_declared_attributes() or an instance attribute.

  • validator (Callable[[Any], bool]) – Callable that accepts the candidate value and returns True if the value is acceptable, False otherwise.

Raises:

TypeError – If validator is not callable.

Return type:

None

Notes

  • Validators are stored per-instance (registered inside __init__ of the concrete strategy). Registering a validator replaces any previously registered validator for the same attribute.

  • Validators are executed before transformers.

Examples

# inside a strategy __init__:
self.add_attribute_validator('md', lambda v: v is None or 0.0 <= float(v) <= 1.0)
add_change_callback(attr_name, callback)[source]

Register a post-assignment change callback for an attribute.

The callback is invoked after the attribute value has been successfully assigned. Its signature must be (attr_name, old_value, new_value). Callbacks may raise ValueError to reject an assignment, or any other exception which will be wrapped as RuntimeError by __setattr__.

Parameters:
  • attr_name (str) – Attribute name to monitor.

  • callback (Callable[[str, Any, Any], None]) – Callable invoked after assignment; may perform cross-attribute checks or trigger side-effects.

Raises:

TypeError – If callback is not callable.

Return type:

None

Notes

  • Callbacks are executed after validators and transformers and after the value has been stored on the instance.

  • If multiple callbacks for the same attribute are needed, wrap them in a small dispatcher function or register a single callback that calls others.

Examples

def on_md_change(name, old, new):
    # enforce relationship with other attribute(s)
    if new is not None and self.nmd is not None and self.q is not None:
        if new**self.q + self.nmd**self.q > 1.0:
            raise ValueError("q-rung constraint violated")
self.add_change_callback('md', on_md_change)
execute_operation(op_name, operand)[source]

Dispatch and execute a named operation for this strategy instance.

This method is the per-instance entry point to the operation subsystem. It resolves the appropriate OperationMixin implementation from the global operation registry, builds a t-norm configuration, performs caching, and finally calls the concrete operation implementation.

Parameters:
  • op_name (str) – Operation identifier (for example: 'add', 'complement', 'gt'). See get_available_operations() for supported names.

  • operand (FuzznumStrategy or int or float or None) – Second operand for binary/comparison operations, scalar for unary-with-operand operations, or None for pure unary ops.

Returns:

Operation-specific result. Concrete operations define the returned dictionary structure (commonly includes keys like 'value' or component arrays).

Return type:

dict

Raises:
  • NotImplementedError – If no operation implementation is registered for this instance’s mtype and the requested op_name.

  • TypeError – If the provided operand has an incompatible type for the requested operation (e.g. scalar where a strategy was expected).

  • ValueError – If op_name is unknown or preprocessing fails.

Notes

  • Results are cached per-instance when a stable cache key can be created.

  • The t-norm configuration used is obtained from the global operation registry; this method wraps the concrete operation call with timing and caching logic.

Examples

# binary operation with another strategy instance
res = my_strategy.execute_operation('add', other_strategy)
print(res)  # operation-defined dict

# unary complement
res = my_strategy.execute_operation('complement', None)
get_available_operations()[source]

Gets the names of all operations supported by the current fuzzy number type.

This method queries the global operation registry to determine which operations are registered and available for the mtype of this FuzznumStrategy instance.

Returns:

A list of strings, where each string is the name of an operation supported by this fuzzy number type.

Return type:

List[str]

Examples

ops = s.get_available_operations()
# ['add', 'complement', 'gt', ...]
get_declared_attributes()[source]

Retrieves a copy of the list of declared attribute names for this strategy.

This method provides introspection capabilities, allowing external code to discover which attributes are explicitly defined as data members of a FuzznumStrategy instance or its subclasses, in their definition order.

Returns:

Copy of the declared attributes list, preserving definition order.

Return type:

List[str]

Examples

attrs = instance.get_declared_attributes()
# e.g. ['md', 'nmd']
mtype: str = 'qrofn'
q: Optional[int] = 1
abstractmethod report()[source]

Produce a detailed textual report of the fuzzy number.

Returns:

Multi-line detailed representation.

Return type:

str

Notes

  • Must be implemented by concrete strategies.

abstractmethod str()[source]

Produce a concise string representation.

Returns:

Short one-line representation.

Return type:

str

Notes

  • Subclasses may override; default can delegate to report.

validate_all_attributes()[source]

Validates all attributes of the FuzznumStrategy instance.

This method runs both the centralized _validate() hook (for inter-attribute constraints) and any per-attribute validators registered via add_attribute_validator(). It collects failures rather than raising immediately to provide a consolidated report.

Returns:

A dictionary with two keys: - is_valid (bool): True if all checks passed. - errors (List[str]): Human-readable error messages for failures.

Return type:

dict

Notes

  • Use this method in tests or validation pipelines to obtain a full diagnostic report without raising exceptions.

  • For strict enforcement, call _validate() directly and let it raise exceptions on serious violations.

Examples

report = s.validate_all_attributes()
if not report['is_valid']:
    print('\n'.join(report['errors']))