zoomy_core.symbolic.primitives_canonical module#
Purely-structural primitive operations.
These primitives implement structural rewrites that preserve
mathematical equality without firing any calculus rule. They use only
sympy.Basic.xreplace() and explicit pattern matching on the
expression tree — never .doit(), sp.simplify, or
sp.expand outside of carefully-scoped Mul-over-Add distribution.
Each primitive is a callable primitive(expr) -> expr. Composition
with zoomy_core.model.models.ins_generator.Operation-style
wrappers happens elsewhere; here we keep the pure-function
implementations so they can be unit-tested in isolation.
Functions in this module are lifted verbatim from
zoomy_core.model.models.ins_generator (see file header for the
specific source-line range each one came from). No logic changes —
just relocation behind a documented primitive interface.
- zoomy_core.symbolic.primitives_canonical.alpha_rename(expr)#
Rename every
Integral‘s bound variable to a depth-keyed canonical Dummy.Lifted verbatim from
_canonicalize_integral_dummies(ins_generator.py:4154). Purexreplace; no math change — alpha-equivalence of bound variables is a definitional property of the integral.
- zoomy_core.symbolic.primitives_canonical.split_integral_over_add(expr)#
∫(f+g) dv → ∫f dv + ∫g dv— linearity of the integral.Distributes through
Derivative(Integral(...), v)by linearity of the derivative. Outer multiplicative factors stay attached.Lifted from
_split_integrals_expr(ins_generator.py:4217).
- zoomy_core.symbolic.primitives_canonical.merge_integrals_over_add(expr)#
Combine sibling Integrals with matching
(limits, deriv-wrapper)signature into a singleIntegral(Σ c_i · f_i, lim).Inverse of
split_integral_over_add(). An outer factor moves inside the integral only if it doesn’t depend on the integration variable — otherwise the factor stays out (passthrough); moving it inside would change the math.Lifted from
_merge_integrals_expr(ins_generator.py:4263).
- zoomy_core.symbolic.primitives_canonical.distribute_mul_over_add(expr)#
c·(a+b) → c·a + c·b— ring-axiom distribution.Uses
sp.expandwith all non-Mul branches disabled so the only rewrite that fires is Mul-over-Add distribution. Integrals are protected during the expansion so their integrands stay intact (sympy’sexpandwould otherwise fragmentIntegral(f+g, lim)intoIntegral(f, lim) + Integral(g, lim)— that’s a separate primitive,split_integral_over_add).Lifted from
_expand_preserve_integrals(ins_generator.py:4124).
- zoomy_core.symbolic.primitives_canonical.kill_zero_length_integral(expr)#
Integral(_, (var, a, a)) → 0— structural equality only.This is a tightened version of the legacy
_kill_zero_length_integrals(ins_generator.py:4366) which fell back tosp.simplify(lo - hi) == 0.sp.simplifycan fire arbitrary rules on the bounds — for the redesign we accept only structural equality (lo == hiorlo - hireduces toS.Zerovia sympy’sAddcanonicalisation alone, no further-simplification pass).Callers who want a deeper bound match must apply a
Substrule that makes the bounds structurally equal first.
- zoomy_core.symbolic.primitives_canonical.zero_derivative_of_free_symbol(expr)#
∂_v sym → 0whensymis a freeSymboldistinct from every differentiation variable.These appear as Leibniz boundary residuals (
∂_x zwhenzis a coordinate, not a function). Sympy doesn’t auto-reduce.Lifted verbatim from
_kill_free_derivatives(ins_generator.py:4387).
- zoomy_core.symbolic.primitives_canonical.un_subs(expr)#
Unwrap every
Subs(f, var, val)whose inner is safe toxreplace(var → val).Safety = none of:
varappears as aDerivativedifferentiation variable inf(would commit to chain-rule-through-valambiguity).varis the integration variable of a nestedIntegralinf(would shadow the binder and produce nonsense limits).varis the binding variable of a nestedSubsinf(same shadowing concern).
Lifted verbatim from
_resolve_subs_safe(ins_generator.py:4539). The redesign promotes this to a primitive — the previously implicit calls (insideExpression.apply,EvaluateIntegrals,ProjectBasisIntegrals) all become explicit user steps.
- zoomy_core.symbolic.primitives_canonical.drop_zero_integrand(expr)#
Integral(0, …) → 0— structural.Lifted from the
∫0branch ofSimplifyIntegrals._leaf_sp(ins_generator.py:2371) andIsolateBasisIntegrand(ins_generator.py:2698).
- zoomy_core.symbolic.primitives_canonical.drop_zero_derivative_inner(expr)#
Derivative(0, …) → 0— structural.Lifted from the
∂(0)branch of_evaluate_linear_derivatives(ins_generator.py:4621).
- zoomy_core.symbolic.primitives_canonical.constant_integrand(expr)#
∫_a^b c dvar → c·(b − a)when integrand has novar.Lifted from the constant-integrand branch of
SimplifyIntegrals._leaf_sp(ins_generator.py:2373) andIsolateBasisIntegrand(ins_generator.py:2698).
- zoomy_core.symbolic.primitives_canonical.protect_integrals(expr, *, also_derivative_of_integral=False)#
Replace every
Integral(and optionallyDerivative(Integral(...), ...)) atom with a freshDummyand return(protected_expr, restore_map).Restore via
protected_expr.xreplace(restore_map). Used to run arithmetic (sp.expandMul-over-Add) on the expression’s outer structure without sympy fragmenting the integrand.Lifted from
_expand_preserve_integrals(ins_generator.py:4124) and the protect-helper inside_simplify_preserve_integrals(ins_generator.py:4434).