mixle.engines.symbolic_engine module¶
A small dependency-free symbolic compute engine.
The symbolic engine is for expression tracing, generated-kernel inspection, and lightweight algebraic experiments. Numeric execution remains the job of NumPy/Torch engines; arrays here are NumPy object arrays of scalar expression nodes so generated kernels can be inspected without a separate runtime.
- class SymbolicExpression(op, args=())[source]
Bases:
objectA compact immutable symbolic expression tree.
- static symbol(name)[source]
Create a named symbolic variable node.
- Parameters:
name (str)
- Return type:
SymbolicExpression
- static constant(value)[source]
Create a symbolic constant node.
- Parameters:
value (Any)
- Return type:
SymbolicExpression
- static call(op, *args)[source]
Create an operation node with symbolic arguments.
- evaluate(values)[source]
Evaluate the expression with a mapping from symbol names to values.
A
wherenode with a SCALAR condition evaluates only the selected branch (MXR-080-1565); see_evaluate_where(). Every other node evaluates all of its arguments.
- symbols()[source]
Return sorted symbolic variable names referenced by this expression.
- class SymbolicEngine[source]
Bases:
ComputeEngineSmall symbolic expression engine over scalar nodes and object arrays.
- with_precision(precision)[source]
Return this engine unchanged: symbolic expressions carry no float precision policy.
The numeric engines swap their float dtype here, but symbolic nodes are exact expression trees with no reduced-precision representation, so precision adjustment is a no-op rather than an error – this lets backend-neutral code call
with_precisionuniformly across engines.- Parameters:
precision (Any)
- Return type:
SymbolicEngine
- constant(value)[source]
Return
valueas a symbolic constant node.- Parameters:
value (Any)
- Return type:
SymbolicExpression
- symbol(name)[source]
Return a named symbolic expression variable.
- Parameters:
name (str)
- Return type:
SymbolicExpression
- asarray(x, dtype=None)[source]
Convert scalars/arrays/strings into symbolic expression objects.
- zeros(shape, dtype=None)[source]
Return an object array filled with symbolic zero constants.
- empty(shape, dtype=None)[source]
Return an uninitialized object array for symbolic expressions.
- arange(*args, **kwargs)[source]
Return symbolic constants corresponding to
np.arangevalues.
- to_numpy(x)[source]
Return
xas a NumPy object array without numeric evaluation.
- evaluate(x, values)[source]
Evaluate a scalar expression or object-array expression tree.
- symbols(x)[source]
Return sorted symbolic variable names referenced by
x.
- op_counts(x)[source]
Return aggregate operation counts over a scalar or array expression.
- diagnostics(x)[source]
Return a compact diagnostic summary for generated-kernel inspection.
- stack(arrays, axis=0)[source]
Stack symbolic arrays with NumPy object-array semantics.
- concatenate(arrays, axis=0)[source]
Join symbolic arrays with NumPy object-array semantics.
- static sum(x, axis=None, dtype=None, keepdims=False, initial=None, where=None)[source]
Return a symbolic sum reduction over
axis, matching NumPy’ssumcontract.keepdims,initial, andwhereare implemented exactly asnp.sumdefines them: reduced axes are kept as size-1 dimensions,initialseeds each reduction group (the additive identity 0.0 when omitted), andwhereexcludes masked-out elements by substituting the additive identity in their place before folding.dtypehas no symbolic analogue – an expression node carries no numeric storage to cast – so a non-Nonevalue is rejected rather than silently ignored (matching the “reject unsupported arguments” half of the declared reduction contract, since honoring it is not meaningful here).
- static logsumexp(x, axis=None, keepdims=False)[source]
Return a symbolic log-sum-exp reduction over
axis, matching SciPy’slogsumexp.keepdimsis implemented exactly asscipy.special.logsumexpdefines it. SciPy’s ownlogsumexp(whichNumpyEngine.logsumexpforwards to directly) has nodtype/initial/whereparameter either, so there is no declared behavior to honor for them here; passing them raises the sameTypeErrorSciPy’s own function would, for the same reason (an unexpected keyword argument). An all--infinput (every term has zero probability in log-space) correctly evaluates to-inf, notNaN– see_logsumexp_values(). An empty reduction is likewise defined as-inf(the identity: the log of a sum of zero terms), matching SciPy.
- static max(x, axis=None, keepdims=False, initial=None, where=None)[source]
Return a symbolic max reduction over
axis, matching NumPy’smaxcontract.keepdimsandinitialare implemented exactly asnp.maxdefines them (np.maxitself has nodtypeparameter, so none is declared here either). Max has no universal identity element, so – exactly likenp.max– passingwherewithout also passinginitialis rejected rather than silently reducing an under-specified group. An empty reduction with noinitialraises (there is nothing to seed it with), matchingnp.max; withinitialit returnsinitial.
- static where(cond, x, y)[source]
Return a symbolic elementwise conditional expression.
- static maximum(x, y)[source]
Return a symbolic elementwise maximum expression.
- static less(x, y)[source]
Return a symbolic less-than comparison.
- static less_equal(x, y)[source]
Return a symbolic less-than-or-equal comparison.
- static greater(x, y)[source]
Return a symbolic greater-than comparison.
- static greater_equal(x, y)[source]
Return a symbolic greater-than-or-equal comparison.
- static equal(x, y)[source]
Return a symbolic equality comparison.
- static not_equal(x, y)[source]
Return a symbolic inequality comparison.
- static logical_and(x, y)[source]
Return a symbolic elementwise logical-and expression.
- static logical_or(x, y)[source]
Return a symbolic elementwise logical-or expression.
- static logical_not(x)[source]
Return a symbolic elementwise logical-not expression.
- static clip(x, a_min=None, a_max=None)[source]
Return a symbolic clipped expression.
- static cumsum(x, axis=None, dtype=None, out=None)[source]
Return a symbolic cumulative sum over
axis, matching NumPy’scumsumcontract.axisbehaves exactly asnp.cumsumdefines it (Noneflattens first).dtypehas no symbolic analogue – an expression node carries no numeric storage to cast into, the same reasonsum()rejects it – andoutcannot be honored either, because the result is a freshly built object array of expression nodes rather than a buffer that can be written into. Both are therefore rejected rather than silently discarded (MXR-080-1567: the previous implementation was a lambda that swalloweddtype,out, and any other argument into unused*args/**kwargsand passed onlyaxisthrough, so a caller asking for a float32 accumulation or an output buffer got neither and no signal). An unexpected keyword now raisesTypeErrorfrom ordinary signature binding, as the other engines’cumsumdoes.
- index_add(out, index, values)[source]
Return a symbolic index-add operation node.
- static to_sympy(x)[source]
Lower a symbolic expression (or object array) to a sympy expression.
- static to_sage(x)[source]
Lower a symbolic expression (or object array) to a sage expression.
- SYMBOLIC_ENGINE = <mixle.engines.symbolic_engine.SymbolicEngine object>
Shared symbolic engine; arithmetic on symbolic nodes/object arrays dispatches here.
- is_symbolic_payload(x)[source]
Return True for a symbolic node or a NumPy object array of ALL-symbolic nodes.
Every element of an object array is checked, not just the first: classifying the whole array from a single element lets a mixed-ownership payload – some elements genuine
SymbolicExpressionnodes, others unrelated plain objects – be misrouted based on whichever kind happens to occupy index 0 (numeric-first routes the symbolic elements through NumPy, which was never built to handle them; symbolic-first routes the unrelated elements through the symbolic engine instead). Mixed ownership is rejected outright rather than silently guessed at from one element.Scans left to right and stops at the first element whose kind disagrees with the first element’s, so a genuinely uniform array (the overwhelmingly common case) still only pays for a single early-exit scan, while a mixed one is still caught regardless of which kind happens to sit at index 0.