IEEE / Array Bridge (SCM) — Summary¶
ZeroProofML v0.6.1 uses Signed Common Meadows (SCM): all domain errors and division-by-zero collapse to a single absorptive bottom element ⊥. There are no Transreal tags (+∞, −∞, Φ) in the core.
v0.6.1 boundary convention: from_ieee(NaN) and from_ieee(±Inf) are the only supported way to bring non-finite IEEE inputs into SCM — they route to ⊥ at this documented boundary. Direct SCMValue(float("nan")) / SCMValue(float("inf")) construction now raises ValueError, and finite-op-finite arithmetic that produces a non-finite result raises OverflowError. See 16_verification_report.md and the zeroproof/utils/ieee_bridge.py module docstring.
For the exact SCM vs finite IEEE execution boundary, including which overflows are masked and which paths are refused, see 01_scm_foundations.md.
The bridge story is therefore:
- Scalars: map IEEE-754 NaN/±inf ↔ ⊥
- Arrays/tensors: carry a numeric payload plus an explicit boolean bottom mask
Scalar conversion (zeroproofml.utils.ieee_bridge)¶
Use this when you ingest external floats that may contain NaN/Inf.
from zeroproofml.utils.ieee_bridge import from_ieee, to_ieee
v = from_ieee(float("nan")) # -> SCMValue(⊥)
assert v.is_bottom
out = to_ieee(v) # -> nan (tooling-friendly sentinel)
Batch helpers are available for iterables: batch_from_ieee, batch_to_ieee.
Vectorised operations with masks (zeroproofml.scm.ops)¶
For numerical backends, SCM values are represented as:
- payload: float/complex array (NumPy/JAX) or tensor (Torch)
- mask: boolean array/tensor marking ⊥ positions
Vectorised operators propagate ⊥ by combining masks (and for division/inverse
also checking for zeros). Masks are keyword-only: use mask_x=... and
mask_y=.... The former positional and historical interleaved forms were
removed in v0.7.0a1 and raise TypeError in both canonical and compatibility
namespaces.
import numpy as np
from zeroproofml.scm.ops import scm_div_numpy
x = np.array([1.0, 2.0, 3.0])
x_mask = np.array([False, False, False])
y = np.array([1.0, 0.0, 1.0])
y_mask = np.array([False, False, False])
q, q_mask = scm_div_numpy(x, y, mask_x=x_mask, mask_y=y_mask) # q_mask[1] == True (division by zero -> ⊥)
Backends:
- NumPy: scm_*_numpy
- PyTorch: scm_*_torch
- JAX: scm_*_jax
All vectorised functions return (payload, mask). Since v0.6.1 the returned
mask also absorbs unsupported real domains and backend non-finite results from
finite IEEE execution; masked payload entries are finite zero placeholders.
Complex vector pow accepts real exponents only; complex exponents are refused
in v0.6.1.
Torch rational layers: bottom_mask as the SCM boundary¶
Torch SCM layers follow a single-check pattern: forward passes return a mask that you treat as ⊥ at the boundary (loss, logging, inference decode).
Example: SCMRationalLayer returns (output, bottom_mask) where bottom_mask flags denominator singularities. Since v0.6.1 the layer's forward substitutes safe operands before the division (and for sub-float64 inputs adds a promoted-float64 pre-division overflow check), so the autograd graph never sees a NaN/Inf division node; the torch.where below is a display convention, not a repair.
import torch
from zeroproofml.layers import SCMRationalLayer
layer = SCMRationalLayer(1, 1)
x = torch.tensor([-1.0, 0.0, 1.0])
y, bottom = layer(x)
decoded = torch.where(bottom, torch.full_like(y, float("nan")), y)
Design choices¶
⊥maps to IEEENaNon export (to_ieee) so downstream tooling (NumPy/Pandas/metrics) can treat it as “invalid”.- Array/tensor SCM semantics stay compiler-friendly: the “special value” is in the mask, not in the payload.
- Coverage metrics are naturally expressed as
1 - bottom_mask.float().mean().