cadrumo.domain.calculations.registry.formula_runtime module

Registry-backed formula runtime using typed operation graphs.

Evaluates FormulaExpression trees declared on a ModeloRevision against casilla inputs and binding values drawn from a RegistrySnapshot. The calculation entry point calculate_registry_snapshot() is the primary surface used by ValidatedRegistryAuthority-backed callers to produce CasillaObservation rows with full provenance.

See also

domain.calculations.registry.runtime_graph

Produces formula evaluation order and dependency projections.

domain.calculations.registry.formula_runtime_ops

Arithmetic, rounding, and parameter lookup helpers called by this evaluator.

domain.calculations.registry.formula_initial_values

Builds the initial casilla value map and materialised observation envelope for this runtime.

class RegistryCalculationEntry(**data)[source]

Bases: BaseModel

One trace row emitted by the registry formula runtime.

Carries the per-formula provenance for a single formula-computed CasillaId. Entries cover only casillas computed by a registry formula; input and bound casillas remain in CasillaObservation storage and must be read through RegistryCalculationResult.observations.

Parameters:

data (Any)

formula_id: FormulaId
target_casilla_id: CasillaId
op: str
operand_refs: tuple[str, ...]
operand_casilla_refs: tuple[CasillaId, ...]
operand_values: tuple[Decimal, ...]
value: Decimal
legal_refs: tuple[LegalRefId, ...]
source_refs: tuple[SourceRefId, ...]
class RegistryCalculationUnresolvedOutcome(**data)[source]

Bases: BaseModel

One formula target that could not produce a Decimal value.

The outcome rides beside RegistryCalculationResult.observations so the engine’s value channels remain Decimal-only. Legal/source refs and formula lineage mirror CasillaObservation for the same target.

Parameters:

data (Any)

casilla_id: CasillaId
reason: RegistryUnresolvedOutcomeReason
formula_id: FormulaId
op: str
operand_refs: tuple[str, ...]
operand_casilla_refs: tuple[CasillaId, ...]
operand_values: tuple[Decimal, ...]
legal_refs: tuple[LegalRefId, ...]
source_refs: tuple[SourceRefId, ...]
context: Mapping[str, str]
class RegistryCalculationResult(**data)[source]

Bases: BaseModel

Calculated outputs for one registry snapshot.

Canonical storage is observations: a typed tuple of CasillaObservation covering every casilla on the RegistrySnapshot revision (inputs, bound, and formula-computed). Each observation carries its final scalar value plus the legal / source provenance for that casilla pulled from the registry. Formula-computed observations additionally carry formula_id, op, operand_refs, and operand_values so the full evaluation lineage survives the engine boundary.

The values and entries views are derived convenience properties for readers that need the flat {casilla_id: Decimal} map or the formula-only RegistryCalculationEntry tuple. The typed envelope is the contract; the flat views never grow new fields.

Coverage asymmetry preserved by the derivation:

  • values covers every Decimal observation (inputs, bound, computed), keyed by casilla_id to value. Text observations stay exclusively on the canonical typed envelope.

  • entries covers ONLY observations where formula_id is set. len(entries) <= len(observations) always; equality holds only when every casilla is formula-computed (rare in practice).

Consumers that need provenance for non-computed casillas must iterate observations directly; the entries view drops them by design.

Parameters:

data (Any)

modelo: str
revision: str
observations: tuple[CasillaObservation, ...]
unresolved_outcomes: tuple[RegistryCalculationUnresolvedOutcome, ...]
property values: Mapping[CasillaId, Decimal]

Read-only view from registry casilla id to final Decimal value.

Deliberately a plain @property, not a pydantic computed_field: the typed observations envelope is canonical storage; exposing this in JSON would round-trip self-incompatibly under extra='forbid' because the loader would refuse the duplicate field on the way back in.

property entries: tuple[RegistryCalculationEntry, ...]

Read-only view of formula-computed RegistryCalculationEntry rows.

Preserves the formula-only entry view with target_casilla_id and op fields for the application-layer indexers that build {target_casilla_id: entry} dictionaries. Insertion order from observations is preserved; the engine emits in formula evaluation order, which matches the original entries shape.

calculate_registry_snapshot(snapshot, *, inputs, date_context, binding_values=None, enum_binding_values=None, relation_values=None, unresolved_relation_ids=(), unresolved_binding_ids=(), date_binding_values=None, boolean_binding_values=None, text_inputs=None)[source]

Evaluate all computed formulas for a registry snapshot.

enum_binding_values carries string-valued bindings (typically profile-sourced enums like CCAA) that the lookup_bracket_by_ccaa op routes against. They are kept in a separate mapping from binding_values so the Decimal-only contract on numeric bindings stays intact.

date_binding_values carries date-valued profile facts (e.g. birth_date) consumed by the age_at_year_end op. Date facts cannot flow through the Decimal binding_values channel; keeping them in a dedicated channel preserves the Decimal-only invariant.

boolean_binding_values carries the truth values of bindings whose registry value contract declares the boolean channel – an applicability flag, a regimen eligibility predicate. The channel exists so “no” and “zero euros” are not the same bytes in transport: the projection to ZERO/ONE happens where a predicate reads the operand, and a binding supplied on both this channel and the Decimal one is refused rather than silently preferred.

The returned RegistryCalculationResult stores CasillaObservation rows for all materialised casillas. Input validation is delegated to domain.calculations.registry.formula_runtime_ops and domain.calculations.registry.formula_text_inputs; initial casilla values and absent-by-design markers are delegated to domain.calculations.registry.formula_initial_values.

Parameters:
  • snapshot (RegistrySnapshot) – The RegistrySnapshot that supplies the revision, casilla definitions, and formula graph to evaluate.

  • inputs (Mapping[TypeVar(InputKey), TypeVar(InputValue)]) – Operator-supplied input casilla values; rejected if any value is not a decimal.Decimal.

  • date_context (Mapping[str, date]) – Date-axis context (e.g. filing_period) consumed by date-aware ops; filing_period defaults to the snapshot’s typed calculation filing date when present, otherwise its year-end.

  • binding_values (Mapping[TypeAliasType, Decimal] | None) – Optional resolved numeric binding values keyed by BindingDefinition id; Decimal-only.

  • enum_binding_values (Mapping[TypeAliasType, str] | None) – Optional string-valued bindings (e.g. profile CCAA) keyed by binding id; consumed by enum-routed ops.

  • relation_values (Mapping[TypeAliasType, Decimal] | None) – Optional resolved relation values keyed by relation.id; Decimal-only.

  • unresolved_relation_ids (tuple[TypeAliasType, ...]) – Relation ids that source resolution proved missing/incomplete but non-blocking. Formula targets depending on these ids are omitted instead of zero-contributed; relation ids not listed here remain hard validation errors when absent.

  • unresolved_binding_ids (tuple[TypeAliasType, ...]) – Binding ids whose enrolled resolver ran for a present source but produced no value (expected-but-missing). Formula targets depending on these ids are omitted instead of raising binding_value_missing; binding ids not listed here remain hard validation errors when absent from binding_values.

  • date_binding_values (Mapping[TypeAliasType, date] | None) – Optional date-valued profile bindings (e.g. birth_date) consumed by date-aware ops.

  • boolean_binding_values (Mapping[TypeAliasType, bool] | None) – Optional truth-valued bindings, keyed by binding id, for every binding whose registry value contract declares the boolean channel. A boolean operand is projected to ONE/ ZERO at the moment a predicate reads it, never in transport, so a yes/no fact and a zero-valued amount stay distinguishable up to the point of use.

  • text_inputs (Mapping[TypeVar(TextInputKey), TypeVar(TextInputValue)] | None) – Optional string-valued operator inputs keyed by casilla id; consumed by text-routed ops.

Return type:

RegistryCalculationResult

evaluate_expression(expression, *, values, binding_values, parameters, date_context, relation_values, unresolved_relation_ids, unresolved_casilla_ids, operand_refs, operand_casilla_refs, operand_values, unresolved_binding_ids=frozenset({}), enum_binding_values=None, date_binding_values=None, boolean_binding_values=None, filing_year=0, text_values=None)[source]

Build the shared EvalContext for one formula tree and evaluate it.

The entry point from loose arguments: it normalises the optional channels and constructs the context once. Recursive re-entry goes through evaluate_with_context(), which carries that same context object forward instead of rebuilding it.

Return type:

Decimal

Returns:

The evaluated Decimal value of expression.

Parameters:
class EvalContext(values, binding_values, parameters, date_context, relation_values, unresolved_relation_ids, unresolved_casilla_ids, operand_refs, operand_casilla_refs, operand_values, enum_binding_values, date_binding_values, filing_year, boolean_binding_values=<factory>, unresolved_binding_ids=frozenset({}), text_values=<factory>)[source]

Bases: object

Bundles the runtime sinks + maps threaded through every recursive call.

Frozen and slotted. Exactly one instance is built per formula tree, by evaluate_expression(), and handed by reference to every per-op evaluator and every recursive re-entry through evaluate_with_context(); the tree is walked without rebuilding it. The three list sinks (operand_refs, operand_casilla_refs, operand_values) ARE mutated in place — they accumulate evaluation provenance for the explainability surface, and passing the one context object by reference is what keeps every node of the tree appending to those same three lists.

Parameters:
values: Mapping[TypeAliasType, Decimal]
binding_values: Mapping[TypeAliasType, Decimal]
parameters: Mapping[str, ParameterDefinition]
date_context: Mapping[str, date]
relation_values: Mapping[TypeAliasType, Decimal]
unresolved_relation_ids: frozenset[TypeAliasType]
unresolved_casilla_ids: set[TypeAliasType]
operand_refs: list[str]
operand_casilla_refs: list[TypeAliasType]
operand_values: list[Decimal]
enum_binding_values: Mapping[TypeAliasType, str]
date_binding_values: Mapping[TypeAliasType, date]
filing_year: int
boolean_binding_values: Mapping[TypeAliasType, bool]
unresolved_binding_ids: frozenset[TypeAliasType]
text_values: Mapping[TypeAliasType, str]
evaluate_with_context(expression, ctx)[source]

Evaluate one expression node against ctx, recursing into its args.

The dispatcher proper: leaf, specialised per-op evaluator, or generic arg-folding op. ctx is passed through by reference rather than destructured and rebuilt, so the whole tree shares one context object and one set of provenance sinks.

Return type:

Decimal

Parameters: