Skip to content

Practical API recipes

The worked scenarios explain complete calculations. These smaller recipes cover domain validation, sampled properties, operator alternatives, finite comparisons, and error handling. Each Python block stands alone and is executed against installed wheel and sdist artifacts in CI.

Validate a domain before integration

from fuzzyroutines import ContinuousUniverse, DiscreteUniverse, IntegrationDomain

universe = ContinuousUniverse(0, 10, leftClosed=True, rightClosed=True)
domain = IntegrationDomain.FromLegacyInterval((2, 8)).ValidateWithin(universe)
assert universe.isBounded and universe.Contains(0)
assert domain.Contains(2) and domain.ToLegacyInterval() == (2, 8)
discrete = DiscreteUniverse((1, 3, 5))
assert discrete.Contains(3) and not discrete.Contains(2)
print(domain.ToLegacyInterval(), discrete.points)

An integration window is a finite closed interval, not the mathematical positive support. A universe may be unbounded or have open endpoints; a requested integration window must respect those endpoint restrictions.

Read exact and sampled properties

from fuzzyroutines import (
    ContinuousInterval, ContinuousRegion, ContinuousUniverse, DeriveProperties,
    DiscreteRegion, IntegrationDomain, IsNormal, SampleProperties,
    ScalarFuzzySet, Triangle,
)

model = Triangle(0, 2, 4)
universe = ContinuousUniverse(0, 4, leftClosed=True, rightClosed=True)
properties = DeriveProperties(model, universe)
sampled = SampleProperties(model, IntegrationDomain(0, 4), sampleCount=5)
assert properties.isExact and properties.height == 1
assert properties.core.Contains(2) and not properties.positiveSupport.Contains(0)
assert not sampled.isExact and sampled.heightEstimate == 1
assert sampled.coreSamples.points == (2,)
assert IsNormal(ScalarFuzzySet(universe, model))
interval = ContinuousInterval(1, 3, leftClosed=True, rightClosed=False)
region = ContinuousRegion((interval,))
assert region.Contains(1) and not region.Contains(3) and not region.isEmpty
assert not interval.isSingleton and not DiscreteRegion((1, 3)).isEmpty
print(properties.height, sampled.coordinates)

For built-in analytical families, DeriveProperties computes exact geometry restricted to the universe. SampleProperties reports observed support, core, boundary, and a height estimate on a stated grid. Hitting the peak in this example makes the estimate one; missing a narrow peak in another model can underestimate height. Generic continuous exact height is not inferred from samples. IsNormal uses exact height and an explicit tolerance, defaulting to \(10^{-12}\).

Choose scalar operator families deliberately

from math import isclose
from fuzzyroutines import NegationPolicy, SNormPolicy, TNormPolicy

expectedAnd = {"logic": 0.4, "algebraic": 0.28, "boundary": 0.1, "drastic": 0}
expectedOr = {"logic": 0.7, "algebraic": 0.82, "boundary": 1, "drastic": 1}
for family in expectedAnd:
    assert isclose(TNormPolicy(family).Evaluate(0.4, 0.7), expectedAnd[family], abs_tol=1e-12)
    assert isclose(SNormPolicy(family).Evaluate(0.4, 0.7), expectedOr[family], abs_tol=1e-12)
assert NegationPolicy("standard").Evaluate(0.4) == 0.6
assert NegationPolicy("parametric", alpha=0.5).Evaluate(0.5) == 0.5
assert NegationPolicy("parabolic", alpha=0.5).Evaluate(0.5) == 0.5
print(expectedAnd, expectedOr)

The parametric negation requires \(0<\alpha<1\); parabolic negation requires \(1/4\leq\alpha\leq3/4\). Their alpha is a negation parameter, not an alpha-cut threshold. Boundary and drastic policies are explicit alternatives; abrupt endpoint behavior can have consequences for your model.

Compare continuous models at explicit coordinates

from fuzzyroutines import (
    ComparisonDomain, ComparisonPolicy, ContinuousUniverse,
    EqualOnDomain, IncludedOnDomain, ScalarFuzzySet, Triangle,
)

universe = ContinuousUniverse(0, 4, leftClosed=True, rightClosed=True)
left = ScalarFuzzySet(universe, Triangle(0, 2, 4))
right = ScalarFuzzySet(universe, Triangle(0, 2, 4))
coordinates = ComparisonDomain((0, 1, 2, 3, 4)).ValidateWithin(universe)
policy = ComparisonPolicy("tolerance", absoluteTolerance=1e-12, relativeTolerance=1e-10)
assert policy.Equal(0.5, 0.5 + 1e-13)
assert policy.Included(0.4, 0.5)
assert EqualOnDomain(left, right, policy, coordinates)
assert IncludedOnDomain(left, right, policy, coordinates)
print(coordinates.points)

The result certifies only the five declared observations. It is not a global continuous equivalence proof, even when a separate analytical argument shows these particular definitions are identical.

Handle a mathematically undefined result

from fuzzyroutines import (
    Centroid, CentroidPolicy, ContinuousUniverse, IntegrationDomain,
    MembershipScalar, ScalarFuzzySet,
)
from fuzzyroutines.exceptions import UndefinedResultError


def ZeroGrade(coordinate: MembershipScalar) -> MembershipScalar:
    """Return zero area on every allowed coordinate."""

    return 0


empty = ScalarFuzzySet(
    ContinuousUniverse(0, 1, leftClosed=True, rightClosed=True), ZeroGrade,
)
try:
    Centroid(empty, IntegrationDomain(0, 1), CentroidPolicy(maximumDepth=20))
except UndefinedResultError:
    print("No centroid: the membership area is zero")
else:
    raise AssertionError("zero-area membership unexpectedly had a centroid")

Choose a domain-appropriate fallback or propagate this error. Returning zero silently would invent a representative coordinate. Invalid types, parameters, and domains have their own documented exception categories; adaptive exhaustion has CentroidConvergenceError. Catch the specific contract your application can handle instead of suppressing every error.