Fuzzy Operations Development Guide
This guide provides comprehensive instructions for developing custom fuzzy operations in AxisFuzzy. Using Q-Rung Orthopair Fuzzy Numbers (QROFNs) as a practical example, you will learn how to implement the four core operation types, integrate high-performance backends, and ensure proper registration within the AxisFuzzy framework.
Understanding the Operation Framework
The AxisFuzzy operation framework is built around the OperationMixin abstract base class, which
provides a standardized interface for implementing fuzzy number operations. This framework supports
four distinct operation categories, each with specific implementation requirements and use cases.
Core Architecture
The operation framework follows a strategy pattern where each operation type inherits from
OperationMixin and implements specific abstract methods. The framework handles preprocessing,
validation, performance monitoring, and result formatting automatically.
from axisfuzzy.core import OperationMixin, register_operation
@register_operation
class CustomOperation(OperationMixin):
def get_operation_name(self) -> str:
return 'custom_op'
def get_supported_mtypes(self) -> List[str]:
return ['qrofn']
Operation Types and Implementation Interfaces
The framework defines four core operation interfaces that correspond to different mathematical operation patterns:
- Binary Operations (
_execute_binary_op_impl) Handle operations between two fuzzy numbers, such as addition, multiplication, or intersection. These operations take two strategy instances and return a dictionary containing the result components.
def _execute_binary_op_impl(self, strategy_1, strategy_2, tnorm):
# Example: QROFN Addition
md_result = tnorm.t_conorm(strategy_1.md, strategy_2.md)
nmd_result = tnorm.t_norm(strategy_1.nmd, strategy_2.nmd)
return {'md': md_result, 'nmd': nmd_result, 'q': strategy_1.q}
- Unary Operations with Operand (
_execute_unary_op_operand_impl) Process operations involving a fuzzy number and a scalar value, such as power or scalar multiplication. The operand parameter provides the scalar value.
def _execute_unary_op_operand_impl(self, strategy, operand, tnorm):
# Example: QROFN Power
md_result = strategy.md ** operand
nmd_result = strategy.nmd ** operand
return {'md': md_result, 'nmd': nmd_result, 'q': strategy.q}
- Pure Unary Operations (
_execute_unary_op_pure_impl) Implement operations that transform a single fuzzy number without additional parameters, such as complement or negation operations.
def _execute_unary_op_pure_impl(self, strategy, tnorm):
# Example: QROFN Complement
return {'md': strategy.nmd, 'nmd': strategy.md, 'q': strategy.q}
- Comparison Operations (
_execute_comparison_op_impl) Handle comparison operations between fuzzy numbers, returning boolean results for relationships like greater than, less than, or equality.
def _execute_comparison_op_impl(self, strategy_1, strategy_2, tnorm):
# Example: QROFN Greater Than
score_1 = strategy_1.md ** strategy_1.q - strategy_1.nmd ** strategy_1.q
score_2 = strategy_2.md ** strategy_2.q - strategy_2.nmd ** strategy_2.q
return {'value': score_1 > score_2}
Operation Registration Mechanism
The @register_operation decorator provides automatic registration of operation classes with
the global operation scheduler. This decorator supports both eager and lazy registration patterns.
# Immediate registration (default)
@register_operation
class QROFNAddition(OperationMixin):
pass
# Lazy registration
@register_operation(eager=False)
class QROFNMultiplication(OperationMixin):
pass
The registration process validates that classes properly inherit from OperationMixin and
instantiates operation objects for immediate availability in the operation scheduler.
T-norm and T-conorm Integration
Operations receive a tnorm parameter that provides access to triangular norm and conorm
functions. These mathematical operators are essential for fuzzy set operations and ensure
consistent behavior across different operation implementations.
# T-norm application (intersection-like behavior)
result = tnorm.t_norm(value1, value2)
# T-conorm application (union-like behavior)
result = tnorm.t_conorm(value1, value2)
The framework supports multiple T-norm families including algebraic, Einstein, Hamacher, and Frank norms, allowing operations to adapt their behavior based on the configured norm type.
QROFN Framework Example
Q-Rung Orthopair Fuzzy Numbers demonstrate the framework’s capabilities through their dual-component structure (membership and non-membership degrees) and q-rung parameter. Each QROFN operation must preserve the q-rung constraint while applying appropriate T-norm/T-conorm combinations.
@register_operation
class QROFNAddition(OperationMixin):
def get_operation_name(self) -> str:
return 'add'
def get_supported_mtypes(self) -> List[str]:
return ['qrofn']
def _execute_binary_op_impl(self, strategy_1, strategy_2, tnorm):
# Addition: md = S(md1, md2), nmd = T(nmd1, nmd2)
md_result = tnorm.t_conorm(strategy_1.md, strategy_2.md)
nmd_result = tnorm.t_norm(strategy_1.nmd, strategy_2.nmd)
return {'md': md_result, 'nmd': nmd_result, 'q': strategy_1.q}
This framework design ensures type safety, performance optimization, and consistent behavior across all fuzzy number types while providing flexibility for custom mathematical formulations.
Complete QROFN Operations Reference
The AxisFuzzy framework provides a comprehensive suite of operations for Q-Rung Orthopair Fuzzy Numbers. The following table catalogs all available operations, their types, and implementation characteristics:
Operation Name |
Operation Type |
Notes |
Arithmetic Operations |
||
|
Binary |
Addition using t-conorm for MD, t-norm for NMD |
|
Binary |
Subtraction with constraint validation |
|
Binary |
Multiplication using t-norm for MD, t-conorm for NMD |
|
Binary |
Division with zero-division protection |
|
Unary+Operand |
Power operation using t-norm generator functions |
|
Unary+Operand |
Scalar multiplication with t-norm generators |
|
Unary+Operand |
Exponential operation (experimental) |
|
Unary+Operand |
Logarithmic operation (experimental) |
Comparison Operations |
||
|
Binary |
Greater than using score function (md - nmd) |
|
Binary |
Less than using score function |
|
Binary |
Equality with epsilon tolerance |
|
Binary |
Greater than or equal with score function |
|
Binary |
Less than or equal with score function |
|
Binary |
Not equal with epsilon tolerance |
Set-Theoretic Operations |
||
|
Binary |
Fuzzy intersection using t-norm |
|
Binary |
Fuzzy union using t-conorm |
|
Unary |
Fuzzy complement (swap MD and NMD) |
|
Binary |
Set difference A ∩ ¬B |
|
Binary |
Symmetric difference (A ∪ B) ∩ ¬(A ∩ B) |
Logical Operations |
||
|
Binary |
Fuzzy implication ¬A ∪ B |
|
Binary |
Fuzzy equivalence (A → B) ∩ (B → A) |
Matrix Operations |
||
|
Binary |
Matrix multiplication for Fuzzarray objects |
Operation Type Categories:
Binary: Operations between two fuzzy numbers or arrays
Unary: Operations on a single fuzzy number or array
Unary+Operand: Operations on a fuzzy number/array with a scalar operand
Method Categories:
Arithmetic: Mathematical operations following fuzzy arithmetic principles
Comparison: Ordering operations using score functions and epsilon tolerance
Set Theory: Classical fuzzy set operations (intersection, union, complement)
Logic: Fuzzy logical operations (implication, equivalence)
Linear Algebra: Matrix and tensor operations for high-dimensional fuzzy data
Implementation Notes:
All operations support both
Fuzznum(scalar) andFuzzarray(vectorized) executionExperimental operations (
exp,log) may have limited stability guaranteesComparison operations use the score function \(S(A) = \mu_A - \nu_A\) for ordering
Set-theoretic operations leverage configurable t-norm and t-conorm families
Matrix operations preserve fuzzy constraints while enabling linear algebraic computations
Implementing Binary Operations
Binary operations form the foundation of fuzzy arithmetic and set operations. This section demonstrates how to implement high-performance binary operations using the QROFN framework as a comprehensive example.
Mathematical Foundation
QROFN binary operations are based on T-norm and T-conorm pairs that preserve the orthopair constraint \(md^q + nmd^q \leq 1\). The fundamental patterns are:
Addition Pattern:
Multiplication Pattern:
Where \(T\) is a T-norm, \(S\) is a T-conorm, and the choice of T-norm/T-conorm pair determines the specific algebraic properties.
Implementation Architecture
Binary operations require implementing two core methods:
class QROFNAddition(OperationMixin):
def _execute_binary_op_impl(self, strategy_1, strategy_2, tnorm):
"""Single Fuzznum operation"""
pass
def _execute_fuzzarray_op_impl(self, fuzzarray_1, other, tnorm):
"""Vectorized Fuzzarray operation"""
pass
The dual implementation ensures both scalar and vectorized operations maintain consistent semantics while optimizing for their respective use cases.
QROFN Addition Implementation
Addition demonstrates the S-T pattern where membership degrees use T-conorm (disjunctive) and non-membership degrees use T-norm (conjunctive):
@register_operation
class QROFNAddition(OperationMixin):
def get_operation_name(self) -> str:
return 'add'
def get_supported_mtypes(self) -> List[str]:
return ['qrofn']
def _execute_binary_op_impl(self, strategy_1, strategy_2, tnorm):
# Addition: md = S(md1, md2), nmd = T(nmd1, nmd2)
md = tnorm.t_conorm(strategy_1.md, strategy_2.md)
nmd = tnorm.t_norm(strategy_1.nmd, strategy_2.nmd)
return {'md': md, 'nmd': nmd, 'q': strategy_1.q}
The T-conorm increases membership (optimistic combination) while T-norm decreases non-membership (conservative combination), reflecting additive semantics.
QROFN Multiplication Implementation
Multiplication uses the T-S pattern, inverting the T-norm/T-conorm roles:
def _execute_binary_op_impl(self, strategy_1, strategy_2, tnorm):
# Multiplication: md = T(md1, md2), nmd = S(nmd1, nmd2)
md = tnorm.t_norm(strategy_1.md, strategy_2.md)
nmd = tnorm.t_conorm(strategy_1.nmd, strategy_2.nmd)
return {'md': md, 'nmd': nmd, 'q': strategy_1.q}
This pattern reflects multiplicative semantics where both operands must contribute to membership (conjunctive) while non-membership accumulates (disjunctive).
High-Performance Fuzzarray Operations
Vectorized operations leverage NumPy broadcasting and the _prepare_operands helper for optimal performance:
def _execute_fuzzarray_op_impl(self, fuzzarray_1, other, tnorm):
# Extract and broadcast component arrays
mds1, nmds1, mds2, nmds2 = _prepare_operands(fuzzarray_1, other)
# Vectorized T-norm/T-conorm operations
md_res = tnorm.t_conorm(mds1, mds2) # Addition pattern
nmd_res = tnorm.t_norm(nmds1, nmds2)
# Construct result backend
backend_cls = get_registry_fuzztype().get_backend('qrofn')
new_backend = backend_cls.from_arrays(md_res, nmd_res, q=fuzzarray_1.q)
return Fuzzarray(backend=new_backend)
The _prepare_operands Function
This critical helper handles operand preprocessing, type validation, and broadcasting:
def _prepare_operands(fuzzarray_1, other):
mds1, nmds1 = fuzzarray_1.backend.get_component_arrays()
if isinstance(other, Fuzzarray):
# Validate compatibility
if other.mtype != fuzzarray_1.mtype:
raise ValueError(f"Mtype mismatch: {fuzzarray_1.mtype} vs {other.mtype}")
if other.q != fuzzarray_1.q:
raise ValueError(f"Q-rung mismatch: {fuzzarray_1.q} vs {other.q}")
mds2, nmds2 = other.backend.get_component_arrays()
return np.broadcast_arrays(mds1, nmds1, mds2, nmds2)
elif isinstance(other, Fuzznum):
# Handle scalar broadcasting
mds2 = np.full((1,), other.md, dtype=mds1.dtype)
nmds2 = np.full((1,), other.nmd, dtype=nmds1.dtype)
return np.broadcast_arrays(mds1, nmds1, mds2, nmds2)
Error Handling and Validation
Robust operations require comprehensive validation:
Type Compatibility: Ensure operands share the same mtype and q-rung parameter.
Shape Broadcasting: Leverage NumPy’s broadcasting rules with clear error messages for incompatible shapes.
Numerical Stability: T-norm/T-conorm operations maintain the orthopair constraint automatically.
Performance Considerations: Use np.broadcast_arrays for efficient memory layout and vectorization.
Advanced Binary Operations
Beyond arithmetic, QROFN supports set-theoretic operations:
Intersection (Minimum):
Union (Maximum):
These operations use the same implementation pattern but with different T-norm/T-conorm semantics, demonstrating the framework’s flexibility.
Integration with Operation Scheduler
The @register_operation decorator automatically integrates operations with the global scheduler:
from axisfuzzy.core import fuzzynum
# Automatic registration enables operator overloading
a = fuzzynum(md=0.8, nmd=0.3, mtype='qrofn', q=3)
b = fuzzynum(md=0.6, nmd=0.4, mtype='qrofn', q=3)
result = a + b # Dispatches to QROFNAddition
This seamless integration allows natural mathematical syntax while maintaining the high-performance vectorized backend.
Unary and Comparison Operations
Unary and comparison operations form the foundation of advanced fuzzy logic computations, enabling scalar transformations and ordering relationships between fuzzy numbers. This section explores the implementation patterns for power operations, scalar multiplication, complement operations, and score-based comparison strategies.
Mathematical Foundations for Unary Operations
Unary operations in AxisFuzzy fall into two distinct categories: operand-based operations that require an additional scalar parameter, and pure unary operations that transform the fuzzy number independently.
Power Operations implement scalar exponentiation using T-norm generator and pseudo-inverse functions:
where \(g\) and \(f\) are the dual generator and generator functions of the T-norm respectively, and \(g^{-1}\) and \(f^{-1}\) are the corresponding pseudo-inverse functions.
Times Operations provide scalar multiplication using the T-norm generator framework:
Note that the times operation uses the generator functions in reverse order compared to the power operation, ensuring correct implementation of different operation semantics.
Complement Operations implement fuzzy negation by swapping membership degrees:
Implementation Architecture for Unary Operations
Unary operations utilize two specialized execution methods depending on their mathematical nature:
- Operand-Based Unary Operations (
_execute_unary_op_operand_impl) Handle operations requiring a scalar operand, such as power and times operations.
def _execute_unary_op_operand_impl(self,
strategy: Any,
operand: Union[int, float],
tnorm: OperationTNorm) -> Dict[str, Any]:
# Example: QROFN Power Operation using T-norm generators
md = tnorm.g_inv_func(operand * tnorm.g_func(strategy.md))
nmd = tnorm.f_inv_func(operand * tnorm.f_func(strategy.nmd))
return {'md': md, 'nmd': nmd, 'q': strategy.q}
- Pure Unary Operations (
_execute_unary_op_pure_impl) Handle operations that transform the fuzzy number without additional parameters.
def _execute_unary_op_pure_impl(self,
strategy: Any,
tnorm: OperationTNorm) -> Dict[str, Any]:
# Example: QROFN Complement
return {'md': strategy.nmd, 'nmd': strategy.md, 'q': strategy.q}
QROFN Power and Times Implementation
The QROFNPower and QROFNTimes classes demonstrate operand-based unary operations:
@register_operation
class QROFNPower(OperationMixin):
"""
Implements the power operation for Q-Rung Orthopair Fuzzy Numbers (QROFNs).
This operation calculates A^operand using T-norm generator functions.
"""
def get_operation_name(self) -> str:
return 'pow'
def get_supported_mtypes(self) -> List[str]:
return ['qrofn']
def _execute_unary_op_operand_impl(self,
strategy: Any,
operand: Union[int, float],
tnorm: OperationTNorm) -> Dict[str, Any]:
"""
Executes the unary power operation using T-norm generator functions.
The power operation uses the mathematical formula:
A^n = (g^{-1}(n·g(md_A)), f^{-1}(n·f(nmd_A)))
"""
# Use T-norm generator functions for mathematically consistent power operation
md = tnorm.g_inv_func(operand * tnorm.g_func(strategy.md))
nmd = tnorm.f_inv_func(operand * tnorm.f_func(strategy.nmd))
return {'md': md, 'nmd': nmd, 'q': strategy.q}
@register_operation
class QROFNTimes(OperationMixin):
"""
Implements the times (scalar multiplication) operation for QROFNs.
This operation calculates n·A using T-norm generator functions.
Note: Times operation swaps the generator functions compared to power.
"""
def get_operation_name(self) -> str:
return 'times'
def get_supported_mtypes(self) -> List[str]:
return ['qrofn']
def _execute_unary_op_operand_impl(self,
strategy: Any,
operand: Union[int, float],
tnorm: OperationTNorm) -> Dict[str, Any]:
"""
Executes the unary times operation using T-norm generator functions.
The times operation uses the mathematical formula:
n·A = (f^{-1}(n·f(md_A)), g^{-1}(n·g(nmd_A)))
"""
# Note: Times operation swaps f and g functions compared to power
md = tnorm.f_inv_func(operand * tnorm.f_func(strategy.md))
nmd = tnorm.g_inv_func(operand * tnorm.g_func(strategy.nmd))
return {'md': md, 'nmd': nmd, 'q': strategy.q}
Key Implementation Features:
Type Validation: Ensures operands are valid numeric types
Constraint Preservation: Maintains q-rung orthopair properties
Vectorization Support: Compatible with NumPy broadcasting for arrays
High-Performance Fuzzarray Operations
Unary operations on Fuzzarray objects leverage vectorized operations with T-norm generators for optimal performance:
@register_operation
class QROFNPower(OperationMixin):
# get_operation_name():...
# get_supported_mtypes():...
# _execute_unary_op_operand_impl():...
def _execute_fuzzarray_op_impl(self,
fuzzarray: Fuzzarray,
operand: Union[int, float],
tnorm: OperationTNorm) -> Fuzzarray:
"""
Executes vectorized power operation on Fuzzarray.
"""
mds, nmds = fuzzarray.backend.get_component_arrays()
md_res = tnorm.g_inv_func(operand * tnorm.g_func(mds))
nmd_res = tnorm.f_inv_func(operand * tnorm.f_func(nmds))
backend_cls = get_registry_fuzztype().get_backend('qrofn')
new_backend = backend_cls.from_arrays(md_res, nmd_res, q=fuzzarray.q)
return Fuzzarray(backend=new_backend)
@register_operation
class QROFNTimes(OperationMixin):
# get_operation_name():...
# get_supported_mtypes():...
# _execute_unary_op_operand_impl():...
def _execute_fuzzarray_op_impl(self,
fuzzarray: Fuzzarray,
operand: Union[int, float],
tnorm: OperationTNorm) -> Fuzzarray:
"""
Executes vectorized times operation on Fuzzarray.
"""
mds, nmds = fuzzarray.backend.get_component_arrays()
md_res = tnorm.f_inv_func(operand * tnorm.f_func(mds))
nmd_res = tnorm.g_inv_func(operand * tnorm.g_func(nmds))
backend_cls = get_registry_fuzztype().get_backend('qrofn')
new_backend = backend_cls.from_arrays(md_res, nmd_res, q=fuzzarray.q)
return Fuzzarray(backend=new_backend)
Performance Optimizations:
Direct Array Access: Bypasses object overhead for component arrays
In-Place Operations: Minimizes memory allocation where possible
Broadcasting Support: Handles scalar-array operations efficiently
Comparison Operations and Score Functions
Comparison operations implement ordering relationships using mathematical score functions that map fuzzy numbers to comparable scalar values.
Score Function Definition for QROFNs:
Accuracy Function for tie-breaking:
Implementation of QROFN Comparison Logic
The QROFNGreaterThan class demonstrates the comparison operation pattern:
@register_operation
class QROFNGreaterThan(OperationMixin):
def get_operation_name(self) -> str:
return 'gt'
def get_supported_mtypes(self) -> List[str]:
return ['qrofn']
def _execute_comparison_op_impl(self, strategy_1, strategy_2, tnorm):
# Simple comparison using membership degree difference
# This provides a direct score-based comparison for QROFN values
return {'value': strategy_1.md - strategy_1.nmd > strategy_2.md - strategy_2.nmd}
Comparison Features:
Epsilon Tolerance: Handles floating-point precision issues
Hierarchical Comparison: Uses accuracy function for tie-breaking
Boolean Return Format: Standardized dictionary format for results
Vectorized Array Comparisons
Array-level comparisons return boolean arrays for element-wise analysis:
def _execute_fuzzarray_op_impl(self, fuzzarray_1, fuzzarray_2, tnorm):
# Extract component arrays using helper function
mds1, nmds1, mds2, nmds2 = _prepare_operands(fuzzarray_1, fuzzarray_2)
# Vectorized comparison using membership degree differences
return np.where(mds1 - nmds1 > mds2 - nmds2, True, False)
Vectorization Benefits:
Batch Processing: Handles large arrays efficiently
Memory Efficiency: Minimizes temporary array creation
Broadcasting Support: Automatic shape compatibility handling
Error Handling and Validation
Robust error handling ensures mathematical consistency and user-friendly diagnostics:
def _execute_unary_op_operand_impl(self, strategy, operand, tnorm):
"""Execute unary operation with operand validation"""
# Type validation for operand
if not isinstance(operand, (int, float)):
raise TypeError("Operand must be numeric (int or float)")
# Value validation for specific operations
if operand < 0:
raise ValueError("Operand must be non-negative for power operations")
# Execute the actual operation logic
# Implementation depends on specific operation type
Validation Features:
Type Safety: Ensures operands match expected types
Mathematical Constraints: Validates domain restrictions
Descriptive Errors: Provides clear diagnostic messages
This comprehensive approach to unary and comparison operations establishes a robust foundation for advanced fuzzy computations while maintaining the performance characteristics essential for scientific computing applications.
High-Performance Backend Development
The FuzzarrayBackend architecture provides the computational foundation for AxisFuzzy’s high-performance fuzzy array operations. This section covers essential backend implementation patterns and practical application examples for developing custom fuzzy number backends.
Backend Architecture Overview
AxisFuzzy employs a Structure-of-Arrays (SoA) design where each fuzzy number component is stored in separate NumPy arrays, enabling efficient vectorized operations:
# Memory layout comparison
# AoS: [md₁,nmd₁] [md₂,nmd₂] [md₃,nmd₃] ...
# SoA: [md₁,md₂,md₃,...] [nmd₁,nmd₂,nmd₃,...]
Essential Backend Implementation
A minimal backend implementation requires these core components:
from axisfuzzy.core import FuzzarrayBackend, register_backend
@register_backend
class CustomBackend(FuzzarrayBackend):
mtype = 'custom_type'
@property
def cmpnum(self) -> int:
return 2 # Number of component arrays
@property
def cmpnames(self) -> Tuple[str, ...]:
return ('md', 'nmd') # Component names
def _initialize_arrays(self):
self.mds = np.zeros(self.shape, dtype=self.dtype)
self.nmds = np.zeros(self.shape, dtype=self.dtype)
def get_component_arrays(self) -> Tuple[np.ndarray, ...]:
return self.mds, self.nmds
Vectorized Constraint Validation
Implement efficient constraint checking for mathematical validity:
@staticmethod
def _validate_fuzzy_constraints_static(mds: np.ndarray, nmds: np.ndarray,
q: int) -> None:
"""Vectorized QROFN constraint: md^q + nmd^q ≤ 1"""
epsilon = get_config().DEFAULT_EPSILON
sum_of_powers = np.power(mds, q) + np.power(nmds, q)
violations = sum_of_powers > (1.0 + epsilon)
if np.any(violations):
violation_indices = np.where(violations)
first_idx = tuple(idx[0] for idx in violation_indices)
raise ValueError(f"Constraint violation at {first_idx}")
Practical Application Examples
Backend Registration and Usage:
# Automatic registration via decorator
@register_backend
class QROFNBackend(FuzzarrayBackend):
mtype = 'qrofn'
# Factory function automatically selects backend
from axisfuzzy.core.fuzzarray import fuzzarray
arr = fuzzarray(data, mtype='qrofn', q=2)
High-Performance Array Operations:
# Efficient element access and modification
def get_fuzznum_view(self, index: Any) -> 'Fuzznum':
md_val = float(self.mds[index])
nmd_val = float(self.nmds[index])
return Fuzznum(mtype=self.mtype, q=self.q).create(
md=md_val, nmd=nmd_val)
# Memory-efficient operations
def copy(self) -> 'QROFNBackend':
new_backend = QROFNBackend(self.shape, self.q, **self.kwargs)
new_backend.mds = self.mds.copy()
new_backend.nmds = self.nmds.copy()
return new_backend
Integration with Operations:
# Backend provides arrays for vectorized operations
def _execute_fuzzarray_op_impl(self, fuzzarray_1, other, tnorm):
mds1, nmds1 = fuzzarray_1.backend.get_component_arrays()
mds2, nmds2 = other.backend.get_component_arrays()
# Vectorized computation using t-norms
result_mds = tnorm.t_conorm(mds1, mds2)
result_nmds = tnorm.t_norm(nmds1, nmds2)
return fuzzarray_1.backend.from_arrays(
result_mds, result_nmds, q=fuzzarray_1.q)
The SoA architecture enables AxisFuzzy to achieve optimal performance for large-scale fuzzy computations while maintaining mathematical correctness through vectorized constraint validation and efficient NumPy integration.
Operation Development Guide
This section demonstrates the complete development workflow for implementing QROFN operations in AxisFuzzy, using actual code examples from the library to illustrate best practices for operation registration, backend integration, and testing patterns.
Operation Implementation Pattern
QROFN operations follow a standardized implementation pattern. Here’s the complete implementation of QROFN addition:
@register_operation
class QROFNAddition(OperationMixin):
"""
Implements the addition operation for Q-Rung Orthopair Fuzzy Numbers.
The addition formula: md = S(md_A, md_B), nmd = T(nmd_A, nmd_B)
where S is a t-conorm and T is a t-norm.
"""
def get_operation_name(self) -> str:
return 'add'
def get_supported_mtypes(self) -> List[str]:
return ['qrofn']
def _execute_binary_op_impl(self, strategy_1, strategy_2, tnorm):
# Core mathematical operation
md = tnorm.t_conorm(strategy_1.md, strategy_2.md)
nmd = tnorm.t_norm(strategy_1.nmd, strategy_2.nmd)
return {'md': md, 'nmd': nmd, 'q': strategy_1.q}
def _execute_fuzzarray_op_impl(self, fuzzarray_1, other, tnorm):
# High-performance vectorized implementation
mds1, nmds1, mds2, nmds2 = _prepare_operands(fuzzarray_1, other)
md_res = tnorm.t_conorm(mds1, mds2)
nmd_res = tnorm.t_norm(nmds1, nmds2)
backend_cls = get_registry_fuzztype().get_backend('qrofn')
new_backend = backend_cls.from_arrays(md_res, nmd_res, q=fuzzarray_1.q)
return Fuzzarray(backend=new_backend)
Key implementation principles:
Decorator Registration:
@register_operationenables automatic discoveryType Safety:
get_supported_mtypes()ensures operation compatibilityDual Implementation: Both single fuzzy number and vectorized array operations
Mathematical Correctness: Operations follow established fuzzy logic formulas
High-performance computation based on the backend
The _execute_fuzzarray_op_impl method provides vectorized operations for high-performance computation on fuzzy arrays. This method leverages NumPy’s broadcasting and vectorization capabilities to process entire arrays efficiently.
Core Implementation Pattern
def _execute_fuzzarray_op_impl(self,
fuzzarray_1: Fuzzarray,
other: Optional[Any],
tnorm: OperationTNorm) -> Fuzzarray:
"""
High-performance vectorized operation for QROFN fuzzy arrays.
Args:
fuzzarray_1: Primary fuzzy array operand
other: Secondary operand (Fuzzarray, Fuzznum, or scalar)
tnorm: T-norm/T-conorm operations handler
Returns:
Fuzzarray: Result of vectorized operation
"""
# Step 1: Prepare operands with broadcasting
mds1, nmds1, mds2, nmds2 = _prepare_operands(fuzzarray_1, other)
# Step 2: Apply vectorized fuzzy operations
# For addition: md = S(md1, md2), nmd = T(nmd1, nmd2)
md_res = tnorm.t_conorm(mds1, mds2) # T-conorm for membership
nmd_res = tnorm.t_norm(nmds1, nmds2) # T-norm for non-membership
# Step 3: Create result backend and return Fuzzarray
backend_cls = get_registry_fuzztype().get_backend('qrofn')
new_backend = backend_cls.from_arrays(md_res, nmd_res, q=fuzzarray_1.q)
return Fuzzarray(backend=new_backend)
Advanced Vectorized Operations with Conditions
For complex operations like subtraction and division, conditional logic is vectorized using NumPy masks:
def _execute_fuzzarray_op_impl(self, fuzzarray_1, other, tnorm):
"""Vectorized subtraction with conditional validation."""
mds1, nmds1, mds2, nmds2 = _prepare_operands(fuzzarray_1, other)
q = fuzzarray_1.q
epsilon = get_config().DEFAULT_EPSILON
# Vectorized condition checking
with np.errstate(divide='ignore', invalid='ignore'):
condition_1 = np.divide(nmds1, nmds2)
condition_2 = ((1 - mds1**q) / (1 - mds2**q))**(1/q)
# Boolean mask for valid operations
valid_mask = (
(condition_1 >= epsilon) & (condition_1 <= 1 - epsilon) &
(condition_2 >= epsilon) & (condition_2 <= 1 - epsilon) &
(condition_1 <= condition_2)
)
# Vectorized computation with conditional results
md_res_valid = ((mds1**q - mds2**q) / (1 - mds2**q))**(1/q)
nmd_res_valid = np.divide(nmds1, nmds2)
# Apply results only where conditions are met
md_res = np.where(valid_mask, md_res_valid, 0.0) # Default: (0, 1)
nmd_res = np.where(valid_mask, nmd_res_valid, 1.0)
# Handle numerical errors
np.nan_to_num(md_res, copy=False, nan=0.0)
np.nan_to_num(nmd_res, copy=False, nan=1.0)
backend_cls = get_registry_fuzztype().get_backend('qrofn')
new_backend = backend_cls.from_arrays(md_res, nmd_res, q=q)
return Fuzzarray(backend=new_backend)
Performance Optimization Techniques
Broadcasting Strategy: Uses
_prepare_operandsfor automatic shape compatibilityError State Management:
np.errstatehandles division by zero gracefullyConditional Vectorization:
np.whereand boolean masks replace loopsMemory Efficiency: In-place operations with
copy=FalseparametersNumerical Stability:
np.nan_to_numensures robust floating-point handling
Operand Preparation Utilities
The _prepare_operands function handles type checking and broadcasting for vectorized operations:
def _prepare_operands(fuzzarray_1, other):
"""Helper to get component arrays from operands with broadcasting."""
mds1, nmds1 = fuzzarray_1.backend.get_component_arrays()
if isinstance(other, Fuzzarray):
# Validate compatibility
if other.mtype != fuzzarray_1.mtype:
raise ValueError(f"Cannot operate on different mtypes: "
f"{fuzzarray_1.mtype} and {other.mtype}")
if other.q != fuzzarray_1.q:
raise ValueError(f"Cannot operate on different q values: "
f"{fuzzarray_1.q} and {other.q}")
mds2, nmds2 = other.backend.get_component_arrays()
return np.broadcast_arrays(mds1, nmds1, mds2, nmds2)
elif isinstance(other, Fuzznum):
# Handle Fuzznum broadcasting
mds2 = np.full((1,), other.md, dtype=mds1.dtype)
nmds2 = np.full((1,), other.nmd, dtype=nmds1.dtype)
return np.broadcast_arrays(mds1, nmds1, mds2, nmds2)
Conclusion
This development guide provides a comprehensive framework for implementing custom fuzzy operations in AxisFuzzy. The systematic approach demonstrated through QROFN examples ensures both mathematical correctness and high-performance execution across scalar and vectorized computations.
Key Implementation Principles:
Operation Framework Mastery: Understand the four operation types (binary, unary with operand, pure unary, comparison) and implement appropriate
_execute_*_implmethods for your mathematical requirements.Dual Implementation Strategy: Provide both
_execute_binary_op_implfor scalar operations and_execute_fuzzarray_op_implfor vectorized computations, ensuring semantic consistency while optimizing for performance.Registration Integration: Use
@register_operationdecorators to seamlessly integrate operations with AxisFuzzy’s dispatch system and enable natural operator overloading syntax.Performance Optimization: Leverage
_prepare_operandsutilities, NumPy broadcasting, and SoA backend architecture for efficient memory usage and vectorized execution.
Best Practices:
Maintain mathematical rigor in T-norm/T-conorm applications and constraint preservation
Implement comprehensive error handling for type compatibility and numerical stability
Follow established patterns for operand preparation and result construction
Ensure consistent behavior between scalar and array operation implementations
By following this guide, developers can confidently extend AxisFuzzy’s operation capabilities while maintaining the library’s standards for correctness, performance, and mathematical precision. The modular architecture ensures that custom operations integrate seamlessly with existing fuzzy number types and computational workflows.
Next Steps: After implementation, consider contributing your operations back to the AxisFuzzy community through established contribution guidelines, enabling broader adoption and collaborative improvement of your mathematical models.