Source code for pysatl_core.transformations.transformation_method

"""
Transformation computation primitives.

This module defines analytical computations produced by distribution
transformations. A transformation method remains compatible with AnalyticalComputation
so that transformed distributions continue to participate in the
existing characteristic graph and computation strategy.
"""

from __future__ import annotations

__author__ = "Leonid Elkin"
__copyright__ = "Copyright (c) 2025 PySATL project"
__license__ = "SPDX-License-Identifier: MIT"

from collections.abc import Mapping
from typing import TYPE_CHECKING

from pysatl_core.distributions.computations.computation import AnalyticalComputation
from pysatl_core.types import (
    GenericCharacteristicName,
    ParentRole,
    ResolvedSourceMethods,
    SourceRequirements,
    TransformationEvaluator,
    TransformationName,
)

if TYPE_CHECKING:
    from pysatl_core.distributions.distribution import Distribution


[docs] class TransformationMethod[In, Out](AnalyticalComputation[In, Out]): """ Analytical computation originating from a transformation. Parameters ---------- target : GenericCharacteristicName Name of the target characteristic produced by the transformation. transformation : TransformationName Logical name of the transformation that created this computation. bases : Mapping[ParentRole, Distribution] Parent distributions grouped by role. source_requirements : SourceRequirements Required parent characteristics used to build the computation. evaluator : TransformationEvaluator[In, Out] Factory producing a transformed computation callable. owner : object Transformation object passed to ``evaluator``. """ __slots__ = ( "transformation", "source_requirements", "_is_analytical", ) transformation: TransformationName source_requirements: SourceRequirements _is_analytical: bool @staticmethod def _source_status( base: Distribution, characteristic: GenericCharacteristicName, ) -> tuple[bool, bool]: """ Resolve presence and analytical status for a parent characteristic. Parameters ---------- base : Distribution Parent distribution. characteristic : GenericCharacteristicName Required characteristic name. Returns ------- tuple[bool, bool] ``(is_present, is_analytical)`` for the first loop variant of the characteristic. """ methods = base.analytical_computations.get(characteristic) if methods is None: return False, False first_label = next(iter(methods)) return True, base.loop_is_analytical(characteristic, first_label)
[docs] def __init__( self, *, target: GenericCharacteristicName, transformation: TransformationName, bases: Mapping[ParentRole, Distribution], source_requirements: SourceRequirements, evaluator: TransformationEvaluator[In, Out], owner: object, ) -> None: """ Build a transformation method with source-semantics metadata. Parameters ---------- target : GenericCharacteristicName Target characteristic produced by the method. transformation : TransformationName Logical transformation name. bases : Mapping[ParentRole, Distribution] Parent distributions grouped by role. source_requirements : SourceRequirements Required parent characteristics. evaluator : TransformationEvaluator[In, Out] Factory producing the bound transformed computation from resolved parent methods. owner : object Transformation object passed to ``evaluator``. Raises ------ ValueError If none of required source characteristics is present in parent analytical computations. """ normalized_requirements: SourceRequirements = { role: tuple(characteristics) for role, characteristics in source_requirements.items() } has_any_present_source = False is_analytical = True for role, characteristics in normalized_requirements.items(): base = bases[role] for characteristic in characteristics: is_present, source_is_analytical = self._source_status(base, characteristic) has_any_present_source = has_any_present_source or is_present is_analytical = is_analytical and is_present and source_is_analytical if not has_any_present_source: raise ValueError( "Transformation method requires at least one present source characteristic." ) resolved: ResolvedSourceMethods = { role: { characteristic: bases[role].query_method(characteristic) for characteristic in characteristics } for role, characteristics in normalized_requirements.items() } object.__setattr__(self, "target", target) object.__setattr__(self, "func", evaluator(owner, resolved)) object.__setattr__(self, "transformation", transformation) object.__setattr__(self, "source_requirements", normalized_requirements) object.__setattr__(self, "_is_analytical", is_analytical)
@property def is_analytical(self) -> bool: """Return whether all required source loops are analytical.""" return self._is_analytical
__all__ = [ "TransformationMethod", ]