Align version=1 with the strict Lambda v0.2.0 source
#2
by betterwithage - opened
- build.toml +5 -0
- build/torch-cpu/__init__.py +2 -0
- build/torch-cpu/metadata.json +1 -10
- build/torch-cpu/szl_lambda_gate/__init__.py +216 -172
- build/torch-cpu/szl_lambda_gate/_lambda.py +556 -313
- build/torch-cpu/szl_lambda_gate/_ops.py +10 -10
- build/torch-cpu/szl_lambda_gate/_v1.py +221 -0
- build/torch-cpu/szl_lambda_gate/governed_norm/__init__.py +279 -0
- build/torch-cpu/szl_lambda_gate/governed_norm/_norm.py +246 -0
- build/torch-cpu/szl_lambda_gate/governed_norm/_receipt.py +253 -0
- build/torch-cpu/szl_lambda_gate/governed_norm/layers.py +60 -0
- build/torch-cpu/szl_lambda_gate/layers.py +54 -51
- build/torch-universal/__init__.py +2 -0
- build/torch-universal/metadata.json +1 -0
- build/torch-universal/szl_lambda_gate/__init__.py +216 -0
- build/torch-universal/szl_lambda_gate/_lambda.py +556 -0
- build/torch-universal/szl_lambda_gate/_ops.py +10 -0
- build/torch-universal/szl_lambda_gate/_v1.py +221 -0
- build/torch-universal/szl_lambda_gate/governed_norm/__init__.py +279 -0
- build/torch-universal/szl_lambda_gate/governed_norm/_norm.py +246 -0
- build/torch-universal/szl_lambda_gate/governed_norm/_receipt.py +253 -0
- build/torch-universal/szl_lambda_gate/governed_norm/layers.py +60 -0
- build/torch-universal/szl_lambda_gate/layers.py +54 -0
- distribution.json +1 -0
- reference/szl_lambda_v1.py +205 -0
- source/LICENSE +202 -0
- spec/lambda_v1_vectors.json +730 -0
- spec/szl.lambda.v1.json +133 -0
build.toml
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[general]
|
| 2 |
+
name = "szl_lambda_gate"
|
| 3 |
+
|
| 4 |
+
[torch]
|
| 5 |
+
universal = true
|
build/torch-cpu/__init__.py
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .szl_lambda_gate import * # noqa: F401,F403
|
| 2 |
+
from .szl_lambda_gate import __all__
|
build/torch-cpu/metadata.json
CHANGED
|
@@ -1,10 +1 @@
|
|
| 1 |
-
{
|
| 2 |
-
"name": "szl-lambda-gate",
|
| 3 |
-
"id": "_szl_lambda_gate_cpu_9cf02fddf8",
|
| 4 |
-
"version": 1,
|
| 5 |
-
"license": "Apache-2.0",
|
| 6 |
-
"python-depends": [],
|
| 7 |
-
"backend": {
|
| 8 |
-
"type": "cpu"
|
| 9 |
-
}
|
| 10 |
-
}
|
|
|
|
| 1 |
+
{"backend":{"type":"cpu"},"digest":{"algorithm":"sha256","files":{"__init__.py":"NuinjlWRNlWgg0y2D61HEaKtW/VOktRHP8w2Nj3opjQ=","szl_lambda_gate/__init__.py":"QpfkkacuKUGxXitqBYLq0nM4NnCsX7uNFOLHbeye0V0=","szl_lambda_gate/_lambda.py":"+VOjgBnJGm+oqcQR9VuqT18rnnbXFTu6VLuKXZRprzg=","szl_lambda_gate/_ops.py":"at8dxC56nm2pFOME2pcgQ45ZwjQQThHz5cyqRezuI7Y=","szl_lambda_gate/_v1.py":"msLY/gpH/xkeunbmKx6d2bK1T2S6DD2GMG4M3Vi8Row=","szl_lambda_gate/governed_norm/__init__.py":"UP5PsJoWXQrpp4Hw7zUVOIx1KhTN6V9zaHhU9Zp9+tw=","szl_lambda_gate/governed_norm/_norm.py":"z3Ks4oHshsUEQm914j7r22YaHjoS5N/MbIufpjSwOUI=","szl_lambda_gate/governed_norm/_receipt.py":"2xJEvRhK/9ypKb04rHKLJpb4KtVlqvgtFMo4RdTXSKk=","szl_lambda_gate/governed_norm/layers.py":"15yDNVyyMyudDp2YWEuGQ05IZTLifTBlLfEBWLMPw3s=","szl_lambda_gate/layers.py":"UdpHjhhe5NFtqplxvcvc/LsMDFt4N6ACuXzQXLlo6nU="}},"id":"_szl_lambda_gate_cpu_7cb79cba7ecb5dbb9da59b2d6b516a98d2a114d6","license":"Apache-2.0","name":"szl-lambda-gate","python-depends":[],"source":"https://github.com/szl-holdings/szl-lambda-gate","universal":false,"version":1}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
build/torch-cpu/szl_lambda_gate/__init__.py
CHANGED
|
@@ -1,172 +1,216 @@
|
|
| 1 |
-
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
-
# © 2026 SZL Holdings · Stephen P. Lutar · ORCID 0009-0001-0110-4173
|
| 3 |
-
"""szl_lambda_gate — the Lambda-Spine aggregator (Λ) as a universal kernel.
|
| 4 |
-
|
| 5 |
-
A pure-PyTorch (universal) kernel from SZL Holdings for the Hugging Face
|
| 6 |
-
Kernel Hub. It ports the canonical Λ aggregator into a differentiable,
|
| 7 |
-
torch.compile-friendly torch op:
|
| 8 |
-
|
| 9 |
-
Λ(x) = ∏ xᵢ^{wᵢ}, Σwᵢ = 1, wᵢ > 0, xᵢ ∈ [0,1] (weighted geometric mean)
|
| 10 |
-
|
| 11 |
-
plus an ADVISORY governance gate (Λ vs threshold), the four carried axioms as
|
| 12 |
-
real runtime self-checks, and pure nn.Module layers.
|
| 13 |
-
|
| 14 |
-
Load from the Hub:
|
| 15 |
-
|
| 16 |
-
import torch
|
| 17 |
-
from kernels import get_kernel
|
| 18 |
-
|
| 19 |
-
lg = get_kernel("SZLHOLDINGS/szl-lambda-gate")
|
| 20 |
-
axes = torch.tensor([0.9, 0.8, 0.95]) # axis scores in [0,1]
|
| 21 |
-
score = lg.lambda_aggregate(axes) # Λ(x) ∈ [0,1]
|
| 22 |
-
res = lg.lambda_gate(axes, threshold=0.5) # ADVISORY pass/fail
|
| 23 |
-
print(res.score, res.passed, res.advisory)
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
from
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
from .
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
from .
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
"
|
| 69 |
-
"
|
| 70 |
-
"
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
"
|
| 76 |
-
"
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
"
|
| 80 |
-
"
|
| 81 |
-
"
|
| 82 |
-
"
|
| 83 |
-
"
|
| 84 |
-
"
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
""
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
""
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
"""
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
"""
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
+
# © 2026 SZL Holdings · Stephen P. Lutar · ORCID 0009-0001-0110-4173
|
| 3 |
+
"""szl_lambda_gate — the Lambda-Spine aggregator (Λ) as a universal kernel.
|
| 4 |
+
|
| 5 |
+
A pure-PyTorch (universal) kernel from SZL Holdings for the Hugging Face
|
| 6 |
+
Kernel Hub. It ports the canonical Λ aggregator into a differentiable,
|
| 7 |
+
torch.compile-friendly torch op:
|
| 8 |
+
|
| 9 |
+
Λ(x) = ∏ xᵢ^{wᵢ}, Σwᵢ = 1, wᵢ > 0, xᵢ ∈ [0,1] (weighted geometric mean)
|
| 10 |
+
|
| 11 |
+
plus an ADVISORY governance gate (Λ vs threshold), the four carried axioms as
|
| 12 |
+
real runtime self-checks, and pure nn.Module layers.
|
| 13 |
+
|
| 14 |
+
Load from the Hub:
|
| 15 |
+
|
| 16 |
+
import torch
|
| 17 |
+
from kernels import get_kernel
|
| 18 |
+
|
| 19 |
+
lg = get_kernel("SZLHOLDINGS/szl-lambda-gate")
|
| 20 |
+
axes = torch.tensor([0.9, 0.8, 0.95]) # axis scores in [0,1]
|
| 21 |
+
score = lg.lambda_aggregate(axes) # Λ(x) ∈ [0,1]
|
| 22 |
+
res = lg.lambda_gate(axes, threshold=0.5) # ADVISORY pass/fail
|
| 23 |
+
print(res.score, res.passed, res.advisory)
|
| 24 |
+
|
| 25 |
+
# szl.lambda/v1 strict gate (spec/szl.lambda.v1.json): no clamping, no
|
| 26 |
+
# renormalisation, tau required; bad input -> verdict BLOCK with a code.
|
| 27 |
+
w = torch.tensor([0.4, 0.3, 0.3], dtype=torch.float64)
|
| 28 |
+
v1 = lg.lambda_v1_gate(axes.double(), w, tau=0.8)
|
| 29 |
+
print(v1.verdict, v1.code) # GO / NO_GO / ABSTAIN / BLOCK
|
| 30 |
+
|
| 31 |
+
WHAT Λ IS / IS NOT (HONESTY — SZL Holdings doctrine v11):
|
| 32 |
+
Λ is the weighted-geometric-mean aggregator — a non-compensatory, ADVISORY
|
| 33 |
+
way to roll axis scores in [0,1] into one number (any zeroed axis zeroes the
|
| 34 |
+
aggregate). It is NOT "proven trust" and NOT a closed theorem: Λ-uniqueness
|
| 35 |
+
remains Conjecture 1 (OPEN — an unresolved CAUCHY_ND step plus a missing
|
| 36 |
+
symmetry axiom). Label it honestly everywhere; a gate "pass" is advisory.
|
| 37 |
+
|
| 38 |
+
PROVENANCE: backed by the Lean 4 formalization szl-holdings/lutar-lean
|
| 39 |
+
(749 declarations / 14 axioms / 163 tracked sorries),
|
| 40 |
+
DOI 10.5281/zenodo.20434308 (lutar-lean). Λ uniqueness = Conjecture 1 (open).
|
| 41 |
+
"""
|
| 42 |
+
from typing import Optional
|
| 43 |
+
|
| 44 |
+
import torch
|
| 45 |
+
|
| 46 |
+
from . import layers # noqa: F401 (must be importable for Hub layer mapping)
|
| 47 |
+
# CONSOLIDATION (Wave D): the governed-norm universal kernel is folded in here
|
| 48 |
+
# as a subpackage so szl-lambda-gate is the ONE canonical kernels package. The
|
| 49 |
+
# source repo szl-holdings/szl-governed-norm is DEPRECATED and points here;
|
| 50 |
+
# nothing was deleted (additive, reversible copy). Λ stays Conjecture 1.
|
| 51 |
+
from . import governed_norm # noqa: F401 (folded-in governed normalization kernels)
|
| 52 |
+
from ._lambda import YUYAY_AXES, YUYAY_FLOORS, LambdaGateResult
|
| 53 |
+
from ._lambda import _resolve_threshold
|
| 54 |
+
from ._lambda import find_axiom_violation as _find_axiom_violation
|
| 55 |
+
from ._lambda import is_bounded_by_max as _is_bounded_by_max
|
| 56 |
+
from ._lambda import is_egyptian_exact as _is_egyptian_exact
|
| 57 |
+
from ._lambda import is_homogeneous as _is_homogeneous
|
| 58 |
+
from ._lambda import is_monotone as _is_monotone
|
| 59 |
+
from ._lambda import lambda_aggregate as _lambda_aggregate
|
| 60 |
+
from ._lambda import lambda_gate as _lambda_gate
|
| 61 |
+
from ._lambda import lambda_gate_batch as _lambda_gate_batch
|
| 62 |
+
from ._lambda import selfcheck as _selfcheck
|
| 63 |
+
from ._lambda import yuyay_weights as _yuyay_weights
|
| 64 |
+
# szl.lambda/v1 strict entry (spec/szl.lambda.v1.json): validated, coded, tau required.
|
| 65 |
+
from ._v1 import LambdaV1Error, LambdaV1GateResult, lambda_v1, lambda_v1_gate
|
| 66 |
+
|
| 67 |
+
__all__ = [
|
| 68 |
+
"lambda_aggregate",
|
| 69 |
+
"lambda_gate",
|
| 70 |
+
"lambda_gate_batch",
|
| 71 |
+
"LambdaGateResult",
|
| 72 |
+
"lambda_v1",
|
| 73 |
+
"lambda_v1_gate",
|
| 74 |
+
"LambdaV1Error",
|
| 75 |
+
"LambdaV1GateResult",
|
| 76 |
+
"is_monotone",
|
| 77 |
+
"is_egyptian_exact",
|
| 78 |
+
"is_bounded_by_max",
|
| 79 |
+
"is_homogeneous",
|
| 80 |
+
"find_axiom_violation",
|
| 81 |
+
"selfcheck",
|
| 82 |
+
"yuyay_weights",
|
| 83 |
+
"YUYAY_AXES",
|
| 84 |
+
"YUYAY_FLOORS",
|
| 85 |
+
"layers",
|
| 86 |
+
"DOCTRINE_FOOTER",
|
| 87 |
+
"PROVENANCE",
|
| 88 |
+
"__version__",
|
| 89 |
+
# ---- folded-in governed-norm kernels (Wave D consolidation) ----
|
| 90 |
+
"governed_norm",
|
| 91 |
+
"rms_norm",
|
| 92 |
+
"layer_norm",
|
| 93 |
+
"fused_add_rms_norm",
|
| 94 |
+
]
|
| 95 |
+
|
| 96 |
+
# ---- folded-in governed-norm surface (Wave D consolidation) ---------------- #
|
| 97 |
+
# Convenience top-level re-exports of the governed normalization kernels that
|
| 98 |
+
# were absorbed from szl-governed-norm. The full surface (ReceiptChain,
|
| 99 |
+
# emit_receipt, receipt_* helpers, selfcheck, layers) lives under
|
| 100 |
+
# ``szl_lambda_gate.governed_norm``. These are a DIFFERENT kernel family from Λ
|
| 101 |
+
# (normalization, not the Λ aggregator); Λ itself remains Conjecture 1
|
| 102 |
+
# (advisory, uniqueness OPEN) and is never described as proven trust.
|
| 103 |
+
rms_norm = governed_norm.rms_norm
|
| 104 |
+
layer_norm = governed_norm.layer_norm
|
| 105 |
+
fused_add_rms_norm = governed_norm.fused_add_rms_norm
|
| 106 |
+
|
| 107 |
+
__version__ = "0.2.0"
|
| 108 |
+
DOCTRINE_FOOTER = (
|
| 109 |
+
"SZL Holdings · Λ = Conjecture 1 (ADVISORY, weighted geometric mean) · "
|
| 110 |
+
"uniqueness OPEN · NOT proven trust · honesty over checklist"
|
| 111 |
+
)
|
| 112 |
+
PROVENANCE = {
|
| 113 |
+
"lean_repo": "szl-holdings/lutar-lean",
|
| 114 |
+
"lean_declarations": 749,
|
| 115 |
+
"lean_axioms": 14,
|
| 116 |
+
"lean_tracked_sorries": 163,
|
| 117 |
+
"doi_lutar_lean": "10.5281/zenodo.20434308",
|
| 118 |
+
"lambda_status": "Conjecture 1 (open) — uniqueness unproven; advisory only",
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
def lambda_aggregate(
|
| 123 |
+
axes: torch.Tensor,
|
| 124 |
+
weights: Optional[torch.Tensor] = None,
|
| 125 |
+
) -> torch.Tensor:
|
| 126 |
+
"""Λ(x) = ∏ xᵢ^{wᵢ}, the weighted geometric mean over the last dim of axes.
|
| 127 |
+
|
| 128 |
+
See ``szl_lambda_gate._lambda.lambda_aggregate``. Axis scores in [0,1],
|
| 129 |
+
uniform weights when ``weights`` is None. Differentiable, batched, and
|
| 130 |
+
torch.compile-friendly. ADVISORY — NOT proven trust.
|
| 131 |
+
"""
|
| 132 |
+
return _lambda_aggregate(axes, weights=weights)
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
def lambda_gate(
|
| 136 |
+
axes: torch.Tensor,
|
| 137 |
+
weights: Optional[torch.Tensor] = None,
|
| 138 |
+
threshold: Optional[float] = None,
|
| 139 |
+
) -> LambdaGateResult:
|
| 140 |
+
"""ADVISORY Λ governance gate: returns LambdaGateResult(score, passed,
|
| 141 |
+
threshold, advisory). ``passed`` = Λ(axes) >= threshold. ``threshold`` must
|
| 142 |
+
lie within Λ's range [0,1] (a value outside it is a misconfiguration — a
|
| 143 |
+
negative threshold would advisory-pass a fully-failing Λ=0 candidate — and
|
| 144 |
+
is rejected). Omitting it uses the legacy 0.5 with a DeprecationWarning
|
| 145 |
+
(policy_tau is 0.8); pass it, or use ``lambda_v1_gate(axes, weights, tau)``.
|
| 146 |
+
A pass is an advisory, non-compensatory signal — NOT proven trust
|
| 147 |
+
(Λ = Conjecture 1).
|
| 148 |
+
"""
|
| 149 |
+
threshold = _resolve_threshold(threshold, stacklevel=2)
|
| 150 |
+
return _lambda_gate(axes, weights=weights, threshold=threshold)
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
def lambda_gate_batch(
|
| 154 |
+
candidates: torch.Tensor,
|
| 155 |
+
weights: Optional[torch.Tensor] = None,
|
| 156 |
+
threshold: Optional[float] = None,
|
| 157 |
+
) -> LambdaGateResult:
|
| 158 |
+
"""ADVISORY batch gate over many candidate action-vectors (shape (..., N, k)).
|
| 159 |
+
|
| 160 |
+
The realistic per-inference-step call: score all N candidates at once and
|
| 161 |
+
return the advisory pass mask. Returns LambdaGateResult(score, passed,
|
| 162 |
+
threshold, advisory) with score/passed of shape (..., N). ``threshold``
|
| 163 |
+
must lie within Λ's range [0,1] (same domain guard as ``lambda_gate``);
|
| 164 |
+
omitting it uses the legacy 0.5 with a DeprecationWarning.
|
| 165 |
+
NOT proven trust.
|
| 166 |
+
"""
|
| 167 |
+
threshold = _resolve_threshold(threshold, stacklevel=2)
|
| 168 |
+
return _lambda_gate_batch(candidates, weights=weights, threshold=threshold)
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
def yuyay_weights(dtype: torch.dtype = torch.float64, device=None) -> torch.Tensor:
|
| 172 |
+
"""Canonical 13-axis Yuyay Λ weight vector (uniform 1/13), ADVISORY only.
|
| 173 |
+
|
| 174 |
+
Use as ``weights`` over the 13 ``YUYAY_AXES``. The yuyay_v3 gate is a
|
| 175 |
+
conjunctive AND with per-axis floors (``YUYAY_FLOORS``); this Λ roll-up is
|
| 176 |
+
the weighted geometric mean and is ADVISORY — NOT proven trust.
|
| 177 |
+
"""
|
| 178 |
+
return _yuyay_weights(dtype=dtype, device=device)
|
| 179 |
+
|
| 180 |
+
|
| 181 |
+
def find_axiom_violation(k=5, trials=200, weights=None, seed=0, tol=1e-6):
|
| 182 |
+
"""Random-search for any A1–A4 violation; returns (axiom, axes, weights) or
|
| 183 |
+
None. An honest falsification attempt — finding nothing is evidence, not a
|
| 184 |
+
proof (Λ-uniqueness is Conjecture 1, open).
|
| 185 |
+
"""
|
| 186 |
+
return _find_axiom_violation(k=k, trials=trials, weights=weights, seed=seed, tol=tol)
|
| 187 |
+
|
| 188 |
+
|
| 189 |
+
def selfcheck(k=5, trials=64, seed=0) -> dict:
|
| 190 |
+
"""Expose the A1–A4 empirical self-checks + version as a single verdict dict.
|
| 191 |
+
|
| 192 |
+
Callable as get_kernel(...).selfcheck(). EMPIRICAL checks on sampled inputs,
|
| 193 |
+
NOT a proof of Λ-uniqueness (Conjecture 1, open). Advisory only.
|
| 194 |
+
"""
|
| 195 |
+
return _selfcheck(k=k, trials=trials, seed=seed)
|
| 196 |
+
|
| 197 |
+
|
| 198 |
+
# ---- axiom runtime self-checks (real, verifiable; NOT a uniqueness proof) -- #
|
| 199 |
+
def is_monotone(axes, weights=None, delta=0.05, tol=1e-7) -> bool:
|
| 200 |
+
"""A1 IsMonotone self-check: Λ is non-decreasing in each axis (on this data)."""
|
| 201 |
+
return _is_monotone(axes, weights=weights, delta=delta, tol=tol)
|
| 202 |
+
|
| 203 |
+
|
| 204 |
+
def is_egyptian_exact(c, k=3, weights=None, tol=1e-5) -> bool:
|
| 205 |
+
"""A3 IsEgyptianExact self-check: Λ(c, …, c) = c."""
|
| 206 |
+
return _is_egyptian_exact(c, k=k, weights=weights, tol=tol)
|
| 207 |
+
|
| 208 |
+
|
| 209 |
+
def is_bounded_by_max(axes, weights=None, tol=1e-6) -> bool:
|
| 210 |
+
"""A4 IsBounded self-check: Λ(x) ≤ maxᵢ xᵢ."""
|
| 211 |
+
return _is_bounded_by_max(axes, weights=weights, tol=tol)
|
| 212 |
+
|
| 213 |
+
|
| 214 |
+
def is_homogeneous(axes, t, weights=None, tol=1e-5) -> bool:
|
| 215 |
+
"""A2 IsHomogeneous(degree 1) self-check: Λ(t·x) = t·Λ(x)."""
|
| 216 |
+
return _is_homogeneous(axes, t, weights=weights, tol=tol)
|
build/torch-cpu/szl_lambda_gate/_lambda.py
CHANGED
|
@@ -1,313 +1,556 @@
|
|
| 1 |
-
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
-
# © 2026 SZL Holdings · Stephen P. Lutar · ORCID 0009-0001-0110-4173
|
| 3 |
-
"""Pure-PyTorch Lambda-Spine aggregator (Λ) for the szl-lambda-gate kernel.
|
| 4 |
-
|
| 5 |
-
Λ(x) = ∏ xᵢ^{wᵢ}, Σwᵢ = 1, wᵢ > 0, xᵢ ∈ [0,1] (weighted geometric mean)
|
| 6 |
-
|
| 7 |
-
This is a TORCH port of the canonical pure-Python reference
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
if
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
)
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
):
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
)
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
|
| 248 |
-
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
"
|
| 268 |
-
)
|
| 269 |
-
|
| 270 |
-
|
| 271 |
-
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
|
| 275 |
-
|
| 276 |
-
|
| 277 |
-
|
| 278 |
-
|
| 279 |
-
|
| 280 |
-
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
|
| 312 |
-
"
|
| 313 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
+
# © 2026 SZL Holdings · Stephen P. Lutar · ORCID 0009-0001-0110-4173
|
| 3 |
+
"""Pure-PyTorch Lambda-Spine aggregator (Λ) for the szl-lambda-gate kernel.
|
| 4 |
+
|
| 5 |
+
Λ(x) = ∏ xᵢ^{wᵢ}, Σwᵢ = 1, wᵢ > 0, xᵢ ∈ [0,1] (weighted geometric mean)
|
| 6 |
+
|
| 7 |
+
This is a TORCH port of the canonical pure-Python reference
|
| 8 |
+
(packages/puriq-os/puriq_os/lambda_aggregator.py — saved alongside this kernel
|
| 9 |
+
as lambda_aggregator_source.py). It is a correctness reference, computed via
|
| 10 |
+
logs in float32 for stability, differentiable (autograd works), and
|
| 11 |
+
torch.compile-friendly. Depends ONLY on torch + the Python standard library
|
| 12 |
+
(a Kernel Hub requirement for universal kernels).
|
| 13 |
+
|
| 14 |
+
WHAT Λ IS / IS NOT (HONESTY — SZL Holdings doctrine v11):
|
| 15 |
+
Λ is the *weighted-geometric-mean aggregator*: a non-compensatory way to
|
| 16 |
+
combine axis scores in [0,1] into one number. It is ADVISORY governance
|
| 17 |
+
signal — a conservative roll-up where any single zeroed axis drives the
|
| 18 |
+
aggregate to 0. It is NOT "proven trust" and NOT a closed theorem. Its
|
| 19 |
+
*uniqueness* (that the weighted geometric mean is the only aggregator
|
| 20 |
+
satisfying the carried axioms) remains Conjecture 1 — OPEN (an unresolved
|
| 21 |
+
CAUCHY_ND step plus a missing symmetry axiom in the Lean development). Do
|
| 22 |
+
not describe Λ as proven trust anywhere.
|
| 23 |
+
|
| 24 |
+
PRIOR ART (honest attribution): the weighted geometric mean as a *less-
|
| 25 |
+
compensatory* composite-indicator aggregator is established practice — the
|
| 26 |
+
UN HDI (arithmetic→geometric switch, 2010), the OECD Handbook on
|
| 27 |
+
Constructing Composite Indicators (2008), and the UNECE well-being
|
| 28 |
+
guidelines all use it "to limit the compensation effect". The veto / cut-off
|
| 29 |
+
idea (a single failing criterion blocks a pass regardless of the others) is
|
| 30 |
+
the ELECTRE veto threshold / "satisficing" minimum-threshold screen. The
|
| 31 |
+
13-axis conjunctive form exposed by :func:`yuyay_weights` is SZL's own
|
| 32 |
+
yuyay_v3 "Heart" gate. None of this makes Λ "proven trust"; the gate is
|
| 33 |
+
ADVISORY (a11oy: "the advisory Λ trust score is a research conjecture, not a
|
| 34 |
+
pass/fail oracle").
|
| 35 |
+
|
| 36 |
+
PROVENANCE: backed by the Lean 4 formalization szl-holdings/lutar-lean
|
| 37 |
+
(749 declarations / 14 axioms / 163 tracked sorries),
|
| 38 |
+
DOI 10.5281/zenodo.20434308 (lutar-lean).
|
| 39 |
+
Λ uniqueness = Conjecture 1 (open).
|
| 40 |
+
|
| 41 |
+
Axioms carried (Lutar/Axioms.lean), available below as runtime self-checks:
|
| 42 |
+
A1 IsMonotone — Λ is non-decreasing in each axis
|
| 43 |
+
A2 IsHomogeneous — Λ(t·x) = t·Λ(x) (degree 1)
|
| 44 |
+
A3 IsEgyptianExact — Λ(c,…,c) = c (the uniform-diagonal fixpoint)
|
| 45 |
+
A4 IsBounded(by max) — Λ(x) ≤ maxᵢ xᵢ
|
| 46 |
+
"""
|
| 47 |
+
import warnings
|
| 48 |
+
from typing import Optional
|
| 49 |
+
|
| 50 |
+
import torch
|
| 51 |
+
|
| 52 |
+
# ---- the deprecated implicit threshold ------------------------------------- #
|
| 53 |
+
# lambda_gate / lambda_gate_batch / layers.LambdaGate used to default to 0.5.
|
| 54 |
+
# That default stays, so there is no behaviour change, but omitting the
|
| 55 |
+
# threshold now raises a DeprecationWarning. The admit policy value is
|
| 56 |
+
# policy_tau in frontier/model_admit_contract.v1.json, and the strict
|
| 57 |
+
# szl.lambda/v1 gate (_v1.lambda_v1_gate) takes tau as a required argument.
|
| 58 |
+
_LEGACY_DEFAULT_THRESHOLD = 0.5
|
| 59 |
+
_DEFAULT_THRESHOLD_WARNING = (
|
| 60 |
+
"default threshold 0.5 differs from policy_tau 0.8; pass tau. Omitting the "
|
| 61 |
+
"threshold of lambda_gate / lambda_gate_batch / LambdaGate is deprecated: "
|
| 62 |
+
"pass threshold= explicitly (policy_tau is in "
|
| 63 |
+
"frontier/model_admit_contract.v1.json), or use the strict "
|
| 64 |
+
"lambda_v1_gate(axes, weights, tau). The legacy default 0.5 still applies."
|
| 65 |
+
)
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
def _resolve_threshold(threshold: Optional[float], stacklevel: int) -> float:
|
| 69 |
+
"""``None`` (the threshold was omitted) -> the legacy 0.5, with a DeprecationWarning.
|
| 70 |
+
|
| 71 |
+
``stacklevel`` is what the calling entry point would pass to
|
| 72 |
+
``warnings.warn`` itself (2 = its caller), so the warning names the line
|
| 73 |
+
that omitted the threshold. Dynamo cannot trace ``warnings.warn`` (a
|
| 74 |
+
fullgraph compile would fail), so the warning is skipped while
|
| 75 |
+
``torch.compile`` traces; the default is applied either way.
|
| 76 |
+
"""
|
| 77 |
+
if threshold is not None:
|
| 78 |
+
return threshold
|
| 79 |
+
if not bool(getattr(torch.compiler, "is_compiling", lambda: False)()):
|
| 80 |
+
warnings.warn(_DEFAULT_THRESHOLD_WARNING, DeprecationWarning, stacklevel=stacklevel + 1)
|
| 81 |
+
return _LEGACY_DEFAULT_THRESHOLD
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
# Compute reductions/log-sum in float32 for stability when inputs are low
|
| 85 |
+
# precision; keep float64 inputs in float64 (downcasting would break gradcheck
|
| 86 |
+
# and silently lose precision).
|
| 87 |
+
_SUPPORTED_DTYPES = (torch.float16, torch.bfloat16, torch.float32, torch.float64)
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
def _compute_dtype(in_dtype: torch.dtype) -> torch.dtype:
|
| 91 |
+
return torch.float32 if in_dtype in (torch.float16, torch.bfloat16) else in_dtype
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
def _check_axes(axes: torch.Tensor) -> None:
|
| 95 |
+
"""Cheap, allocation-free metadata guards on the axis-score tensor.
|
| 96 |
+
|
| 97 |
+
Inspects only type / dtype / rank / last-dim, so it constant-folds under
|
| 98 |
+
torch.compile and adds no tensor work on the happy path.
|
| 99 |
+
"""
|
| 100 |
+
if not isinstance(axes, torch.Tensor):
|
| 101 |
+
raise TypeError(f"axes must be a torch.Tensor, got {type(axes).__name__}")
|
| 102 |
+
if axes.dtype not in _SUPPORTED_DTYPES:
|
| 103 |
+
raise TypeError(
|
| 104 |
+
f"axes has unsupported dtype {axes.dtype}; "
|
| 105 |
+
f"expected one of {tuple(str(d) for d in _SUPPORTED_DTYPES)}"
|
| 106 |
+
)
|
| 107 |
+
if axes.dim() < 1:
|
| 108 |
+
raise ValueError(
|
| 109 |
+
"axes must have at least 1 dimension (the k axis scores live on "
|
| 110 |
+
f"the last dim); got a {axes.dim()}-d tensor"
|
| 111 |
+
)
|
| 112 |
+
if axes.shape[-1] < 1:
|
| 113 |
+
raise ValueError("axes last dimension (k = number of axes) must be >= 1")
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
def _resolve_weights(
|
| 117 |
+
axes: torch.Tensor,
|
| 118 |
+
weights: Optional[torch.Tensor],
|
| 119 |
+
cdt: torch.dtype,
|
| 120 |
+
) -> torch.Tensor:
|
| 121 |
+
"""Return a normalized (Σw = 1) weight vector of shape (k,) in compute dtype.
|
| 122 |
+
|
| 123 |
+
``weights=None`` -> uniform 1/k (the Egyptian-exact diagonal). Otherwise the
|
| 124 |
+
weights must be 1-D of length k, strictly positive, with a positive sum;
|
| 125 |
+
they are normalized so Σwᵢ = 1.
|
| 126 |
+
"""
|
| 127 |
+
k = axes.shape[-1]
|
| 128 |
+
if weights is None:
|
| 129 |
+
return torch.full((k,), 1.0 / k, dtype=cdt, device=axes.device)
|
| 130 |
+
if not isinstance(weights, torch.Tensor):
|
| 131 |
+
raise TypeError(f"weights must be a torch.Tensor or None, got {type(weights).__name__}")
|
| 132 |
+
if weights.device != axes.device:
|
| 133 |
+
raise ValueError(
|
| 134 |
+
f"weights is on device {weights.device} but axes is on {axes.device}; "
|
| 135 |
+
"move them to the same device"
|
| 136 |
+
)
|
| 137 |
+
if weights.dim() != 1 or weights.shape[0] != k:
|
| 138 |
+
raise ValueError(
|
| 139 |
+
f"weights must be 1-D with shape ({k},) to match the last dim of axes; "
|
| 140 |
+
f"got shape {tuple(weights.shape)}"
|
| 141 |
+
)
|
| 142 |
+
wf = weights.to(cdt)
|
| 143 |
+
compiling = bool(getattr(torch.compiler, "is_compiling", lambda: False)())
|
| 144 |
+
if not compiling:
|
| 145 |
+
if not bool(torch.all(torch.isfinite(wf))):
|
| 146 |
+
raise ValueError("weights must all be finite (no NaN/Inf)")
|
| 147 |
+
if bool(torch.any(wf <= 0.0)):
|
| 148 |
+
raise ValueError("weights must be strictly positive (wᵢ > 0)")
|
| 149 |
+
sw = wf.sum()
|
| 150 |
+
if not bool(sw > 0.0):
|
| 151 |
+
raise ValueError("weights must sum to a positive value")
|
| 152 |
+
return wf / sw
|
| 153 |
+
# Compiled path stays in tensor-land. Non-positive weights are a misuse;
|
| 154 |
+
# clamp them away from zero so the graph does not break, then normalize.
|
| 155 |
+
# This path is outside szl.lambda/v1; _v1.lambda_v1 validates before it
|
| 156 |
+
# gets here and is not meant to run under torch.compile.
|
| 157 |
+
wf = torch.where(torch.isfinite(wf), wf, torch.ones_like(wf))
|
| 158 |
+
wf = torch.clamp(wf, min=torch.finfo(wf.dtype).tiny)
|
| 159 |
+
return wf / wf.sum()
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
def lambda_aggregate(
|
| 163 |
+
axes: torch.Tensor,
|
| 164 |
+
weights: Optional[torch.Tensor] = None,
|
| 165 |
+
) -> torch.Tensor:
|
| 166 |
+
"""Weighted geometric mean Λ(x) = ∏ xᵢ^{wᵢ} over the last dim of ``axes``.
|
| 167 |
+
|
| 168 |
+
Λ is the (ADVISORY) Lambda-Spine aggregator. Axis scores are expected in
|
| 169 |
+
[0,1] and are clamped into [0,1]; uniform weights (1/k) are used when
|
| 170 |
+
``weights`` is None — the Egyptian-exact diagonal. Computed via logs in
|
| 171 |
+
float32 (or float64 for float64 inputs) for numerical stability:
|
| 172 |
+
|
| 173 |
+
Λ(x) = exp( Σᵢ wᵢ · log(clamp(xᵢ, 0, 1)) )
|
| 174 |
+
|
| 175 |
+
Non-compensatory zero-routing (A4-consistent): any axis that is zero, OR
|
| 176 |
+
that is NON-FINITE (NaN / ±Inf), is treated as a FAILING axis and drives
|
| 177 |
+
the whole aggregate to exactly 0. This is the conservative governance
|
| 178 |
+
choice — a garbage/invalid axis must never silently pass as a "perfect"
|
| 179 |
+
(clamped-to-1) axis, and the output (and its gradient) stay finite and in
|
| 180 |
+
[0,1] for every input. Zeros/non-finite axes are routed explicitly so
|
| 181 |
+
log(0) = -inf and log(NaN) = NaN never produce a NaN value or gradient.
|
| 182 |
+
|
| 183 |
+
Args:
|
| 184 |
+
axes: tensor of shape (..., k) of axis scores in [0,1]. Batched:
|
| 185 |
+
the reduction is over the last dim, leading dims are batch.
|
| 186 |
+
weights: optional 1-D tensor of shape (k,); None -> uniform. Normalized
|
| 187 |
+
internally so Σwᵢ = 1.
|
| 188 |
+
|
| 189 |
+
Returns:
|
| 190 |
+
tensor of shape (...) — Λ(x) ∈ [0,1] per batch row. Differentiable
|
| 191 |
+
w.r.t. ``axes`` (and ``weights``).
|
| 192 |
+
|
| 193 |
+
NOT the szl.lambda/v1 contract: this function clamps, zero-routes NaN/±Inf
|
| 194 |
+
and renormalises (pinned in tests/test_lambda_v1_torch_divergence.py). For
|
| 195 |
+
validated, coded errors use :func:`szl_lambda_gate._v1.lambda_v1`.
|
| 196 |
+
|
| 197 |
+
HONESTY: this is a non-compensatory governance roll-up, NOT proven trust.
|
| 198 |
+
Λ-uniqueness is Conjecture 1 (open).
|
| 199 |
+
"""
|
| 200 |
+
_check_axes(axes)
|
| 201 |
+
in_dtype = axes.dtype
|
| 202 |
+
cdt = _compute_dtype(in_dtype)
|
| 203 |
+
xf = axes.to(cdt)
|
| 204 |
+
w = _resolve_weights(axes, weights, cdt) # (k,), Σw=1
|
| 205 |
+
|
| 206 |
+
# A "bad" axis is one that fails non-compensatorily: a non-positive score
|
| 207 |
+
# OR a non-finite value (NaN / ±Inf). clamp(+inf)=1 would otherwise count a
|
| 208 |
+
# garbage axis as perfect, and clamp(NaN)=NaN would poison the product — we
|
| 209 |
+
# treat BOTH as failing (zeroing) axes. Detect non-finite on the RAW input.
|
| 210 |
+
finite_mask = torch.isfinite(xf)
|
| 211 |
+
xc = xf.clamp(0.0, 1.0)
|
| 212 |
+
bad_mask = (~finite_mask) | (xc <= 0.0)
|
| 213 |
+
any_bad = torch.any(bad_mask, dim=-1) # (...)
|
| 214 |
+
|
| 215 |
+
# Replace bad axes with 1.0 before the log purely to keep log finite and the
|
| 216 |
+
# gradient well-defined; the bad-axis contribution is reinstated via any_bad.
|
| 217 |
+
safe = torch.where(bad_mask, torch.ones_like(xc), xc)
|
| 218 |
+
logx = torch.log(safe) # (..., k)
|
| 219 |
+
acc = (logx * w).sum(dim=-1) # (...) weighted log-sum
|
| 220 |
+
val = torch.exp(acc) # (...) Λ before zero-routing
|
| 221 |
+
|
| 222 |
+
out = torch.where(any_bad, torch.zeros_like(val), val)
|
| 223 |
+
out = out.clamp(0.0, 1.0)
|
| 224 |
+
return out.to(in_dtype)
|
| 225 |
+
|
| 226 |
+
|
| 227 |
+
def lambda_gate(
|
| 228 |
+
axes: torch.Tensor,
|
| 229 |
+
weights: Optional[torch.Tensor] = None,
|
| 230 |
+
threshold: Optional[float] = None,
|
| 231 |
+
):
|
| 232 |
+
"""ADVISORY governance gate over Λ(x): score plus a pass/fail vs threshold.
|
| 233 |
+
|
| 234 |
+
Computes Λ(x) (see :func:`lambda_aggregate`) and compares it to
|
| 235 |
+
``threshold``: pass := Λ(x) >= threshold.
|
| 236 |
+
|
| 237 |
+
DEPRECATED DEFAULT: omitting ``threshold`` (or passing ``None``) still uses
|
| 238 |
+
the legacy 0.5 but raises a ``DeprecationWarning``, because 0.5 differs
|
| 239 |
+
from the admit contract's policy_tau (0.8). Pass the threshold explicitly,
|
| 240 |
+
or use the strict szl.lambda/v1 gate
|
| 241 |
+
:func:`szl_lambda_gate._v1.lambda_v1_gate`, where tau is required.
|
| 242 |
+
|
| 243 |
+
``threshold`` must be a finite float within Λ's range ``[0, 1]`` (Λ is the
|
| 244 |
+
weighted geometric mean over [0,1]). This bound is enforced: a threshold
|
| 245 |
+
below 0 or above 1 is meaningless for the advisory gate and is rejected —
|
| 246 |
+
see the non-compensatory rationale below. The domain edges are valid:
|
| 247 |
+
``0.0`` admits every candidate (a permissive "no-gate" boundary) and
|
| 248 |
+
``1.0`` admits only a Λ == 1 candidate.
|
| 249 |
+
|
| 250 |
+
Returns a :class:`LambdaGateResult` namedtuple with fields:
|
| 251 |
+
score — Λ(x) tensor of shape (...), in [0,1]
|
| 252 |
+
passed — boolean tensor of shape (...), Λ(x) >= threshold
|
| 253 |
+
threshold — the float threshold used
|
| 254 |
+
advisory — always True; a STANDING reminder that this is a
|
| 255 |
+
non-compensatory governance signal, NOT proven trust.
|
| 256 |
+
|
| 257 |
+
Non-compensatory threshold hardening: because a failing/garbage candidate
|
| 258 |
+
(a zero, NaN, or ±Inf axis) is routed to Λ = 0, a NEGATIVE threshold would
|
| 259 |
+
advisory-"pass" exactly those fully-failing candidates (0 >= t for t < 0) —
|
| 260 |
+
the opposite of a conservative admission gate. A threshold above 1 can
|
| 261 |
+
never pass. Both are misconfigurations, so the [0,1] domain is enforced up
|
| 262 |
+
front rather than silently producing a wrong pass mask.
|
| 263 |
+
|
| 264 |
+
HONESTY: a "pass" is an ADVISORY signal only. Λ is the weighted-geometric-
|
| 265 |
+
mean aggregator; its uniqueness is Conjecture 1 (open). Do not treat a
|
| 266 |
+
pass as proven trust or a closed theorem.
|
| 267 |
+
"""
|
| 268 |
+
threshold = _resolve_threshold(threshold, stacklevel=2)
|
| 269 |
+
t = float(threshold)
|
| 270 |
+
if t != t or t == float("inf") or t == float("-inf"):
|
| 271 |
+
raise ValueError(f"threshold must be a finite float, got {threshold!r}")
|
| 272 |
+
if t < 0.0 or t > 1.0:
|
| 273 |
+
raise ValueError(
|
| 274 |
+
"threshold must be within Λ's range [0, 1] (Λ is the weighted "
|
| 275 |
+
f"geometric mean over [0,1]); got {t!r}. A threshold below 0 would "
|
| 276 |
+
"advisory-pass a fully-failing (Λ=0) candidate and one above 1 can "
|
| 277 |
+
"never pass — both signal a misconfigured gate."
|
| 278 |
+
)
|
| 279 |
+
score = lambda_aggregate(axes, weights)
|
| 280 |
+
passed = score >= t
|
| 281 |
+
return LambdaGateResult(score=score, passed=passed, threshold=t, advisory=True)
|
| 282 |
+
|
| 283 |
+
|
| 284 |
+
def lambda_gate_batch(
|
| 285 |
+
candidates: torch.Tensor,
|
| 286 |
+
weights: Optional[torch.Tensor] = None,
|
| 287 |
+
threshold: Optional[float] = None,
|
| 288 |
+
):
|
| 289 |
+
"""ADVISORY batch gate: score MANY candidate action-vectors in one call.
|
| 290 |
+
|
| 291 |
+
This is the realistic way a model/agent uses the gate — one call per
|
| 292 |
+
inference step that scores every proposed action-vector at once and returns
|
| 293 |
+
the advisory pass mask (which candidates clear the threshold).
|
| 294 |
+
|
| 295 |
+
``candidates`` is a tensor of shape (..., N, k): the last dim ``k`` holds
|
| 296 |
+
the per-axis scores of a single candidate, and the second-to-last dim ``N``
|
| 297 |
+
enumerates the candidates (any leading dims are extra batch). Equivalent to
|
| 298 |
+
calling :func:`lambda_gate` on the whole tensor — the reduction is over the
|
| 299 |
+
last dim — but named to make the agent-loop intent explicit. ``threshold``
|
| 300 |
+
inherits the same [0,1] domain guard as :func:`lambda_gate` (a threshold
|
| 301 |
+
outside Λ's range is a misconfiguration and is rejected). Omitting it uses
|
| 302 |
+
the legacy 0.5 with a ``DeprecationWarning``, as in :func:`lambda_gate`.
|
| 303 |
+
|
| 304 |
+
Returns a :class:`LambdaGateResult` with:
|
| 305 |
+
score — Λ tensor of shape (..., N), one score per candidate
|
| 306 |
+
passed — boolean mask of shape (..., N): score >= threshold
|
| 307 |
+
threshold — the float threshold used
|
| 308 |
+
advisory — always True (NOT proven trust)
|
| 309 |
+
|
| 310 |
+
HONESTY: the pass mask is an ADVISORY, non-compensatory signal. A "pass"
|
| 311 |
+
is not proven trust; Λ-uniqueness is Conjecture 1 (open).
|
| 312 |
+
"""
|
| 313 |
+
threshold = _resolve_threshold(threshold, stacklevel=2)
|
| 314 |
+
_check_axes(candidates)
|
| 315 |
+
if candidates.dim() < 2:
|
| 316 |
+
raise ValueError(
|
| 317 |
+
"candidates must be at least 2-D, shape (..., N, k): the last dim is "
|
| 318 |
+
f"the k axis scores and the one before it enumerates the N candidates; "
|
| 319 |
+
f"got a {candidates.dim()}-d tensor"
|
| 320 |
+
)
|
| 321 |
+
# Reuse the single-call gate — its reduction over the last dim already gives
|
| 322 |
+
# one score per candidate, so the (..., N) layout falls out for free.
|
| 323 |
+
return lambda_gate(candidates, weights=weights, threshold=threshold)
|
| 324 |
+
|
| 325 |
+
|
| 326 |
+
# ---- A1..A4 axiom RUNTIME self-checks (real, verifiable) ------------------- #
|
| 327 |
+
# These are honest empirical checks callers can run on concrete inputs. They
|
| 328 |
+
# verify the carried axioms hold for THIS implementation on the given data —
|
| 329 |
+
# they are NOT a proof of Λ-uniqueness (that is Conjecture 1, open).
|
| 330 |
+
|
| 331 |
+
def is_egyptian_exact(
|
| 332 |
+
c: float,
|
| 333 |
+
k: int = 3,
|
| 334 |
+
weights: Optional[torch.Tensor] = None,
|
| 335 |
+
tol: float = 1e-5,
|
| 336 |
+
) -> bool:
|
| 337 |
+
"""A3 IsEgyptianExact: Λ(c, …, c) = c for a constant axis vector of length k.
|
| 338 |
+
|
| 339 |
+
Builds the uniform vector (c repeated k times) and checks Λ equals c within
|
| 340 |
+
``tol``. ``c`` is clamped into [0,1] to match the aggregator's domain.
|
| 341 |
+
"""
|
| 342 |
+
if k < 1:
|
| 343 |
+
raise ValueError("k must be >= 1")
|
| 344 |
+
cc = min(max(float(c), 0.0), 1.0)
|
| 345 |
+
axes = torch.full((k,), cc, dtype=torch.float64)
|
| 346 |
+
val = lambda_aggregate(axes, weights)
|
| 347 |
+
return bool(torch.abs(val - cc) <= tol)
|
| 348 |
+
|
| 349 |
+
|
| 350 |
+
def is_bounded_by_max(
|
| 351 |
+
axes: torch.Tensor,
|
| 352 |
+
weights: Optional[torch.Tensor] = None,
|
| 353 |
+
tol: float = 1e-6,
|
| 354 |
+
) -> bool:
|
| 355 |
+
"""A4 IsBounded: Λ(x) ≤ maxᵢ xᵢ (over the last dim), within ``tol``.
|
| 356 |
+
|
| 357 |
+
Returns True iff the bound holds for every batch row. Non-finite axis
|
| 358 |
+
values are clamped/zero-routed the same way the aggregator treats them, so
|
| 359 |
+
the bound is checked on the conservative (finite) domain.
|
| 360 |
+
"""
|
| 361 |
+
_check_axes(axes)
|
| 362 |
+
val = lambda_aggregate(axes, weights) # (...)
|
| 363 |
+
xf = axes.to(_compute_dtype(axes.dtype))
|
| 364 |
+
# Mirror the aggregator: non-finite axes are failing (treated as 0) for the
|
| 365 |
+
# purposes of the max bound, so the check matches the routed semantics.
|
| 366 |
+
xf = torch.where(torch.isfinite(xf), xf, torch.zeros_like(xf))
|
| 367 |
+
mx = xf.clamp(0.0, 1.0).amax(dim=-1) # (...)
|
| 368 |
+
return bool(torch.all(val.to(mx.dtype) <= mx + tol))
|
| 369 |
+
|
| 370 |
+
|
| 371 |
+
def is_homogeneous(
|
| 372 |
+
axes: torch.Tensor,
|
| 373 |
+
t: float,
|
| 374 |
+
weights: Optional[torch.Tensor] = None,
|
| 375 |
+
tol: float = 1e-5,
|
| 376 |
+
) -> bool:
|
| 377 |
+
"""A2 IsHomogeneous (degree 1): Λ(t·x) = t·Λ(x) for scalar t in [0,1].
|
| 378 |
+
|
| 379 |
+
Verified on the clamped domain: both ``axes`` and ``t*axes`` must remain in
|
| 380 |
+
[0,1] for the identity to be meaningful, so ``axes`` is clamped to [0,1] and
|
| 381 |
+
``t`` to [0,1] before the comparison.
|
| 382 |
+
"""
|
| 383 |
+
_check_axes(axes)
|
| 384 |
+
tt = min(max(float(t), 0.0), 1.0)
|
| 385 |
+
x = axes.to(torch.float64).clamp(0.0, 1.0)
|
| 386 |
+
lhs = lambda_aggregate(x * tt, weights)
|
| 387 |
+
rhs = tt * lambda_aggregate(x, weights)
|
| 388 |
+
return bool(torch.all(torch.abs(lhs - rhs) <= tol))
|
| 389 |
+
|
| 390 |
+
|
| 391 |
+
def is_monotone(
|
| 392 |
+
axes: torch.Tensor,
|
| 393 |
+
weights: Optional[torch.Tensor] = None,
|
| 394 |
+
delta: float = 0.05,
|
| 395 |
+
tol: float = 1e-7,
|
| 396 |
+
) -> bool:
|
| 397 |
+
"""A1 IsMonotone: Λ is non-decreasing in each axis.
|
| 398 |
+
|
| 399 |
+
For each axis j, nudges that axis UP by ``delta`` (clamped to stay ≤ 1) on
|
| 400 |
+
every batch row and checks Λ does not decrease (within ``tol``). Rows that
|
| 401 |
+
cannot move (already at 1) are skipped for that axis. A real check on the
|
| 402 |
+
given data — not a symbolic proof.
|
| 403 |
+
"""
|
| 404 |
+
_check_axes(axes)
|
| 405 |
+
x = axes.to(torch.float64).clamp(0.0, 1.0)
|
| 406 |
+
base = lambda_aggregate(x, weights)
|
| 407 |
+
k = x.shape[-1]
|
| 408 |
+
ok = True
|
| 409 |
+
for j in range(k):
|
| 410 |
+
bumped = x.clone()
|
| 411 |
+
bumped[..., j] = (bumped[..., j] + float(delta)).clamp(0.0, 1.0)
|
| 412 |
+
bumped_val = lambda_aggregate(bumped, weights)
|
| 413 |
+
# Λ must not go DOWN when an axis goes UP.
|
| 414 |
+
ok = ok and bool(torch.all(bumped_val - base >= -tol))
|
| 415 |
+
return ok
|
| 416 |
+
|
| 417 |
+
|
| 418 |
+
# ---- Adversarial axiom search (honest: a falsification attempt) ------------ #
|
| 419 |
+
def find_axiom_violation(
|
| 420 |
+
k: int = 5,
|
| 421 |
+
trials: int = 200,
|
| 422 |
+
weights: Optional[torch.Tensor] = None,
|
| 423 |
+
seed: Optional[int] = 0,
|
| 424 |
+
tol: float = 1e-6,
|
| 425 |
+
):
|
| 426 |
+
"""Random-search for ANY A1–A4 violation on random axis/weight draws.
|
| 427 |
+
|
| 428 |
+
Returns the first ``(axiom, axes, weights)`` triple that violates a carried
|
| 429 |
+
axiom within ``tol``, or ``None`` if none is found in ``trials`` draws. This
|
| 430 |
+
is an honest FALSIFICATION attempt on this implementation — finding nothing
|
| 431 |
+
is empirical evidence, NOT a proof (Λ-uniqueness is Conjecture 1, open).
|
| 432 |
+
"""
|
| 433 |
+
gen = torch.Generator()
|
| 434 |
+
if seed is not None:
|
| 435 |
+
gen.manual_seed(int(seed))
|
| 436 |
+
for _ in range(int(trials)):
|
| 437 |
+
x = torch.rand(k, generator=gen, dtype=torch.float64)
|
| 438 |
+
w = weights
|
| 439 |
+
if w is None:
|
| 440 |
+
w = torch.rand(k, generator=gen, dtype=torch.float64) + 1e-3
|
| 441 |
+
# A3 on a constant draw
|
| 442 |
+
c = float(torch.rand(1, generator=gen).item())
|
| 443 |
+
if not is_egyptian_exact(c, k=k, weights=w, tol=max(tol, 1e-5)):
|
| 444 |
+
return ("A3_IsEgyptianExact", torch.full((k,), c, dtype=torch.float64), w)
|
| 445 |
+
# A4 bounded-by-max
|
| 446 |
+
if not is_bounded_by_max(x, w, tol=max(tol, 1e-6)):
|
| 447 |
+
return ("A4_IsBounded", x, w)
|
| 448 |
+
# A2 homogeneous at a random t
|
| 449 |
+
t = float(torch.rand(1, generator=gen).item())
|
| 450 |
+
if not is_homogeneous(x, t, weights=w, tol=max(tol, 1e-5)):
|
| 451 |
+
return ("A2_IsHomogeneous", x, w)
|
| 452 |
+
# A1 monotone (leave headroom so an up-bump stays in range)
|
| 453 |
+
if not is_monotone(x * 0.9, w, tol=max(tol, 1e-7)):
|
| 454 |
+
return ("A1_IsMonotone", x * 0.9, w)
|
| 455 |
+
return None
|
| 456 |
+
|
| 457 |
+
|
| 458 |
+
# ---- Canonical 13-axis Yuyay preset (ADVISORY ONLY) ------------------------ #
|
| 459 |
+
# SZL's own yuyay_v3 "Heart" gate is a 13-axis CONJUNCTIVE-AND screen (each axis
|
| 460 |
+
# independently clears its floor — no compensation). We expose its published
|
| 461 |
+
# axis NAMES and per-axis FLOORS as advisory metadata, and a uniform Λ weight
|
| 462 |
+
# vector over the 13 axes. This is ADVISORY: Λ here is still the weighted
|
| 463 |
+
# geometric mean, and a "pass" is a research-conjecture signal, NOT proven
|
| 464 |
+
# trust. Source: yuyay_v3 spec (Lutar, 2026).
|
| 465 |
+
YUYAY_AXES = (
|
| 466 |
+
"moralGrounding",
|
| 467 |
+
"measurabilityHonesty",
|
| 468 |
+
"empiricalGrounding",
|
| 469 |
+
"logicalConsistency",
|
| 470 |
+
"sourceTransparency",
|
| 471 |
+
"reproducibility",
|
| 472 |
+
"licenseHygiene",
|
| 473 |
+
"scopeDiscipline",
|
| 474 |
+
"claimCalibration",
|
| 475 |
+
"evalAwareness",
|
| 476 |
+
"deceptionKeywords",
|
| 477 |
+
"conflictingDirectives",
|
| 478 |
+
"reversalDirective",
|
| 479 |
+
)
|
| 480 |
+
# Published per-axis advisory floors for the CONJUNCTIVE screen: two "sacred"
|
| 481 |
+
# axes at 0.95, seven "structural" at 0.90, four "introspection" at 0.90.
|
| 482 |
+
YUYAY_FLOORS = (
|
| 483 |
+
0.95, 0.95, # sacred
|
| 484 |
+
0.90, 0.90, 0.90, 0.90, 0.90, 0.90, 0.90, # structural (7)
|
| 485 |
+
0.90, 0.90, 0.90, 0.90, # introspection (4)
|
| 486 |
+
)
|
| 487 |
+
|
| 488 |
+
|
| 489 |
+
def yuyay_weights(
|
| 490 |
+
dtype: torch.dtype = torch.float64,
|
| 491 |
+
device: Optional[torch.device] = None,
|
| 492 |
+
) -> torch.Tensor:
|
| 493 |
+
"""Canonical 13-axis Yuyay Λ weight vector (uniform 1/13), ADVISORY only.
|
| 494 |
+
|
| 495 |
+
Returns a length-13 weight tensor for use as the ``weights`` argument to
|
| 496 |
+
:func:`lambda_aggregate` / :func:`lambda_gate` over the 13 :data:`YUYAY_AXES`.
|
| 497 |
+
Uniform by default (the Egyptian-exact diagonal). The published yuyay_v3
|
| 498 |
+
gate is a conjunctive AND with per-axis floors (:data:`YUYAY_FLOORS`); the
|
| 499 |
+
Λ roll-up here is the weighted geometric mean and is ADVISORY — NOT proven
|
| 500 |
+
trust (Λ-uniqueness is Conjecture 1, open).
|
| 501 |
+
"""
|
| 502 |
+
k = len(YUYAY_AXES)
|
| 503 |
+
return torch.full((k,), 1.0 / k, dtype=dtype, device=device)
|
| 504 |
+
|
| 505 |
+
|
| 506 |
+
# ---- Kernel self-check surface --------------------------------------------- #
|
| 507 |
+
def selfcheck(
|
| 508 |
+
k: int = 5,
|
| 509 |
+
trials: int = 64,
|
| 510 |
+
seed: Optional[int] = 0,
|
| 511 |
+
) -> dict:
|
| 512 |
+
"""Run the A1–A4 empirical self-checks and report a verdict + version.
|
| 513 |
+
|
| 514 |
+
Returns a dict:
|
| 515 |
+
version — kernel version string
|
| 516 |
+
axioms — {A1..A4: bool} empirical pass on sampled inputs
|
| 517 |
+
all_axioms_hold — bool, every sampled axiom check passed
|
| 518 |
+
adversarial — {trials, violation} from a random falsification search
|
| 519 |
+
(violation is None when no violation was found)
|
| 520 |
+
advisory — always True
|
| 521 |
+
lambda_status — Conjecture 1 (open) honesty string
|
| 522 |
+
|
| 523 |
+
HONESTY: these are EMPIRICAL checks on sampled inputs, NOT a proof of
|
| 524 |
+
Λ-uniqueness (Conjecture 1, open). A clean run is evidence, not proof.
|
| 525 |
+
"""
|
| 526 |
+
x = torch.rand(k, dtype=torch.float64) * 0.9 # headroom for the A1 up-bump
|
| 527 |
+
w = torch.rand(k, dtype=torch.float64) + 1e-3
|
| 528 |
+
axioms = {
|
| 529 |
+
"A1_IsMonotone": is_monotone(x, w),
|
| 530 |
+
"A2_IsHomogeneous": is_homogeneous(x, float(torch.rand(1).item()), weights=w),
|
| 531 |
+
"A3_IsEgyptianExact": is_egyptian_exact(float(torch.rand(1).item()), k=k, weights=w),
|
| 532 |
+
"A4_IsBounded": is_bounded_by_max(x, w),
|
| 533 |
+
}
|
| 534 |
+
violation = find_axiom_violation(k=k, trials=trials, seed=seed)
|
| 535 |
+
return {
|
| 536 |
+
"version": __version__,
|
| 537 |
+
"axioms": axioms,
|
| 538 |
+
"all_axioms_hold": all(axioms.values()) and violation is None,
|
| 539 |
+
"adversarial": {"trials": int(trials), "violation": violation},
|
| 540 |
+
"advisory": True,
|
| 541 |
+
"lambda_status": "Conjecture 1 (open) — uniqueness unproven; advisory only",
|
| 542 |
+
}
|
| 543 |
+
|
| 544 |
+
|
| 545 |
+
# Kept in sync with the package __version__ (single source of truth lives in
|
| 546 |
+
# __init__; duplicated here so _lambda is importable/selfcheck-able standalone).
|
| 547 |
+
__version__ = "0.2.0"
|
| 548 |
+
|
| 549 |
+
|
| 550 |
+
# Namedtuple result type for the gate. Defined after functions so docstrings
|
| 551 |
+
# above can reference it; imported by __init__ and layers.
|
| 552 |
+
from collections import namedtuple # noqa: E402
|
| 553 |
+
|
| 554 |
+
LambdaGateResult = namedtuple(
|
| 555 |
+
"LambdaGateResult", ["score", "passed", "threshold", "advisory"]
|
| 556 |
+
)
|
build/torch-cpu/szl_lambda_gate/_ops.py
CHANGED
|
@@ -1,10 +1,10 @@
|
|
| 1 |
-
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
-
# Auto-style ops namespace shim for the universal kernel. Unique suffix lets
|
| 3 |
-
# multiple versions load in the same process (Kernel Hub requirement).
|
| 4 |
-
import torch
|
| 5 |
-
|
| 6 |
-
ops = torch.ops._szl_lambda_gate_20260623081355
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
def add_op_namespace_prefix(op_name: str) -> str:
|
| 10 |
-
return f"_szl_lambda_gate_20260623081355::{op_name}"
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
+
# Auto-style ops namespace shim for the universal kernel. Unique suffix lets
|
| 3 |
+
# multiple versions load in the same process (Kernel Hub requirement).
|
| 4 |
+
import torch
|
| 5 |
+
|
| 6 |
+
ops = torch.ops._szl_lambda_gate_20260623081355
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
def add_op_namespace_prefix(op_name: str) -> str:
|
| 10 |
+
return f"_szl_lambda_gate_20260623081355::{op_name}"
|
build/torch-cpu/szl_lambda_gate/_v1.py
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
+
# © 2026 SZL Holdings · Stephen P. Lutar · ORCID 0009-0001-0110-4173
|
| 3 |
+
"""szl.lambda/v1 strict kernel entry: Λ and its gate on tensors, with no clamping or defaults.
|
| 4 |
+
|
| 5 |
+
This is the torch port of ``reference/szl_lambda_v1.py`` and follows
|
| 6 |
+
``spec/szl.lambda.v1.json``. An input outside the contract makes ``lambda_v1``
|
| 7 |
+
raise ``LambdaV1Error(code)`` and makes ``lambda_v1_gate`` return verdict BLOCK
|
| 8 |
+
with that code. Nothing is clamped, renormalised, defaulted or rounded before
|
| 9 |
+
the compare::
|
| 10 |
+
|
| 11 |
+
lambda_v1(axes, weights) -> 0-d float64 tensor, Λ in [0, 1]
|
| 12 |
+
lambda_v1_gate(axes, weights, tau) -> LambdaV1GateResult(verdict, code, score, tau, advisory)
|
| 13 |
+
|
| 14 |
+
The legacy ``lambda_aggregate`` / ``lambda_gate`` are unchanged. They clamp
|
| 15 |
+
x > 1 to 1, route NaN and ±Inf to 0 and renormalise weights, and
|
| 16 |
+
``lambda_gate`` falls back to a threshold of 0.5, which is now deprecated.
|
| 17 |
+
Use this module when a verdict must follow szl.lambda/v1.
|
| 18 |
+
|
| 19 |
+
Inputs
|
| 20 |
+
``axes`` and ``weights`` are 1-D tensors of a real dtype (floating or
|
| 21 |
+
integer). bool and complex are not numbers. A Python list, None, or a 0-d
|
| 22 |
+
or 2-D tensor is LAMBDA_TYPE_INVALID; build the tensor with
|
| 23 |
+
``torch.tensor(values, dtype=torch.float64)``. ``weights`` is moved to the
|
| 24 |
+
device of ``axes``. Inputs are read and never modified.
|
| 25 |
+
|
| 26 |
+
``tau`` is a Python int or float (not bool), finite, with 0 < tau <= 1. It
|
| 27 |
+
is required and has no default. The policy value is ``policy_tau`` in
|
| 28 |
+
frontier/model_admit_contract.v1.json.
|
| 29 |
+
|
| 30 |
+
Checks run in phases, in the reference's order, so the reported code does not
|
| 31 |
+
depend on axis order::
|
| 32 |
+
|
| 33 |
+
LAMBDA_TYPE_INVALID (container) > LAMBDA_EMPTY > LAMBDA_LENGTH_MISMATCH
|
| 34 |
+
> LAMBDA_TYPE_INVALID (dtype) > LAMBDA_NONFINITE_AXIS
|
| 35 |
+
> LAMBDA_AXIS_OUT_OF_RANGE > LAMBDA_NONFINITE_WEIGHT
|
| 36 |
+
> LAMBDA_WEIGHT_NONPOSITIVE > LAMBDA_WEIGHT_SUM
|
| 37 |
+
|
| 38 |
+
The gate checks tau first (LAMBDA_TAU_INVALID). The element checks and the
|
| 39 |
+
weight sum (``math.fsum``) run on the float64 values exactly as the reference
|
| 40 |
+
runs them, so every error code agrees with the reference.
|
| 41 |
+
|
| 42 |
+
How the numbers are computed
|
| 43 |
+
* Λ, which is ``lambda_v1`` and the gate's ``score``, comes from
|
| 44 |
+
``_lambda.lambda_aggregate`` in float64, so it stays a differentiable
|
| 45 |
+
tensor. On validated input that function's clamp and NaN routing change
|
| 46 |
+
nothing, and a zero axis gives exactly 0. Its renormalisation divides by
|
| 47 |
+
a sum within 1e-12 of 1, which moves Λ by at most Λ·|log Λ|·1e-12
|
| 48 |
+
(<= 3.7e-13) plus float64 rounding. The vectors' value_tol is 1e-12.
|
| 49 |
+
* The verdict comes from log Λ = fsum(w_k · log x_k) over the same float64
|
| 50 |
+
values, as in the reference, so it never depends on how Λ rounds (a
|
| 51 |
+
subnormal Λ has little relative precision). A zero axis gives
|
| 52 |
+
log Λ = -inf and the verdict NO_GO / ZERO_VETO, a veto rather than an
|
| 53 |
+
error. |log Λ - log tau| <= TIE_EPS gives ABSTAIN / NUMERIC_TIE. Above
|
| 54 |
+
the band the verdict is GO, and below it NO_GO / BELOW_TAU.
|
| 55 |
+
|
| 56 |
+
The checks depend on the data and the verdict is a Python string, so this
|
| 57 |
+
entry is not meant to run inside ``torch.compile``. The legacy compiled weight
|
| 58 |
+
path in ``_lambda._resolve_weights``, which clamps weights to ``finfo.tiny``,
|
| 59 |
+
is outside szl.lambda/v1.
|
| 60 |
+
|
| 61 |
+
Λ is advisory. Λ uniqueness is Conjecture 1 (open), and nothing here depends on it.
|
| 62 |
+
"""
|
| 63 |
+
from __future__ import annotations
|
| 64 |
+
|
| 65 |
+
import math
|
| 66 |
+
from collections import namedtuple
|
| 67 |
+
from typing import Any, List, Optional, Tuple
|
| 68 |
+
|
| 69 |
+
import torch
|
| 70 |
+
|
| 71 |
+
from ._lambda import lambda_aggregate
|
| 72 |
+
|
| 73 |
+
SCHEMA = "szl.lambda/v1"
|
| 74 |
+
UNIQUENESS = "CONJECTURE_1_NOT_USED"
|
| 75 |
+
WEIGHT_SUM_TOL = 1e-12
|
| 76 |
+
TIE_EPS = 1e-9
|
| 77 |
+
|
| 78 |
+
TYPE_INVALID = "LAMBDA_TYPE_INVALID"
|
| 79 |
+
EMPTY = "LAMBDA_EMPTY"
|
| 80 |
+
LENGTH_MISMATCH = "LAMBDA_LENGTH_MISMATCH"
|
| 81 |
+
NONFINITE_AXIS = "LAMBDA_NONFINITE_AXIS"
|
| 82 |
+
AXIS_OUT_OF_RANGE = "LAMBDA_AXIS_OUT_OF_RANGE"
|
| 83 |
+
NONFINITE_WEIGHT = "LAMBDA_NONFINITE_WEIGHT"
|
| 84 |
+
WEIGHT_NONPOSITIVE = "LAMBDA_WEIGHT_NONPOSITIVE"
|
| 85 |
+
WEIGHT_SUM = "LAMBDA_WEIGHT_SUM"
|
| 86 |
+
TAU_INVALID = "LAMBDA_TAU_INVALID"
|
| 87 |
+
|
| 88 |
+
#: Every error code, in precedence order (tau is checked first by the gate).
|
| 89 |
+
ERROR_CODES = (
|
| 90 |
+
TYPE_INVALID,
|
| 91 |
+
EMPTY,
|
| 92 |
+
LENGTH_MISMATCH,
|
| 93 |
+
NONFINITE_AXIS,
|
| 94 |
+
AXIS_OUT_OF_RANGE,
|
| 95 |
+
NONFINITE_WEIGHT,
|
| 96 |
+
WEIGHT_NONPOSITIVE,
|
| 97 |
+
WEIGHT_SUM,
|
| 98 |
+
TAU_INVALID,
|
| 99 |
+
)
|
| 100 |
+
|
| 101 |
+
GO = "GO"
|
| 102 |
+
NO_GO = "NO_GO"
|
| 103 |
+
ABSTAIN = "ABSTAIN"
|
| 104 |
+
BLOCK = "BLOCK"
|
| 105 |
+
VERDICTS = (GO, NO_GO, ABSTAIN, BLOCK)
|
| 106 |
+
|
| 107 |
+
ZERO_VETO = "ZERO_VETO"
|
| 108 |
+
BELOW_TAU = "BELOW_TAU"
|
| 109 |
+
NUMERIC_TIE = "NUMERIC_TIE"
|
| 110 |
+
|
| 111 |
+
_INTEGER_DTYPES = (torch.uint8, torch.int8, torch.int16, torch.int32, torch.int64)
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
class LambdaV1Error(ValueError):
|
| 115 |
+
"""An input outside the szl.lambda/v1 contract. ``code`` is one of ERROR_CODES."""
|
| 116 |
+
|
| 117 |
+
def __init__(self, code: str, detail: str = "") -> None:
|
| 118 |
+
self.code = code
|
| 119 |
+
self.detail = detail
|
| 120 |
+
super().__init__(f"{code}: {detail}" if detail else code)
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
#: verdict in VERDICTS; code as in the spec's gate rules; score is Λ as a 0-d
|
| 124 |
+
#: float64 tensor (None on BLOCK); tau is the validated float (None when tau
|
| 125 |
+
#: itself is invalid); advisory is always True.
|
| 126 |
+
LambdaV1GateResult = namedtuple(
|
| 127 |
+
"LambdaV1GateResult", ["verdict", "code", "score", "tau", "advisory"]
|
| 128 |
+
)
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
def _is_real(value: Any) -> bool:
|
| 132 |
+
return isinstance(value, (int, float)) and not isinstance(value, bool)
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
def _is_real_dtype(dtype: torch.dtype) -> bool:
|
| 136 |
+
return dtype.is_floating_point or dtype in _INTEGER_DTYPES
|
| 137 |
+
|
| 138 |
+
|
| 139 |
+
def _validate(
|
| 140 |
+
axes: Any, weights: Any
|
| 141 |
+
) -> Tuple[torch.Tensor, torch.Tensor, List[float], List[float]]:
|
| 142 |
+
"""Float64 tensors and their values, or LambdaV1Error in the reference's phase order."""
|
| 143 |
+
for name, t in (("axes", axes), ("weights", weights)):
|
| 144 |
+
if not isinstance(t, torch.Tensor):
|
| 145 |
+
raise LambdaV1Error(TYPE_INVALID, f"{name} must be a 1-D torch.Tensor, got {type(t).__name__}")
|
| 146 |
+
if t.dim() != 1:
|
| 147 |
+
raise LambdaV1Error(TYPE_INVALID, f"{name} must be a 1-D tensor, got {t.dim()}-D")
|
| 148 |
+
k, m = axes.shape[0], weights.shape[0]
|
| 149 |
+
if k == 0 or m == 0:
|
| 150 |
+
raise LambdaV1Error(EMPTY, f"len(axes)={k}, len(weights)={m}")
|
| 151 |
+
if k != m:
|
| 152 |
+
raise LambdaV1Error(LENGTH_MISMATCH, f"len(axes)={k} != len(weights)={m}")
|
| 153 |
+
for name, t in (("axes", axes), ("weights", weights)):
|
| 154 |
+
if not _is_real_dtype(t.dtype):
|
| 155 |
+
raise LambdaV1Error(TYPE_INVALID, f"{name} has dtype {t.dtype}, not a real number type")
|
| 156 |
+
x = axes.to(torch.float64)
|
| 157 |
+
w = weights.to(device=x.device, dtype=torch.float64)
|
| 158 |
+
xs, ws = x.tolist(), w.tolist()
|
| 159 |
+
for i, v in enumerate(xs):
|
| 160 |
+
if not math.isfinite(v):
|
| 161 |
+
raise LambdaV1Error(NONFINITE_AXIS, f"axes[{i}]={v!r}")
|
| 162 |
+
for i, v in enumerate(xs):
|
| 163 |
+
if not 0.0 <= v <= 1.0:
|
| 164 |
+
raise LambdaV1Error(AXIS_OUT_OF_RANGE, f"axes[{i}]={v!r} is outside [0, 1]")
|
| 165 |
+
for i, v in enumerate(ws):
|
| 166 |
+
if not math.isfinite(v):
|
| 167 |
+
raise LambdaV1Error(NONFINITE_WEIGHT, f"weights[{i}]={v!r}")
|
| 168 |
+
for i, v in enumerate(ws):
|
| 169 |
+
if not v > 0.0:
|
| 170 |
+
raise LambdaV1Error(WEIGHT_NONPOSITIVE, f"weights[{i}]={v!r} is not > 0")
|
| 171 |
+
try:
|
| 172 |
+
total = math.fsum(ws)
|
| 173 |
+
except OverflowError:
|
| 174 |
+
raise LambdaV1Error(WEIGHT_SUM, "sum of weights overflows a float") from None
|
| 175 |
+
if not abs(total - 1.0) <= WEIGHT_SUM_TOL:
|
| 176 |
+
raise LambdaV1Error(WEIGHT_SUM, f"fsum(weights)={total!r} is not within {WEIGHT_SUM_TOL} of 1")
|
| 177 |
+
return x, w, xs, ws
|
| 178 |
+
|
| 179 |
+
|
| 180 |
+
def lambda_v1(axes: torch.Tensor, weights: torch.Tensor) -> torch.Tensor:
|
| 181 |
+
"""Λ_w(axes) as a 0-d float64 tensor in [0, 1]; exactly 0 iff some axis is 0.
|
| 182 |
+
|
| 183 |
+
Raises ``LambdaV1Error(code)`` for any input outside szl.lambda/v1.
|
| 184 |
+
Differentiable with respect to ``axes``.
|
| 185 |
+
"""
|
| 186 |
+
x, w, _, _ = _validate(axes, weights)
|
| 187 |
+
return lambda_aggregate(x, w)
|
| 188 |
+
|
| 189 |
+
|
| 190 |
+
def _check_tau(tau: Any) -> float:
|
| 191 |
+
if not _is_real(tau):
|
| 192 |
+
raise LambdaV1Error(TAU_INVALID, f"tau is {type(tau).__name__}, not a real number")
|
| 193 |
+
if isinstance(tau, float) and not math.isfinite(tau):
|
| 194 |
+
raise LambdaV1Error(TAU_INVALID, f"tau={tau!r} is not finite")
|
| 195 |
+
if not 0 < tau <= 1:
|
| 196 |
+
raise LambdaV1Error(TAU_INVALID, f"tau={tau!r} is outside (0, 1]")
|
| 197 |
+
return float(tau)
|
| 198 |
+
|
| 199 |
+
|
| 200 |
+
def lambda_v1_gate(axes: torch.Tensor, weights: torch.Tensor, tau: float) -> LambdaV1GateResult:
|
| 201 |
+
"""The szl.lambda/v1 gate. It never raises on bad input; it returns BLOCK with the code.
|
| 202 |
+
|
| 203 |
+
tau is required. The compare is in log space on the unrounded value, and a
|
| 204 |
+
tie within TIE_EPS is ABSTAIN. A verdict is ADVISORY.
|
| 205 |
+
"""
|
| 206 |
+
t: Optional[float] = None
|
| 207 |
+
try:
|
| 208 |
+
t = _check_tau(tau)
|
| 209 |
+
x, w, xs, ws = _validate(axes, weights)
|
| 210 |
+
except LambdaV1Error as err:
|
| 211 |
+
return LambdaV1GateResult(BLOCK, err.code, None, t, True)
|
| 212 |
+
score = lambda_aggregate(x, w)
|
| 213 |
+
if any(v == 0.0 for v in xs):
|
| 214 |
+
return LambdaV1GateResult(NO_GO, ZERO_VETO, score, t, True)
|
| 215 |
+
log_lam = math.fsum(wk * math.log(xk) for xk, wk in zip(xs, ws))
|
| 216 |
+
delta = log_lam - math.log(t)
|
| 217 |
+
if abs(delta) <= TIE_EPS:
|
| 218 |
+
return LambdaV1GateResult(ABSTAIN, NUMERIC_TIE, score, t, True)
|
| 219 |
+
if delta > 0:
|
| 220 |
+
return LambdaV1GateResult(GO, None, score, t, True)
|
| 221 |
+
return LambdaV1GateResult(NO_GO, BELOW_TAU, score, t, True)
|
build/torch-cpu/szl_lambda_gate/governed_norm/__init__.py
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
+
# © 2026 SZL Holdings · Stephen P. Lutar · ORCID 0009-0001-0110-4173
|
| 3 |
+
"""szl_lambda_gate.governed_norm — governed normalization kernels (folded in).
|
| 4 |
+
|
| 5 |
+
CONSOLIDATION (Wave D): this subpackage is the ``szl-governed-norm`` universal
|
| 6 |
+
kernel folded into the canonical ``szl-lambda-gate`` kernels package so the two
|
| 7 |
+
duplicate micro-repos become ONE canonical home. The source repo
|
| 8 |
+
``szl-holdings/szl-governed-norm`` is DEPRECATED (see its DEPRECATED.md) and
|
| 9 |
+
points here; nothing was deleted — this is the additive, reversible copy.
|
| 10 |
+
|
| 11 |
+
It provides correctness-verified RMSNorm and LayerNorm that run on CPU and CUDA
|
| 12 |
+
and are torch.compile-friendly, plus an optional *governed* path that emits
|
| 13 |
+
content-addressed, SHA3-256 hash-chained receipts of each call — provenance at
|
| 14 |
+
the kernel layer, in the spirit of the a11oy governed-AI platform
|
| 15 |
+
(https://a-11-oy.com).
|
| 16 |
+
|
| 17 |
+
Usage (as a subpackage of the canonical kernel)::
|
| 18 |
+
|
| 19 |
+
import torch
|
| 20 |
+
from szl_lambda_gate import governed_norm as gn
|
| 21 |
+
|
| 22 |
+
print(gn.selfcheck()) # one-shot correctness + receipt check
|
| 23 |
+
x = torch.randn(4, 1024, dtype=torch.float16)
|
| 24 |
+
y = gn.rms_norm(x, eps=1e-6) # plain path
|
| 25 |
+
y2 = gn.rms_norm(x, eps=1e-6, governed=True) # records to the default chain
|
| 26 |
+
chain = gn.ReceiptChain()
|
| 27 |
+
y3 = gn.rms_norm(x, eps=1e-6, chain=chain) # records into YOUR chain only
|
| 28 |
+
print(chain.verify()) # (ok, depth, first_break_seq)
|
| 29 |
+
|
| 30 |
+
Honesty: this is a universal (pure-Python) kernel — a correctness reference,
|
| 31 |
+
not a hand-tuned CUDA speed record. No fabricated benchmarks. Its
|
| 32 |
+
differentiator is verifiable governance, not raw FLOPs. Λ = Conjecture 1
|
| 33 |
+
(advisory, uniqueness OPEN) — never described as proven trust anywhere.
|
| 34 |
+
|
| 35 |
+
Note on torch.compile: every op is torch.compile(fullgraph=True)-compatible.
|
| 36 |
+
Receipt emission is an eager-only side effect (it hashes materialized tensor
|
| 37 |
+
bytes), so when a *governed* call is captured into a compiled graph the
|
| 38 |
+
numerics are unchanged but NO receipt is recorded — govern at the eager audit
|
| 39 |
+
boundary. This is documented honestly and covered by tests.
|
| 40 |
+
"""
|
| 41 |
+
from typing import Any, Dict, List, Optional, Tuple
|
| 42 |
+
|
| 43 |
+
import torch
|
| 44 |
+
|
| 45 |
+
from . import layers # noqa: F401 (must be importable for Hub layer mapping)
|
| 46 |
+
from ._norm import fused_add_rms_norm as _fused_add_rms_norm
|
| 47 |
+
from ._norm import layer_norm as _layer_norm
|
| 48 |
+
from ._norm import rms_norm as _rms_norm
|
| 49 |
+
from ._receipt import _GENESIS as _GENESIS_HEAD
|
| 50 |
+
from ._receipt import ReceiptChain, default_chain, emit_receipt
|
| 51 |
+
|
| 52 |
+
__all__ = [
|
| 53 |
+
"rms_norm",
|
| 54 |
+
"layer_norm",
|
| 55 |
+
"fused_add_rms_norm",
|
| 56 |
+
"layers",
|
| 57 |
+
"ReceiptChain",
|
| 58 |
+
"emit_receipt",
|
| 59 |
+
"receipt_head",
|
| 60 |
+
"receipt_count",
|
| 61 |
+
"receipt_tail",
|
| 62 |
+
"receipt_verify",
|
| 63 |
+
"selfcheck",
|
| 64 |
+
"DOCTRINE_FOOTER",
|
| 65 |
+
"__version__",
|
| 66 |
+
]
|
| 67 |
+
|
| 68 |
+
__version__ = "0.2.0"
|
| 69 |
+
DOCTRINE_FOOTER = (
|
| 70 |
+
"SZL Holdings · governed normalization · provenance at the kernel layer · "
|
| 71 |
+
"Lambda = Conjecture 1 (advisory) · honesty over checklist"
|
| 72 |
+
)
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
def _is_tracing() -> bool:
|
| 76 |
+
"""True while torch.compile / Dynamo is tracing this code.
|
| 77 |
+
|
| 78 |
+
Receipt emission reads materialized tensor bytes (hashing on CPU), which is
|
| 79 |
+
an inherently eager, side-effecting host operation that cannot live inside
|
| 80 |
+
a traced FX graph — so under torch.compile we skip the emit. This keeps
|
| 81 |
+
EVERY op torch.compile(fullgraph=True)-compatible while remaining honest:
|
| 82 |
+
when a governed call is captured into a compiled graph, NO receipt is
|
| 83 |
+
recorded (the numerics are unchanged and identical to the eager path).
|
| 84 |
+
Governance is intended for the eager audit boundary; record receipts there.
|
| 85 |
+
"""
|
| 86 |
+
is_compiling = getattr(torch.compiler, "is_compiling", None)
|
| 87 |
+
return bool(is_compiling()) if is_compiling is not None else False
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
def _emit(
|
| 91 |
+
chain: Optional[ReceiptChain],
|
| 92 |
+
op: str,
|
| 93 |
+
x: torch.Tensor,
|
| 94 |
+
out: torch.Tensor,
|
| 95 |
+
eps: float,
|
| 96 |
+
sign_key: Optional[Any] = None,
|
| 97 |
+
organ: str = "szl-governed-norm",
|
| 98 |
+
) -> None:
|
| 99 |
+
"""Append a receipt to ``chain`` (or the process default chain if None).
|
| 100 |
+
|
| 101 |
+
No-op while torch.compile is tracing (see ``_is_tracing``). When
|
| 102 |
+
``sign_key`` (a PEM ECDSA-P256 private key) is supplied and szl-receipt is
|
| 103 |
+
installed, the receipt carries an additive DSSE ``signature`` envelope;
|
| 104 |
+
keyless is UNSIGNED-honest.
|
| 105 |
+
"""
|
| 106 |
+
if _is_tracing():
|
| 107 |
+
return
|
| 108 |
+
target = chain if chain is not None else default_chain()
|
| 109 |
+
target.emit(op, x, out, eps, sign_key=sign_key, organ=organ)
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
def rms_norm(
|
| 113 |
+
x: torch.Tensor,
|
| 114 |
+
weight: Optional[torch.Tensor] = None,
|
| 115 |
+
eps: float = 1e-6,
|
| 116 |
+
governed: bool = False,
|
| 117 |
+
chain: Optional[ReceiptChain] = None,
|
| 118 |
+
sign_key: Optional[Any] = None,
|
| 119 |
+
organ: str = "szl-governed-norm",
|
| 120 |
+
) -> torch.Tensor:
|
| 121 |
+
"""RMSNorm over the last dim.
|
| 122 |
+
|
| 123 |
+
If ``governed=True``, append an audit receipt. By default the receipt goes
|
| 124 |
+
to the process-wide default chain (convenient). Pass your own ``chain`` (a
|
| 125 |
+
``ReceiptChain`` instance) to record into a caller-owned chain instead —
|
| 126 |
+
this avoids global-state contention when many threads/requests govern
|
| 127 |
+
independently. Passing ``chain`` implies governance even if
|
| 128 |
+
``governed=False`` is left at its default. Pass ``sign_key`` (PEM
|
| 129 |
+
ECDSA-P256) to additively sign the receipt via szl-receipt.
|
| 130 |
+
"""
|
| 131 |
+
out = _rms_norm(x, weight=weight, eps=eps)
|
| 132 |
+
if governed or chain is not None:
|
| 133 |
+
_emit(chain, "rms_norm", x, out, eps, sign_key=sign_key, organ=organ)
|
| 134 |
+
return out
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
def layer_norm(
|
| 138 |
+
x: torch.Tensor,
|
| 139 |
+
weight: Optional[torch.Tensor] = None,
|
| 140 |
+
bias: Optional[torch.Tensor] = None,
|
| 141 |
+
eps: float = 1e-5,
|
| 142 |
+
governed: bool = False,
|
| 143 |
+
chain: Optional[ReceiptChain] = None,
|
| 144 |
+
sign_key: Optional[Any] = None,
|
| 145 |
+
organ: str = "szl-governed-norm",
|
| 146 |
+
) -> torch.Tensor:
|
| 147 |
+
"""LayerNorm over the last dim.
|
| 148 |
+
|
| 149 |
+
If ``governed=True`` (or a ``chain`` is supplied), append an audit receipt
|
| 150 |
+
to ``chain`` when given, otherwise to the process default chain. See
|
| 151 |
+
``rms_norm`` for the per-call ``chain`` rationale and ``sign_key``.
|
| 152 |
+
"""
|
| 153 |
+
out = _layer_norm(x, weight=weight, bias=bias, eps=eps)
|
| 154 |
+
if governed or chain is not None:
|
| 155 |
+
_emit(chain, "layer_norm", x, out, eps, sign_key=sign_key, organ=organ)
|
| 156 |
+
return out
|
| 157 |
+
|
| 158 |
+
|
| 159 |
+
def fused_add_rms_norm(
|
| 160 |
+
x: torch.Tensor,
|
| 161 |
+
residual: torch.Tensor,
|
| 162 |
+
weight: Optional[torch.Tensor] = None,
|
| 163 |
+
eps: float = 1e-6,
|
| 164 |
+
governed: bool = False,
|
| 165 |
+
chain: Optional[ReceiptChain] = None,
|
| 166 |
+
sign_key: Optional[Any] = None,
|
| 167 |
+
organ: str = "szl-governed-norm",
|
| 168 |
+
) -> Tuple[torch.Tensor, torch.Tensor]:
|
| 169 |
+
"""Residual-add + RMSNorm (transformer block pattern).
|
| 170 |
+
|
| 171 |
+
Returns ``(y, new_residual)`` where ``new_residual = x + residual`` and
|
| 172 |
+
``y = rms_norm(new_residual, weight, eps)``. If ``governed=True`` (or a
|
| 173 |
+
``chain`` is supplied), append an audit receipt over the normalized output
|
| 174 |
+
to ``chain`` when given, otherwise to the process default chain. Pass
|
| 175 |
+
``sign_key`` to additively sign the receipt via szl-receipt.
|
| 176 |
+
"""
|
| 177 |
+
out, new_residual = _fused_add_rms_norm(x, residual, weight=weight, eps=eps)
|
| 178 |
+
if governed or chain is not None:
|
| 179 |
+
_emit(chain, "fused_add_rms_norm", x, out, eps, sign_key=sign_key, organ=organ)
|
| 180 |
+
return out, new_residual
|
| 181 |
+
|
| 182 |
+
|
| 183 |
+
# ---- governance receipt surface (operates on the default in-process chain) --
|
| 184 |
+
def receipt_head() -> str:
|
| 185 |
+
"""SHA3-256 head of the governed-call receipt chain ('0'*64 if empty)."""
|
| 186 |
+
return default_chain().head()
|
| 187 |
+
|
| 188 |
+
|
| 189 |
+
def receipt_count() -> int:
|
| 190 |
+
"""Number of governed calls recorded."""
|
| 191 |
+
return default_chain().count()
|
| 192 |
+
|
| 193 |
+
|
| 194 |
+
def receipt_tail(n: int = 10) -> List[Dict[str, Any]]:
|
| 195 |
+
"""Last n receipts."""
|
| 196 |
+
return default_chain().tail(n)
|
| 197 |
+
|
| 198 |
+
|
| 199 |
+
def receipt_verify() -> Dict[str, Any]:
|
| 200 |
+
"""Re-walk the receipt chain. Returns {ok, depth, first_break_seq}."""
|
| 201 |
+
ok, depth, brk = default_chain().verify()
|
| 202 |
+
return {"ok": ok, "depth": depth, "first_break_seq": brk, "head": default_chain().head()}
|
| 203 |
+
|
| 204 |
+
|
| 205 |
+
# ---- one-shot self-verification --------------------------------------------
|
| 206 |
+
def selfcheck() -> Dict[str, Any]:
|
| 207 |
+
"""Verify correctness + governance in a single call; never raises.
|
| 208 |
+
|
| 209 |
+
Runs a tiny, self-contained, CPU-only smoke test against PyTorch references
|
| 210 |
+
so downstream code (and SZL's own a11oy / hatun-mcp) can confirm the loaded
|
| 211 |
+
kernel is the real, working article before trusting it.
|
| 212 |
+
|
| 213 |
+
Checks (all on a *private, throwaway* ReceiptChain so the process default
|
| 214 |
+
chain is never touched):
|
| 215 |
+
* ``rms_norm`` matches a Llama-style float32 reference,
|
| 216 |
+
* ``layer_norm`` matches ``torch.nn.functional.layer_norm``,
|
| 217 |
+
* ``fused_add_rms_norm`` matches the unfused add-then-norm path,
|
| 218 |
+
* a governed call emits exactly one receipt and the chain verifies.
|
| 219 |
+
|
| 220 |
+
Returns a JSON-able dict:
|
| 221 |
+
``{ok, version, checks: {name: bool}, receipt_ok, receipt_head, error}``
|
| 222 |
+
``ok`` is True iff every check passed. On unexpected failure ``ok`` is
|
| 223 |
+
False and ``error`` carries the message — this function is designed to be
|
| 224 |
+
safe to call in a health probe and will not raise.
|
| 225 |
+
"""
|
| 226 |
+
checks: Dict[str, bool] = {}
|
| 227 |
+
receipt_ok = False
|
| 228 |
+
receipt_head = _GENESIS_HEAD
|
| 229 |
+
error = None
|
| 230 |
+
try:
|
| 231 |
+
torch.manual_seed(0)
|
| 232 |
+
x = torch.randn(4, 64, dtype=torch.float32)
|
| 233 |
+
w = torch.randn(64, dtype=torch.float32)
|
| 234 |
+
b = torch.randn(64, dtype=torch.float32)
|
| 235 |
+
res = torch.randn(4, 64, dtype=torch.float32)
|
| 236 |
+
eps_r, eps_l = 1e-6, 1e-5
|
| 237 |
+
|
| 238 |
+
# rms_norm vs Llama-style fp32 reference
|
| 239 |
+
xf = x.to(torch.float32)
|
| 240 |
+
ref_rms = (xf * torch.rsqrt(xf.pow(2).mean(-1, keepdim=True) + eps_r)) * w
|
| 241 |
+
checks["rms_norm"] = bool(
|
| 242 |
+
torch.allclose(rms_norm(x, weight=w, eps=eps_r), ref_rms, rtol=1e-5, atol=1e-5)
|
| 243 |
+
)
|
| 244 |
+
|
| 245 |
+
# layer_norm vs torch reference
|
| 246 |
+
ref_ln = torch.nn.functional.layer_norm(x, (64,), weight=w, bias=b, eps=eps_l)
|
| 247 |
+
checks["layer_norm"] = bool(
|
| 248 |
+
torch.allclose(layer_norm(x, weight=w, bias=b, eps=eps_l), ref_ln,
|
| 249 |
+
rtol=1e-5, atol=1e-5)
|
| 250 |
+
)
|
| 251 |
+
|
| 252 |
+
# fused_add_rms_norm vs unfused path
|
| 253 |
+
y_f, new_res = fused_add_rms_norm(x, res, weight=w, eps=eps_r)
|
| 254 |
+
h = x.to(torch.float32) + res.to(torch.float32)
|
| 255 |
+
ref_y = rms_norm(h.to(x.dtype), weight=w, eps=eps_r)
|
| 256 |
+
checks["fused_add_rms_norm"] = bool(
|
| 257 |
+
torch.allclose(y_f, ref_y, rtol=1e-5, atol=1e-5)
|
| 258 |
+
and torch.allclose(new_res, h.to(x.dtype), rtol=1e-6, atol=1e-6)
|
| 259 |
+
)
|
| 260 |
+
|
| 261 |
+
# governance on a private chain: one emit, chain verifies
|
| 262 |
+
probe_chain = ReceiptChain()
|
| 263 |
+
rms_norm(x, weight=w, eps=eps_r, chain=probe_chain)
|
| 264 |
+
ok, depth, brk = probe_chain.verify()
|
| 265 |
+
receipt_ok = bool(ok and depth == 1 and brk == -1)
|
| 266 |
+
receipt_head = probe_chain.head()
|
| 267 |
+
checks["governance"] = receipt_ok
|
| 268 |
+
except Exception as exc: # never raise from a health probe
|
| 269 |
+
error = f"{type(exc).__name__}: {exc}"
|
| 270 |
+
|
| 271 |
+
ok = bool(checks) and all(checks.values()) and error is None
|
| 272 |
+
return {
|
| 273 |
+
"ok": ok,
|
| 274 |
+
"version": __version__,
|
| 275 |
+
"checks": checks,
|
| 276 |
+
"receipt_ok": receipt_ok,
|
| 277 |
+
"receipt_head": receipt_head,
|
| 278 |
+
"error": error,
|
| 279 |
+
}
|
build/torch-cpu/szl_lambda_gate/governed_norm/_norm.py
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
+
# © 2026 SZL Holdings · Stephen P. Lutar · ORCID 0009-0001-0110-4173
|
| 3 |
+
"""Pure-PyTorch normalization primitives for the SZL governed-norm kernel.
|
| 4 |
+
|
| 5 |
+
These are correctness-verified reference implementations (RMSNorm, LayerNorm,
|
| 6 |
+
and the residual-fused RMSNorm pattern used by transformer blocks) written in
|
| 7 |
+
pure PyTorch. They run on CPU and CUDA, are torch.compile-friendly, and depend
|
| 8 |
+
ONLY on torch + the Python standard library (a Kernel Hub requirement for
|
| 9 |
+
universal kernels).
|
| 10 |
+
|
| 11 |
+
HONESTY: this is a *universal* (pure-Python) kernel. It does NOT ship a
|
| 12 |
+
hand-tuned CUDA/Triton binary, so it is a correctness reference, not a
|
| 13 |
+
speed record. We make no fabricated benchmark claims. Where it adds value
|
| 14 |
+
is the optional *governed* path (see _receipt.py): every normalization call
|
| 15 |
+
can emit a content-addressed, hash-chained receipt of its inputs/outputs so
|
| 16 |
+
the operation is auditable — SZL Holdings' provenance doctrine applied at
|
| 17 |
+
the kernel layer.
|
| 18 |
+
|
| 19 |
+
Numerical convention (all ops): reductions and the normalization math are
|
| 20 |
+
computed in float32 for stability, then the result is cast back to the input
|
| 21 |
+
dtype. This is the standard Llama-style convention and is what makes
|
| 22 |
+
float16 / bfloat16 inputs numerically well-behaved.
|
| 23 |
+
|
| 24 |
+
Validation convention: guards below are cheap, branch-only checks on metadata
|
| 25 |
+
(dtype / ndim / shape / device) — they allocate nothing on the happy path and
|
| 26 |
+
constant-fold away under torch.compile, so they do not perturb traced graphs.
|
| 27 |
+
They exist to turn silent broadcasting / device-mismatch bugs into clear,
|
| 28 |
+
early errors. A zero-size normalized last dimension is rejected (normalizing
|
| 29 |
+
over zero elements is undefined); a single-element last dimension is allowed
|
| 30 |
+
(RMSNorm yields sign(x); LayerNorm yields 0, matching F.layer_norm).
|
| 31 |
+
|
| 32 |
+
Non-finite convention (NaN / Inf inputs): these ops do NOT sanitize their
|
| 33 |
+
input. A NaN or Inf in the input propagates through the reduction and appears
|
| 34 |
+
in the output, exactly as it would in torch.nn.functional.layer_norm / a
|
| 35 |
+
hand-written kernel. We deliberately do NOT silently replace non-finite values
|
| 36 |
+
(that would hide upstream numerical bugs); detecting/handling them is the
|
| 37 |
+
caller's responsibility. This propagation behavior is covered by regression
|
| 38 |
+
tests so it cannot change unnoticed.
|
| 39 |
+
"""
|
| 40 |
+
from typing import Optional
|
| 41 |
+
|
| 42 |
+
import torch
|
| 43 |
+
|
| 44 |
+
# Floating dtypes this kernel supports. Integer / complex inputs are rejected
|
| 45 |
+
# early with a clear message rather than silently producing garbage.
|
| 46 |
+
_SUPPORTED_DTYPES = (torch.float16, torch.bfloat16, torch.float32, torch.float64)
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
def _compute_dtype(in_dtype: torch.dtype) -> torch.dtype:
|
| 50 |
+
"""Reduction/normalization compute dtype.
|
| 51 |
+
|
| 52 |
+
Low-precision inputs (fp16/bf16) are upcast to float32 for stability — the
|
| 53 |
+
standard Llama-style convention. float64 inputs are NOT downcast: doing so
|
| 54 |
+
would silently lose precision (and break gradcheck), so we keep float64.
|
| 55 |
+
"""
|
| 56 |
+
return torch.float32 if in_dtype in (torch.float16, torch.bfloat16) else in_dtype
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def _check_input(x: torch.Tensor, name: str = "x") -> None:
|
| 60 |
+
"""Cheap, allocation-free guards on the primary input tensor.
|
| 61 |
+
|
| 62 |
+
Only inspects metadata (type / dtype / ndim), so it is constant-folded by
|
| 63 |
+
torch.compile and adds no runtime tensor work on the happy path.
|
| 64 |
+
"""
|
| 65 |
+
if not isinstance(x, torch.Tensor):
|
| 66 |
+
raise TypeError(f"{name} must be a torch.Tensor, got {type(x).__name__}")
|
| 67 |
+
if x.dtype not in _SUPPORTED_DTYPES:
|
| 68 |
+
raise TypeError(
|
| 69 |
+
f"{name} has unsupported dtype {x.dtype}; "
|
| 70 |
+
f"expected one of {tuple(str(d) for d in _SUPPORTED_DTYPES)}"
|
| 71 |
+
)
|
| 72 |
+
if x.dim() < 1:
|
| 73 |
+
raise ValueError(
|
| 74 |
+
f"{name} must have at least 1 dimension (the normalized dim); "
|
| 75 |
+
f"got a {x.dim()}-d tensor"
|
| 76 |
+
)
|
| 77 |
+
# A zero-size normalized (last) dimension is mathematically undefined:
|
| 78 |
+
# mean/RMS over zero elements is NaN, so normalization has no meaning.
|
| 79 |
+
# Reject it early with a clear message instead of silently returning an
|
| 80 |
+
# empty/NaN tensor (the classic shape-bug-masquerading-as-success case).
|
| 81 |
+
if x.shape[-1] == 0:
|
| 82 |
+
raise ValueError(
|
| 83 |
+
f"{name} has a zero-size normalized last dimension {tuple(x.shape)}; "
|
| 84 |
+
f"normalization over zero elements is undefined"
|
| 85 |
+
)
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
def _check_affine(
|
| 89 |
+
x: torch.Tensor,
|
| 90 |
+
param: Optional[torch.Tensor],
|
| 91 |
+
name: str,
|
| 92 |
+
) -> None:
|
| 93 |
+
"""Validate an optional affine parameter (weight/bias/residual peer).
|
| 94 |
+
|
| 95 |
+
Enforces that the parameter is 1-D and matches the normalized (last)
|
| 96 |
+
dimension, and lives on the same device as ``x``. This catches the
|
| 97 |
+
classic silent-broadcast bug where a mis-shaped weight would broadcast
|
| 98 |
+
instead of erroring. Metadata-only: no allocations.
|
| 99 |
+
"""
|
| 100 |
+
if param is None:
|
| 101 |
+
return
|
| 102 |
+
if not isinstance(param, torch.Tensor):
|
| 103 |
+
raise TypeError(f"{name} must be a torch.Tensor or None, got {type(param).__name__}")
|
| 104 |
+
if param.device != x.device:
|
| 105 |
+
raise ValueError(
|
| 106 |
+
f"{name} is on device {param.device} but x is on {x.device}; "
|
| 107 |
+
f"move them to the same device"
|
| 108 |
+
)
|
| 109 |
+
last = x.shape[-1]
|
| 110 |
+
if param.dim() != 1 or param.shape[0] != last:
|
| 111 |
+
raise ValueError(
|
| 112 |
+
f"{name} must be 1-D with shape ({last},) to match the normalized "
|
| 113 |
+
f"last dimension of x; got shape {tuple(param.shape)}"
|
| 114 |
+
)
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
def _check_eps(eps: float) -> None:
|
| 118 |
+
"""eps must be a positive, finite scalar (rsqrt(var+eps) must be safe)."""
|
| 119 |
+
e = float(eps)
|
| 120 |
+
if not (e > 0.0) or e != e or e == float("inf"):
|
| 121 |
+
raise ValueError(f"eps must be a positive finite float, got {eps!r}")
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
def rms_norm(
|
| 125 |
+
x: torch.Tensor,
|
| 126 |
+
weight: Optional[torch.Tensor] = None,
|
| 127 |
+
eps: float = 1e-6,
|
| 128 |
+
) -> torch.Tensor:
|
| 129 |
+
"""Root-mean-square layer normalization over the last dimension.
|
| 130 |
+
|
| 131 |
+
y = x / sqrt(mean(x^2, dim=-1) + eps) * weight
|
| 132 |
+
|
| 133 |
+
Computed in float32 for numerical stability, then cast back to the input
|
| 134 |
+
dtype (the standard, correctness-preserving convention used by Llama-style
|
| 135 |
+
RMSNorm). `weight` is optional; when omitted, no affine scale is applied.
|
| 136 |
+
|
| 137 |
+
Raises clear TypeError/ValueError on bad dtype, rank, eps, or a weight
|
| 138 |
+
whose shape/device does not match x's normalized dimension.
|
| 139 |
+
"""
|
| 140 |
+
_check_input(x)
|
| 141 |
+
_check_eps(eps)
|
| 142 |
+
_check_affine(x, weight, "weight")
|
| 143 |
+
|
| 144 |
+
in_dtype = x.dtype
|
| 145 |
+
xf = x.to(_compute_dtype(in_dtype))
|
| 146 |
+
variance = xf.pow(2).mean(dim=-1, keepdim=True)
|
| 147 |
+
xf = xf * torch.rsqrt(variance + eps)
|
| 148 |
+
out = xf.to(in_dtype)
|
| 149 |
+
if weight is not None:
|
| 150 |
+
out = out * weight
|
| 151 |
+
return out
|
| 152 |
+
|
| 153 |
+
|
| 154 |
+
def layer_norm(
|
| 155 |
+
x: torch.Tensor,
|
| 156 |
+
weight: Optional[torch.Tensor] = None,
|
| 157 |
+
bias: Optional[torch.Tensor] = None,
|
| 158 |
+
eps: float = 1e-5,
|
| 159 |
+
) -> torch.Tensor:
|
| 160 |
+
"""Standard layer normalization over the last dimension.
|
| 161 |
+
|
| 162 |
+
Mean/variance computed in float32 for stability, then cast back. Matches
|
| 163 |
+
torch.nn.functional.layer_norm semantics for the normalized-shape = last
|
| 164 |
+
dim case; verified against it in the test suite.
|
| 165 |
+
|
| 166 |
+
Raises clear TypeError/ValueError on bad dtype, rank, eps, or a
|
| 167 |
+
weight/bias whose shape/device does not match x's normalized dimension.
|
| 168 |
+
"""
|
| 169 |
+
_check_input(x)
|
| 170 |
+
_check_eps(eps)
|
| 171 |
+
_check_affine(x, weight, "weight")
|
| 172 |
+
_check_affine(x, bias, "bias")
|
| 173 |
+
|
| 174 |
+
in_dtype = x.dtype
|
| 175 |
+
xf = x.to(_compute_dtype(in_dtype))
|
| 176 |
+
mean = xf.mean(dim=-1, keepdim=True)
|
| 177 |
+
# Biased (population) variance = mean of squared deviations. We compute it
|
| 178 |
+
# directly rather than via Tensor.var(unbiased=False): torch's .var emits a
|
| 179 |
+
# "degrees of freedom <= 0" UserWarning when the normalized dim has a single
|
| 180 |
+
# element, even though unbiased=False is well-defined there (variance 0).
|
| 181 |
+
# Computing it ourselves matches F.layer_norm exactly and stays silent and
|
| 182 |
+
# torch.compile(fullgraph=True)-clean for the single-element edge case.
|
| 183 |
+
centered = xf - mean
|
| 184 |
+
var = centered.pow(2).mean(dim=-1, keepdim=True)
|
| 185 |
+
xf = centered * torch.rsqrt(var + eps)
|
| 186 |
+
out = xf.to(in_dtype)
|
| 187 |
+
if weight is not None:
|
| 188 |
+
out = out * weight
|
| 189 |
+
if bias is not None:
|
| 190 |
+
out = out + bias
|
| 191 |
+
return out
|
| 192 |
+
|
| 193 |
+
|
| 194 |
+
def fused_add_rms_norm(
|
| 195 |
+
x: torch.Tensor,
|
| 196 |
+
residual: torch.Tensor,
|
| 197 |
+
weight: Optional[torch.Tensor] = None,
|
| 198 |
+
eps: float = 1e-6,
|
| 199 |
+
):
|
| 200 |
+
"""Residual-add followed by RMSNorm — the canonical transformer block pattern.
|
| 201 |
+
|
| 202 |
+
h = x + residual # updated residual stream
|
| 203 |
+
y = rms_norm(h, weight, eps)
|
| 204 |
+
return y, h
|
| 205 |
+
|
| 206 |
+
This mirrors the `fused_add_rms_norm` used in real LLM inference stacks
|
| 207 |
+
(e.g. the pre-norm transformer block: the normalized output `y` feeds the
|
| 208 |
+
sublayer, while the un-normalized sum `h` is carried forward as the next
|
| 209 |
+
residual). We return BOTH so callers can thread the residual stream, which
|
| 210 |
+
is exactly why the fused form exists.
|
| 211 |
+
|
| 212 |
+
HONESTY: "fused" here means *logically* fused (one Python op, one float32
|
| 213 |
+
cast path, the add done in float32 alongside the norm) — it is a correct,
|
| 214 |
+
allocation-conscious pure-PyTorch reference, not a hand-written fused CUDA
|
| 215 |
+
kernel. No speed claims are made.
|
| 216 |
+
|
| 217 |
+
The add is performed in float32 so that, for float16/bfloat16 inputs, the
|
| 218 |
+
residual accumulation does not lose precision before normalization — this
|
| 219 |
+
matches high-quality reference implementations.
|
| 220 |
+
"""
|
| 221 |
+
_check_input(x, "x")
|
| 222 |
+
_check_input(residual, "residual")
|
| 223 |
+
_check_eps(eps)
|
| 224 |
+
if residual.shape != x.shape:
|
| 225 |
+
raise ValueError(
|
| 226 |
+
f"residual shape {tuple(residual.shape)} must equal x shape "
|
| 227 |
+
f"{tuple(x.shape)} for the residual add"
|
| 228 |
+
)
|
| 229 |
+
if residual.device != x.device:
|
| 230 |
+
raise ValueError(
|
| 231 |
+
f"residual is on device {residual.device} but x is on {x.device}; "
|
| 232 |
+
f"move them to the same device"
|
| 233 |
+
)
|
| 234 |
+
_check_affine(x, weight, "weight")
|
| 235 |
+
|
| 236 |
+
in_dtype = x.dtype
|
| 237 |
+
cdt = _compute_dtype(in_dtype)
|
| 238 |
+
# Add in compute dtype, keep both the normalized output and the residual.
|
| 239 |
+
hf = x.to(cdt) + residual.to(cdt)
|
| 240 |
+
new_residual = hf.to(in_dtype)
|
| 241 |
+
variance = hf.pow(2).mean(dim=-1, keepdim=True)
|
| 242 |
+
yf = hf * torch.rsqrt(variance + eps)
|
| 243 |
+
out = yf.to(in_dtype)
|
| 244 |
+
if weight is not None:
|
| 245 |
+
out = out * weight
|
| 246 |
+
return out, new_residual
|
build/torch-cpu/szl_lambda_gate/governed_norm/_receipt.py
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
+
# © 2026 SZL Holdings · Stephen P. Lutar · ORCID 0009-0001-0110-4173
|
| 3 |
+
"""Content-addressed governance receipts for normalization calls.
|
| 4 |
+
|
| 5 |
+
SZL Holdings' provenance doctrine applied at the kernel layer: when a
|
| 6 |
+
normalization runs in *governed* mode, it emits a small, deterministic
|
| 7 |
+
receipt describing the call — input shape/dtype, eps, and a SHA3-256 digest
|
| 8 |
+
of the (quantized) output tensor — and hash-chains it to the previous
|
| 9 |
+
receipt. This makes a sequence of kernel calls independently auditable
|
| 10 |
+
without trusting the caller.
|
| 11 |
+
|
| 12 |
+
HONESTY:
|
| 13 |
+
- The digest is a real SHA3-256 over the output bytes (rounded to a fixed
|
| 14 |
+
decimal precision so it is reproducible across runs/devices). It is an
|
| 15 |
+
integrity fingerprint, NOT a cryptographic signature — we never claim
|
| 16 |
+
it proves authorship. DSSE signing is a separate, out-of-band concern.
|
| 17 |
+
- Receipts are kept in an in-process, append-only chain. Nothing is written
|
| 18 |
+
to disk or the network from inside the kernel.
|
| 19 |
+
- Stdlib + torch (+ numpy for the output digest) only — Kernel Hub
|
| 20 |
+
universal-kernel requirement.
|
| 21 |
+
- The canonical szl-receipt v0.2.0 evidence binding (``emit_receipt``) is
|
| 22 |
+
ADDITIVE and IMPORT-GUARDED: with szl-receipt absent it returns ``None`` and
|
| 23 |
+
the kernel runs unchanged. It binds subject / input-digest / output-digest /
|
| 24 |
+
policy-id / energy; energy is the literal string "UNAVAILABLE" because this
|
| 25 |
+
kernel measures NO joules — a value is never fabricated. Like the SHA3-256
|
| 26 |
+
chain, it is an EVIDENCE trail, NOT a proof of correctness.
|
| 27 |
+
"""
|
| 28 |
+
import hashlib
|
| 29 |
+
import json
|
| 30 |
+
import threading
|
| 31 |
+
import time
|
| 32 |
+
from typing import Any, Dict, List, Optional, Union
|
| 33 |
+
|
| 34 |
+
import torch
|
| 35 |
+
|
| 36 |
+
_GENESIS = "0" * 64
|
| 37 |
+
|
| 38 |
+
# Logical signing-authority label stamped onto signature envelopes.
|
| 39 |
+
_ORGAN = "szl-governed-norm"
|
| 40 |
+
|
| 41 |
+
# Governing policy id bound into every canonical szl-receipt evidence binding.
|
| 42 |
+
_POLICY_ID = "szl-governed-norm/provenance@v1"
|
| 43 |
+
|
| 44 |
+
# This universal kernel measures NO joules. The honesty doctrine forbids
|
| 45 |
+
# fabricating an energy value, so the canonical binding records the literal
|
| 46 |
+
# string "UNAVAILABLE" rather than a placeholder number.
|
| 47 |
+
_ENERGY_UNAVAILABLE = "UNAVAILABLE"
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
def _maybe_sign(
|
| 51 |
+
body: Dict[str, Any],
|
| 52 |
+
sign_key: Optional[Union[str, bytes]],
|
| 53 |
+
organ: str,
|
| 54 |
+
) -> Optional[Dict[str, Any]]:
|
| 55 |
+
"""ADDITIVE szl-receipt signature layer over the receipt *body*.
|
| 56 |
+
|
| 57 |
+
Returns a DSSE envelope (from ``szl_receipt.sign_receipt``) covering the
|
| 58 |
+
exact canonical body, or ``None`` when szl-receipt is not installed (the
|
| 59 |
+
kernel then behaves exactly as before). Doctrine: with no *sign_key* the
|
| 60 |
+
envelope is UNSIGNED-honest (``signed=False``); a signature is NEVER
|
| 61 |
+
fabricated. This is distinct from and additive to the SHA3-256 chain
|
| 62 |
+
integrity hash (``digest``) — szl-receipt's envelope carries its own
|
| 63 |
+
SHA-256 ``digest``/``algo`` so the two integrity hashes are explicit.
|
| 64 |
+
"""
|
| 65 |
+
try:
|
| 66 |
+
from szl_receipt import Receipt, sign_receipt
|
| 67 |
+
except Exception: # noqa: BLE001 - signing is optional; absence is honest
|
| 68 |
+
return None
|
| 69 |
+
env = sign_receipt(Receipt(kind="governed-norm", body=body),
|
| 70 |
+
sign_key, organ=organ)
|
| 71 |
+
return env
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
def _tensor_digest(t: torch.Tensor, decimals: int = 6) -> str:
|
| 75 |
+
"""Deterministic SHA3-256 over a tensor's rounded float32 contents.
|
| 76 |
+
|
| 77 |
+
Rounding to a fixed number of decimals makes the digest stable across
|
| 78 |
+
devices/dtypes for the same logical values (tiny FP noise won't change
|
| 79 |
+
it). This is an integrity fingerprint, not a signature.
|
| 80 |
+
"""
|
| 81 |
+
flat = t.detach().to(torch.float32).reshape(-1)
|
| 82 |
+
# Round to `decimals` places, integerize, hash the raw bytes. CPU move is
|
| 83 |
+
# required to read bytes; kept O(n) and allocation-light.
|
| 84 |
+
scaled = torch.round(flat * (10 ** decimals)).to(torch.int64).cpu().numpy().tobytes()
|
| 85 |
+
h = hashlib.sha3_256()
|
| 86 |
+
h.update(scaled)
|
| 87 |
+
return h.hexdigest()
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
def _input_digest(x: torch.Tensor, eps: float) -> str:
|
| 91 |
+
"""SHA3-256 over the canonical JSON of a call's input spec.
|
| 92 |
+
|
| 93 |
+
Binds {input shape, dtype, eps} — the *shape* of the call, not the input
|
| 94 |
+
bytes — so the receipt is a compact fingerprint of what produced the
|
| 95 |
+
output. Deterministic and stdlib-only (json + hashlib).
|
| 96 |
+
"""
|
| 97 |
+
spec = {
|
| 98 |
+
"in_shape": list(x.shape),
|
| 99 |
+
"in_dtype": str(x.dtype).replace("torch.", ""),
|
| 100 |
+
"eps": float(eps),
|
| 101 |
+
}
|
| 102 |
+
raw = json.dumps(spec, sort_keys=True, separators=(",", ":")).encode("utf-8")
|
| 103 |
+
return hashlib.sha3_256(raw).hexdigest()
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
def emit_receipt(
|
| 107 |
+
op: str,
|
| 108 |
+
x: torch.Tensor,
|
| 109 |
+
out: torch.Tensor,
|
| 110 |
+
eps: float,
|
| 111 |
+
subject: Optional[str] = None,
|
| 112 |
+
policy_id: str = _POLICY_ID,
|
| 113 |
+
sign_key: Optional[Union[str, bytes]] = None,
|
| 114 |
+
organ: str = _ORGAN,
|
| 115 |
+
) -> Optional[Dict[str, Any]]:
|
| 116 |
+
"""Canonical szl-receipt v0.2.0 evidence binding for a governed-norm call.
|
| 117 |
+
|
| 118 |
+
ADDITIVE and IMPORT-GUARDED: returns ``None`` when szl-receipt is not
|
| 119 |
+
installed, so this universal Kernel-Hub kernel still imports and runs on
|
| 120 |
+
stdlib + torch + numpy alone. When szl-receipt is present it binds an
|
| 121 |
+
EVIDENCE trail and wraps it in a DSSE envelope via ``sign_receipt``:
|
| 122 |
+
|
| 123 |
+
subject organ / norm-call id (who/what emitted this)
|
| 124 |
+
input_digest SHA3-256 over canonical {input shape, dtype, eps}
|
| 125 |
+
output_digest the EXISTING SHA3-256 rounded-tensor digest of ``out``
|
| 126 |
+
policy_id the governing policy id
|
| 127 |
+
energy the literal string "UNAVAILABLE"
|
| 128 |
+
|
| 129 |
+
Doctrine (non-negotiable):
|
| 130 |
+
* A receipt is an integrity/EVIDENCE trail, NOT a proof of correctness.
|
| 131 |
+
* ``energy == "UNAVAILABLE"`` — this kernel measures NO joules; a joule is
|
| 132 |
+
NEVER fabricated.
|
| 133 |
+
* Keyless => UNSIGNED-honest (``signature["signed"] is False``); a
|
| 134 |
+
signature is NEVER fabricated. A real ``sign_key`` yields a real DSSE
|
| 135 |
+
signature over the exact canonical binding.
|
| 136 |
+
|
| 137 |
+
Returns the binding dict (subject/input_digest/output_digest/policy_id/
|
| 138 |
+
energy) with the DSSE envelope under ``signature``, or ``None`` when
|
| 139 |
+
szl-receipt is absent.
|
| 140 |
+
"""
|
| 141 |
+
try:
|
| 142 |
+
from szl_receipt import Receipt, sign_receipt
|
| 143 |
+
except Exception: # noqa: BLE001 - canonical binding is optional; absence is honest
|
| 144 |
+
return None
|
| 145 |
+
body = {
|
| 146 |
+
"subject": subject if subject is not None else f"{organ}/{op}",
|
| 147 |
+
"input_digest": _input_digest(x, eps),
|
| 148 |
+
"output_digest": _tensor_digest(out),
|
| 149 |
+
"policy_id": policy_id,
|
| 150 |
+
"energy": _ENERGY_UNAVAILABLE,
|
| 151 |
+
}
|
| 152 |
+
env = sign_receipt(Receipt(kind="governed-norm", body=body), sign_key, organ=organ)
|
| 153 |
+
return dict(body, signature=env)
|
| 154 |
+
|
| 155 |
+
|
| 156 |
+
class ReceiptChain:
|
| 157 |
+
"""Append-only, SHA3-256 hash-chained log of normalization receipts.
|
| 158 |
+
|
| 159 |
+
Each receipt: {seq, op, in_shape, in_dtype, eps, out_digest, prev, digest, ts}
|
| 160 |
+
digest = SHA3-256 over the canonical JSON body (excluding digest/ts).
|
| 161 |
+
verify() re-walks the chain and returns (ok, depth, first_break_seq).
|
| 162 |
+
"""
|
| 163 |
+
|
| 164 |
+
def __init__(self) -> None:
|
| 165 |
+
self._lock = threading.RLock()
|
| 166 |
+
self._records: List[Dict[str, Any]] = []
|
| 167 |
+
|
| 168 |
+
@staticmethod
|
| 169 |
+
def _digest_body(body: Dict[str, Any]) -> str:
|
| 170 |
+
raw = json.dumps(body, sort_keys=True, separators=(",", ":")).encode("utf-8")
|
| 171 |
+
return hashlib.sha3_256(raw).hexdigest()
|
| 172 |
+
|
| 173 |
+
def emit(
|
| 174 |
+
self,
|
| 175 |
+
op: str,
|
| 176 |
+
x: torch.Tensor,
|
| 177 |
+
out: torch.Tensor,
|
| 178 |
+
eps: float,
|
| 179 |
+
sign_key: Optional[Union[str, bytes]] = None,
|
| 180 |
+
organ: str = _ORGAN,
|
| 181 |
+
policy_id: str = _POLICY_ID,
|
| 182 |
+
) -> Dict[str, Any]:
|
| 183 |
+
with self._lock:
|
| 184 |
+
prev = self._records[-1]["digest"] if self._records else _GENESIS
|
| 185 |
+
seq = len(self._records)
|
| 186 |
+
body = {
|
| 187 |
+
"seq": seq,
|
| 188 |
+
"op": op,
|
| 189 |
+
"in_shape": list(x.shape),
|
| 190 |
+
"in_dtype": str(x.dtype).replace("torch.", ""),
|
| 191 |
+
"eps": float(eps),
|
| 192 |
+
"out_digest": _tensor_digest(out),
|
| 193 |
+
"prev": prev,
|
| 194 |
+
}
|
| 195 |
+
digest = self._digest_body(body)
|
| 196 |
+
rec = dict(body, digest=digest, ts=time.time())
|
| 197 |
+
sig = _maybe_sign(body, sign_key, organ)
|
| 198 |
+
if sig is not None:
|
| 199 |
+
rec["signature"] = sig
|
| 200 |
+
# ADDITIVE canonical szl-receipt v0.2.0 evidence binding. Import-
|
| 201 |
+
# guarded: None when szl-receipt is absent, so the universal kernel
|
| 202 |
+
# keeps working on stdlib + torch + numpy only. Binds subject /
|
| 203 |
+
# input-digest / output-digest / policy-id / energy; energy is
|
| 204 |
+
# "UNAVAILABLE" (no joules measured here). It does NOT enter the
|
| 205 |
+
# SHA3-256 chain body, so verify() is unaffected.
|
| 206 |
+
binding = emit_receipt(
|
| 207 |
+
op, x, out, eps,
|
| 208 |
+
subject=f"{organ}/{op}#{seq}",
|
| 209 |
+
policy_id=policy_id,
|
| 210 |
+
sign_key=sign_key,
|
| 211 |
+
organ=organ,
|
| 212 |
+
)
|
| 213 |
+
if binding is not None:
|
| 214 |
+
rec["receipt"] = binding
|
| 215 |
+
self._records.append(rec)
|
| 216 |
+
return rec
|
| 217 |
+
|
| 218 |
+
def head(self) -> str:
|
| 219 |
+
with self._lock:
|
| 220 |
+
return self._records[-1]["digest"] if self._records else _GENESIS
|
| 221 |
+
|
| 222 |
+
def count(self) -> int:
|
| 223 |
+
with self._lock:
|
| 224 |
+
return len(self._records)
|
| 225 |
+
|
| 226 |
+
def tail(self, n: int = 10) -> List[Dict[str, Any]]:
|
| 227 |
+
with self._lock:
|
| 228 |
+
return list(self._records[-n:])
|
| 229 |
+
|
| 230 |
+
def verify(self):
|
| 231 |
+
"""Re-walk the chain. Returns (ok: bool, depth: int, first_break: int)."""
|
| 232 |
+
with self._lock:
|
| 233 |
+
prev = _GENESIS
|
| 234 |
+
for i, rec in enumerate(self._records):
|
| 235 |
+
body = {k: rec[k] for k in
|
| 236 |
+
("seq", "op", "in_shape", "in_dtype", "eps", "out_digest", "prev")}
|
| 237 |
+
if rec["prev"] != prev or rec["digest"] != self._digest_body(body):
|
| 238 |
+
return (False, len(self._records), i)
|
| 239 |
+
prev = rec["digest"]
|
| 240 |
+
return (True, len(self._records), -1)
|
| 241 |
+
|
| 242 |
+
|
| 243 |
+
# Module-level default chain (opt-in: only written when governed=True is used).
|
| 244 |
+
_DEFAULT_CHAIN: Optional[ReceiptChain] = None
|
| 245 |
+
_chain_lock = threading.Lock()
|
| 246 |
+
|
| 247 |
+
|
| 248 |
+
def default_chain() -> ReceiptChain:
|
| 249 |
+
global _DEFAULT_CHAIN
|
| 250 |
+
with _chain_lock:
|
| 251 |
+
if _DEFAULT_CHAIN is None:
|
| 252 |
+
_DEFAULT_CHAIN = ReceiptChain()
|
| 253 |
+
return _DEFAULT_CHAIN
|
build/torch-cpu/szl_lambda_gate/governed_norm/layers.py
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
+
# © 2026 SZL Holdings · Stephen P. Lutar · ORCID 0009-0001-0110-4173
|
| 3 |
+
"""Hub-compliant kernel layers.
|
| 4 |
+
|
| 5 |
+
Per the Kernel Hub `kernel-requirements`, layers exposed for extension must
|
| 6 |
+
be PURE torch.nn.Module subclasses:
|
| 7 |
+
- no custom __init__,
|
| 8 |
+
- no class variables,
|
| 9 |
+
- only a `forward` method,
|
| 10 |
+
- forward signature compatible with the module it extends.
|
| 11 |
+
|
| 12 |
+
These layers therefore read their parameters (weight/bias/eps) off the
|
| 13 |
+
module instance they are bound to (set by the host model), and only define
|
| 14 |
+
`forward`. They are drop-in replacements for an existing RMSNorm/LayerNorm
|
| 15 |
+
module via the `kernels` layer-mapping mechanism.
|
| 16 |
+
"""
|
| 17 |
+
import torch
|
| 18 |
+
from torch import nn
|
| 19 |
+
|
| 20 |
+
from ._norm import fused_add_rms_norm, layer_norm, rms_norm
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
class RMSNorm(nn.Module):
|
| 24 |
+
"""Pure RMSNorm layer. Expects the host module to provide `self.weight`
|
| 25 |
+
(optional) and `self.variance_epsilon` or `self.eps`."""
|
| 26 |
+
|
| 27 |
+
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
|
| 28 |
+
weight = getattr(self, "weight", None)
|
| 29 |
+
eps = getattr(self, "variance_epsilon", None)
|
| 30 |
+
if eps is None:
|
| 31 |
+
eps = getattr(self, "eps", 1e-6)
|
| 32 |
+
return rms_norm(hidden_states, weight=weight, eps=float(eps))
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
class LayerNorm(nn.Module):
|
| 36 |
+
"""Pure LayerNorm layer. Expects the host module to provide `self.weight`
|
| 37 |
+
(optional), `self.bias` (optional), and `self.eps`."""
|
| 38 |
+
|
| 39 |
+
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
|
| 40 |
+
weight = getattr(self, "weight", None)
|
| 41 |
+
bias = getattr(self, "bias", None)
|
| 42 |
+
eps = getattr(self, "eps", 1e-5)
|
| 43 |
+
return layer_norm(hidden_states, weight=weight, bias=bias, eps=float(eps))
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
class FusedAddRMSNorm(nn.Module):
|
| 47 |
+
"""Pure residual-add + RMSNorm layer for pre-norm transformer blocks.
|
| 48 |
+
|
| 49 |
+
Expects the host module to provide `self.weight` (optional) and
|
| 50 |
+
`self.variance_epsilon` or `self.eps`. Returns `(normalized, new_residual)`
|
| 51 |
+
where `new_residual = hidden_states + residual` is carried forward as the
|
| 52 |
+
next block's residual stream.
|
| 53 |
+
"""
|
| 54 |
+
|
| 55 |
+
def forward(self, hidden_states: torch.Tensor, residual: torch.Tensor):
|
| 56 |
+
weight = getattr(self, "weight", None)
|
| 57 |
+
eps = getattr(self, "variance_epsilon", None)
|
| 58 |
+
if eps is None:
|
| 59 |
+
eps = getattr(self, "eps", 1e-6)
|
| 60 |
+
return fused_add_rms_norm(hidden_states, residual, weight=weight, eps=float(eps))
|
build/torch-cpu/szl_lambda_gate/layers.py
CHANGED
|
@@ -1,51 +1,54 @@
|
|
| 1 |
-
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
-
# © 2026 SZL Holdings · Stephen P. Lutar · ORCID 0009-0001-0110-4173
|
| 3 |
-
"""Hub-compliant kernel layer for the szl-lambda-gate kernel.
|
| 4 |
-
|
| 5 |
-
Per the Kernel Hub `kernel-requirements`, layers exposed for extension must be
|
| 6 |
-
PURE torch.nn.Module subclasses:
|
| 7 |
-
- no custom __init__,
|
| 8 |
-
- no class variables,
|
| 9 |
-
- only a `forward` method.
|
| 10 |
-
|
| 11 |
-
The layer therefore reads its parameters (weights / threshold) off the module
|
| 12 |
-
instance it is bound to (set by the host model) and only defines `forward`.
|
| 13 |
-
|
| 14 |
-
HONESTY: `LambdaGate` emits an ADVISORY governance signal (the weighted
|
| 15 |
-
geometric mean Λ plus a pass/fail vs threshold). Λ is NOT proven trust; its
|
| 16 |
-
uniqueness is Conjecture 1 (open).
|
| 17 |
-
"""
|
| 18 |
-
import torch
|
| 19 |
-
from torch import nn
|
| 20 |
-
|
| 21 |
-
from ._lambda import lambda_aggregate, lambda_gate
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
class LambdaGate(nn.Module):
|
| 25 |
-
"""Pure Λ-gate layer.
|
| 26 |
-
|
| 27 |
-
Reads optional ``self.weights`` (1-D, length k) and ``self.threshold``
|
| 28 |
-
(float
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
+
# © 2026 SZL Holdings · Stephen P. Lutar · ORCID 0009-0001-0110-4173
|
| 3 |
+
"""Hub-compliant kernel layer for the szl-lambda-gate kernel.
|
| 4 |
+
|
| 5 |
+
Per the Kernel Hub `kernel-requirements`, layers exposed for extension must be
|
| 6 |
+
PURE torch.nn.Module subclasses:
|
| 7 |
+
- no custom __init__,
|
| 8 |
+
- no class variables,
|
| 9 |
+
- only a `forward` method.
|
| 10 |
+
|
| 11 |
+
The layer therefore reads its parameters (weights / threshold) off the module
|
| 12 |
+
instance it is bound to (set by the host model) and only defines `forward`.
|
| 13 |
+
|
| 14 |
+
HONESTY: `LambdaGate` emits an ADVISORY governance signal (the weighted
|
| 15 |
+
geometric mean Λ plus a pass/fail vs threshold). Λ is NOT proven trust; its
|
| 16 |
+
uniqueness is Conjecture 1 (open).
|
| 17 |
+
"""
|
| 18 |
+
import torch
|
| 19 |
+
from torch import nn
|
| 20 |
+
|
| 21 |
+
from ._lambda import lambda_aggregate, lambda_gate
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
class LambdaGate(nn.Module):
|
| 25 |
+
"""Pure Λ-gate layer.
|
| 26 |
+
|
| 27 |
+
Reads optional ``self.weights`` (1-D, length k) and ``self.threshold``
|
| 28 |
+
(float) off the bound module instance. An unset threshold uses the legacy
|
| 29 |
+
0.5 with a DeprecationWarning (see ``lambda_gate``); set it explicitly.
|
| 30 |
+
|
| 31 |
+
forward(axes) -> LambdaGateResult(score, passed, threshold, advisory) where
|
| 32 |
+
``score`` = Λ(axes) over the last dim and ``passed`` = score >= threshold.
|
| 33 |
+
Differentiable in ``score`` w.r.t. ``axes``.
|
| 34 |
+
"""
|
| 35 |
+
|
| 36 |
+
def forward(self, axes: torch.Tensor):
|
| 37 |
+
weights = getattr(self, "weights", None)
|
| 38 |
+
threshold = getattr(self, "threshold", None)
|
| 39 |
+
if threshold is not None:
|
| 40 |
+
threshold = float(threshold)
|
| 41 |
+
return lambda_gate(axes, weights=weights, threshold=threshold)
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
class LambdaAggregate(nn.Module):
|
| 45 |
+
"""Pure Λ-aggregator layer: forward(axes) -> Λ(axes) tensor in [0,1].
|
| 46 |
+
|
| 47 |
+
Reads optional ``self.weights`` (1-D, length k) off the bound module
|
| 48 |
+
instance; uniform weights when absent. Returns just the score (no gate),
|
| 49 |
+
fully differentiable w.r.t. ``axes``.
|
| 50 |
+
"""
|
| 51 |
+
|
| 52 |
+
def forward(self, axes: torch.Tensor) -> torch.Tensor:
|
| 53 |
+
weights = getattr(self, "weights", None)
|
| 54 |
+
return lambda_aggregate(axes, weights=weights)
|
build/torch-universal/__init__.py
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .szl_lambda_gate import * # noqa: F401,F403
|
| 2 |
+
from .szl_lambda_gate import __all__
|
build/torch-universal/metadata.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"backend":{"type":"cpu"},"digest":{"algorithm":"sha256","files":{"__init__.py":"NuinjlWRNlWgg0y2D61HEaKtW/VOktRHP8w2Nj3opjQ=","szl_lambda_gate/__init__.py":"QpfkkacuKUGxXitqBYLq0nM4NnCsX7uNFOLHbeye0V0=","szl_lambda_gate/_lambda.py":"+VOjgBnJGm+oqcQR9VuqT18rnnbXFTu6VLuKXZRprzg=","szl_lambda_gate/_ops.py":"at8dxC56nm2pFOME2pcgQ45ZwjQQThHz5cyqRezuI7Y=","szl_lambda_gate/_v1.py":"msLY/gpH/xkeunbmKx6d2bK1T2S6DD2GMG4M3Vi8Row=","szl_lambda_gate/governed_norm/__init__.py":"UP5PsJoWXQrpp4Hw7zUVOIx1KhTN6V9zaHhU9Zp9+tw=","szl_lambda_gate/governed_norm/_norm.py":"z3Ks4oHshsUEQm914j7r22YaHjoS5N/MbIufpjSwOUI=","szl_lambda_gate/governed_norm/_receipt.py":"2xJEvRhK/9ypKb04rHKLJpb4KtVlqvgtFMo4RdTXSKk=","szl_lambda_gate/governed_norm/layers.py":"15yDNVyyMyudDp2YWEuGQ05IZTLifTBlLfEBWLMPw3s=","szl_lambda_gate/layers.py":"UdpHjhhe5NFtqplxvcvc/LsMDFt4N6ACuXzQXLlo6nU="}},"id":"_szl_lambda_gate_universal_7cb79cba7ecb5dbb9da59b2d6b516a98d2a114d6","license":"Apache-2.0","name":"szl-lambda-gate","python-depends":[],"source":"https://github.com/szl-holdings/szl-lambda-gate","universal":true,"version":1}
|
build/torch-universal/szl_lambda_gate/__init__.py
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
+
# © 2026 SZL Holdings · Stephen P. Lutar · ORCID 0009-0001-0110-4173
|
| 3 |
+
"""szl_lambda_gate — the Lambda-Spine aggregator (Λ) as a universal kernel.
|
| 4 |
+
|
| 5 |
+
A pure-PyTorch (universal) kernel from SZL Holdings for the Hugging Face
|
| 6 |
+
Kernel Hub. It ports the canonical Λ aggregator into a differentiable,
|
| 7 |
+
torch.compile-friendly torch op:
|
| 8 |
+
|
| 9 |
+
Λ(x) = ∏ xᵢ^{wᵢ}, Σwᵢ = 1, wᵢ > 0, xᵢ ∈ [0,1] (weighted geometric mean)
|
| 10 |
+
|
| 11 |
+
plus an ADVISORY governance gate (Λ vs threshold), the four carried axioms as
|
| 12 |
+
real runtime self-checks, and pure nn.Module layers.
|
| 13 |
+
|
| 14 |
+
Load from the Hub:
|
| 15 |
+
|
| 16 |
+
import torch
|
| 17 |
+
from kernels import get_kernel
|
| 18 |
+
|
| 19 |
+
lg = get_kernel("SZLHOLDINGS/szl-lambda-gate")
|
| 20 |
+
axes = torch.tensor([0.9, 0.8, 0.95]) # axis scores in [0,1]
|
| 21 |
+
score = lg.lambda_aggregate(axes) # Λ(x) ∈ [0,1]
|
| 22 |
+
res = lg.lambda_gate(axes, threshold=0.5) # ADVISORY pass/fail
|
| 23 |
+
print(res.score, res.passed, res.advisory)
|
| 24 |
+
|
| 25 |
+
# szl.lambda/v1 strict gate (spec/szl.lambda.v1.json): no clamping, no
|
| 26 |
+
# renormalisation, tau required; bad input -> verdict BLOCK with a code.
|
| 27 |
+
w = torch.tensor([0.4, 0.3, 0.3], dtype=torch.float64)
|
| 28 |
+
v1 = lg.lambda_v1_gate(axes.double(), w, tau=0.8)
|
| 29 |
+
print(v1.verdict, v1.code) # GO / NO_GO / ABSTAIN / BLOCK
|
| 30 |
+
|
| 31 |
+
WHAT Λ IS / IS NOT (HONESTY — SZL Holdings doctrine v11):
|
| 32 |
+
Λ is the weighted-geometric-mean aggregator — a non-compensatory, ADVISORY
|
| 33 |
+
way to roll axis scores in [0,1] into one number (any zeroed axis zeroes the
|
| 34 |
+
aggregate). It is NOT "proven trust" and NOT a closed theorem: Λ-uniqueness
|
| 35 |
+
remains Conjecture 1 (OPEN — an unresolved CAUCHY_ND step plus a missing
|
| 36 |
+
symmetry axiom). Label it honestly everywhere; a gate "pass" is advisory.
|
| 37 |
+
|
| 38 |
+
PROVENANCE: backed by the Lean 4 formalization szl-holdings/lutar-lean
|
| 39 |
+
(749 declarations / 14 axioms / 163 tracked sorries),
|
| 40 |
+
DOI 10.5281/zenodo.20434308 (lutar-lean). Λ uniqueness = Conjecture 1 (open).
|
| 41 |
+
"""
|
| 42 |
+
from typing import Optional
|
| 43 |
+
|
| 44 |
+
import torch
|
| 45 |
+
|
| 46 |
+
from . import layers # noqa: F401 (must be importable for Hub layer mapping)
|
| 47 |
+
# CONSOLIDATION (Wave D): the governed-norm universal kernel is folded in here
|
| 48 |
+
# as a subpackage so szl-lambda-gate is the ONE canonical kernels package. The
|
| 49 |
+
# source repo szl-holdings/szl-governed-norm is DEPRECATED and points here;
|
| 50 |
+
# nothing was deleted (additive, reversible copy). Λ stays Conjecture 1.
|
| 51 |
+
from . import governed_norm # noqa: F401 (folded-in governed normalization kernels)
|
| 52 |
+
from ._lambda import YUYAY_AXES, YUYAY_FLOORS, LambdaGateResult
|
| 53 |
+
from ._lambda import _resolve_threshold
|
| 54 |
+
from ._lambda import find_axiom_violation as _find_axiom_violation
|
| 55 |
+
from ._lambda import is_bounded_by_max as _is_bounded_by_max
|
| 56 |
+
from ._lambda import is_egyptian_exact as _is_egyptian_exact
|
| 57 |
+
from ._lambda import is_homogeneous as _is_homogeneous
|
| 58 |
+
from ._lambda import is_monotone as _is_monotone
|
| 59 |
+
from ._lambda import lambda_aggregate as _lambda_aggregate
|
| 60 |
+
from ._lambda import lambda_gate as _lambda_gate
|
| 61 |
+
from ._lambda import lambda_gate_batch as _lambda_gate_batch
|
| 62 |
+
from ._lambda import selfcheck as _selfcheck
|
| 63 |
+
from ._lambda import yuyay_weights as _yuyay_weights
|
| 64 |
+
# szl.lambda/v1 strict entry (spec/szl.lambda.v1.json): validated, coded, tau required.
|
| 65 |
+
from ._v1 import LambdaV1Error, LambdaV1GateResult, lambda_v1, lambda_v1_gate
|
| 66 |
+
|
| 67 |
+
__all__ = [
|
| 68 |
+
"lambda_aggregate",
|
| 69 |
+
"lambda_gate",
|
| 70 |
+
"lambda_gate_batch",
|
| 71 |
+
"LambdaGateResult",
|
| 72 |
+
"lambda_v1",
|
| 73 |
+
"lambda_v1_gate",
|
| 74 |
+
"LambdaV1Error",
|
| 75 |
+
"LambdaV1GateResult",
|
| 76 |
+
"is_monotone",
|
| 77 |
+
"is_egyptian_exact",
|
| 78 |
+
"is_bounded_by_max",
|
| 79 |
+
"is_homogeneous",
|
| 80 |
+
"find_axiom_violation",
|
| 81 |
+
"selfcheck",
|
| 82 |
+
"yuyay_weights",
|
| 83 |
+
"YUYAY_AXES",
|
| 84 |
+
"YUYAY_FLOORS",
|
| 85 |
+
"layers",
|
| 86 |
+
"DOCTRINE_FOOTER",
|
| 87 |
+
"PROVENANCE",
|
| 88 |
+
"__version__",
|
| 89 |
+
# ---- folded-in governed-norm kernels (Wave D consolidation) ----
|
| 90 |
+
"governed_norm",
|
| 91 |
+
"rms_norm",
|
| 92 |
+
"layer_norm",
|
| 93 |
+
"fused_add_rms_norm",
|
| 94 |
+
]
|
| 95 |
+
|
| 96 |
+
# ---- folded-in governed-norm surface (Wave D consolidation) ---------------- #
|
| 97 |
+
# Convenience top-level re-exports of the governed normalization kernels that
|
| 98 |
+
# were absorbed from szl-governed-norm. The full surface (ReceiptChain,
|
| 99 |
+
# emit_receipt, receipt_* helpers, selfcheck, layers) lives under
|
| 100 |
+
# ``szl_lambda_gate.governed_norm``. These are a DIFFERENT kernel family from Λ
|
| 101 |
+
# (normalization, not the Λ aggregator); Λ itself remains Conjecture 1
|
| 102 |
+
# (advisory, uniqueness OPEN) and is never described as proven trust.
|
| 103 |
+
rms_norm = governed_norm.rms_norm
|
| 104 |
+
layer_norm = governed_norm.layer_norm
|
| 105 |
+
fused_add_rms_norm = governed_norm.fused_add_rms_norm
|
| 106 |
+
|
| 107 |
+
__version__ = "0.2.0"
|
| 108 |
+
DOCTRINE_FOOTER = (
|
| 109 |
+
"SZL Holdings · Λ = Conjecture 1 (ADVISORY, weighted geometric mean) · "
|
| 110 |
+
"uniqueness OPEN · NOT proven trust · honesty over checklist"
|
| 111 |
+
)
|
| 112 |
+
PROVENANCE = {
|
| 113 |
+
"lean_repo": "szl-holdings/lutar-lean",
|
| 114 |
+
"lean_declarations": 749,
|
| 115 |
+
"lean_axioms": 14,
|
| 116 |
+
"lean_tracked_sorries": 163,
|
| 117 |
+
"doi_lutar_lean": "10.5281/zenodo.20434308",
|
| 118 |
+
"lambda_status": "Conjecture 1 (open) — uniqueness unproven; advisory only",
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
def lambda_aggregate(
|
| 123 |
+
axes: torch.Tensor,
|
| 124 |
+
weights: Optional[torch.Tensor] = None,
|
| 125 |
+
) -> torch.Tensor:
|
| 126 |
+
"""Λ(x) = ∏ xᵢ^{wᵢ}, the weighted geometric mean over the last dim of axes.
|
| 127 |
+
|
| 128 |
+
See ``szl_lambda_gate._lambda.lambda_aggregate``. Axis scores in [0,1],
|
| 129 |
+
uniform weights when ``weights`` is None. Differentiable, batched, and
|
| 130 |
+
torch.compile-friendly. ADVISORY — NOT proven trust.
|
| 131 |
+
"""
|
| 132 |
+
return _lambda_aggregate(axes, weights=weights)
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
def lambda_gate(
|
| 136 |
+
axes: torch.Tensor,
|
| 137 |
+
weights: Optional[torch.Tensor] = None,
|
| 138 |
+
threshold: Optional[float] = None,
|
| 139 |
+
) -> LambdaGateResult:
|
| 140 |
+
"""ADVISORY Λ governance gate: returns LambdaGateResult(score, passed,
|
| 141 |
+
threshold, advisory). ``passed`` = Λ(axes) >= threshold. ``threshold`` must
|
| 142 |
+
lie within Λ's range [0,1] (a value outside it is a misconfiguration — a
|
| 143 |
+
negative threshold would advisory-pass a fully-failing Λ=0 candidate — and
|
| 144 |
+
is rejected). Omitting it uses the legacy 0.5 with a DeprecationWarning
|
| 145 |
+
(policy_tau is 0.8); pass it, or use ``lambda_v1_gate(axes, weights, tau)``.
|
| 146 |
+
A pass is an advisory, non-compensatory signal — NOT proven trust
|
| 147 |
+
(Λ = Conjecture 1).
|
| 148 |
+
"""
|
| 149 |
+
threshold = _resolve_threshold(threshold, stacklevel=2)
|
| 150 |
+
return _lambda_gate(axes, weights=weights, threshold=threshold)
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
def lambda_gate_batch(
|
| 154 |
+
candidates: torch.Tensor,
|
| 155 |
+
weights: Optional[torch.Tensor] = None,
|
| 156 |
+
threshold: Optional[float] = None,
|
| 157 |
+
) -> LambdaGateResult:
|
| 158 |
+
"""ADVISORY batch gate over many candidate action-vectors (shape (..., N, k)).
|
| 159 |
+
|
| 160 |
+
The realistic per-inference-step call: score all N candidates at once and
|
| 161 |
+
return the advisory pass mask. Returns LambdaGateResult(score, passed,
|
| 162 |
+
threshold, advisory) with score/passed of shape (..., N). ``threshold``
|
| 163 |
+
must lie within Λ's range [0,1] (same domain guard as ``lambda_gate``);
|
| 164 |
+
omitting it uses the legacy 0.5 with a DeprecationWarning.
|
| 165 |
+
NOT proven trust.
|
| 166 |
+
"""
|
| 167 |
+
threshold = _resolve_threshold(threshold, stacklevel=2)
|
| 168 |
+
return _lambda_gate_batch(candidates, weights=weights, threshold=threshold)
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
def yuyay_weights(dtype: torch.dtype = torch.float64, device=None) -> torch.Tensor:
|
| 172 |
+
"""Canonical 13-axis Yuyay Λ weight vector (uniform 1/13), ADVISORY only.
|
| 173 |
+
|
| 174 |
+
Use as ``weights`` over the 13 ``YUYAY_AXES``. The yuyay_v3 gate is a
|
| 175 |
+
conjunctive AND with per-axis floors (``YUYAY_FLOORS``); this Λ roll-up is
|
| 176 |
+
the weighted geometric mean and is ADVISORY — NOT proven trust.
|
| 177 |
+
"""
|
| 178 |
+
return _yuyay_weights(dtype=dtype, device=device)
|
| 179 |
+
|
| 180 |
+
|
| 181 |
+
def find_axiom_violation(k=5, trials=200, weights=None, seed=0, tol=1e-6):
|
| 182 |
+
"""Random-search for any A1–A4 violation; returns (axiom, axes, weights) or
|
| 183 |
+
None. An honest falsification attempt — finding nothing is evidence, not a
|
| 184 |
+
proof (Λ-uniqueness is Conjecture 1, open).
|
| 185 |
+
"""
|
| 186 |
+
return _find_axiom_violation(k=k, trials=trials, weights=weights, seed=seed, tol=tol)
|
| 187 |
+
|
| 188 |
+
|
| 189 |
+
def selfcheck(k=5, trials=64, seed=0) -> dict:
|
| 190 |
+
"""Expose the A1–A4 empirical self-checks + version as a single verdict dict.
|
| 191 |
+
|
| 192 |
+
Callable as get_kernel(...).selfcheck(). EMPIRICAL checks on sampled inputs,
|
| 193 |
+
NOT a proof of Λ-uniqueness (Conjecture 1, open). Advisory only.
|
| 194 |
+
"""
|
| 195 |
+
return _selfcheck(k=k, trials=trials, seed=seed)
|
| 196 |
+
|
| 197 |
+
|
| 198 |
+
# ---- axiom runtime self-checks (real, verifiable; NOT a uniqueness proof) -- #
|
| 199 |
+
def is_monotone(axes, weights=None, delta=0.05, tol=1e-7) -> bool:
|
| 200 |
+
"""A1 IsMonotone self-check: Λ is non-decreasing in each axis (on this data)."""
|
| 201 |
+
return _is_monotone(axes, weights=weights, delta=delta, tol=tol)
|
| 202 |
+
|
| 203 |
+
|
| 204 |
+
def is_egyptian_exact(c, k=3, weights=None, tol=1e-5) -> bool:
|
| 205 |
+
"""A3 IsEgyptianExact self-check: Λ(c, …, c) = c."""
|
| 206 |
+
return _is_egyptian_exact(c, k=k, weights=weights, tol=tol)
|
| 207 |
+
|
| 208 |
+
|
| 209 |
+
def is_bounded_by_max(axes, weights=None, tol=1e-6) -> bool:
|
| 210 |
+
"""A4 IsBounded self-check: Λ(x) ≤ maxᵢ xᵢ."""
|
| 211 |
+
return _is_bounded_by_max(axes, weights=weights, tol=tol)
|
| 212 |
+
|
| 213 |
+
|
| 214 |
+
def is_homogeneous(axes, t, weights=None, tol=1e-5) -> bool:
|
| 215 |
+
"""A2 IsHomogeneous(degree 1) self-check: Λ(t·x) = t·Λ(x)."""
|
| 216 |
+
return _is_homogeneous(axes, t, weights=weights, tol=tol)
|
build/torch-universal/szl_lambda_gate/_lambda.py
ADDED
|
@@ -0,0 +1,556 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
+
# © 2026 SZL Holdings · Stephen P. Lutar · ORCID 0009-0001-0110-4173
|
| 3 |
+
"""Pure-PyTorch Lambda-Spine aggregator (Λ) for the szl-lambda-gate kernel.
|
| 4 |
+
|
| 5 |
+
Λ(x) = ∏ xᵢ^{wᵢ}, Σwᵢ = 1, wᵢ > 0, xᵢ ∈ [0,1] (weighted geometric mean)
|
| 6 |
+
|
| 7 |
+
This is a TORCH port of the canonical pure-Python reference
|
| 8 |
+
(packages/puriq-os/puriq_os/lambda_aggregator.py — saved alongside this kernel
|
| 9 |
+
as lambda_aggregator_source.py). It is a correctness reference, computed via
|
| 10 |
+
logs in float32 for stability, differentiable (autograd works), and
|
| 11 |
+
torch.compile-friendly. Depends ONLY on torch + the Python standard library
|
| 12 |
+
(a Kernel Hub requirement for universal kernels).
|
| 13 |
+
|
| 14 |
+
WHAT Λ IS / IS NOT (HONESTY — SZL Holdings doctrine v11):
|
| 15 |
+
Λ is the *weighted-geometric-mean aggregator*: a non-compensatory way to
|
| 16 |
+
combine axis scores in [0,1] into one number. It is ADVISORY governance
|
| 17 |
+
signal — a conservative roll-up where any single zeroed axis drives the
|
| 18 |
+
aggregate to 0. It is NOT "proven trust" and NOT a closed theorem. Its
|
| 19 |
+
*uniqueness* (that the weighted geometric mean is the only aggregator
|
| 20 |
+
satisfying the carried axioms) remains Conjecture 1 — OPEN (an unresolved
|
| 21 |
+
CAUCHY_ND step plus a missing symmetry axiom in the Lean development). Do
|
| 22 |
+
not describe Λ as proven trust anywhere.
|
| 23 |
+
|
| 24 |
+
PRIOR ART (honest attribution): the weighted geometric mean as a *less-
|
| 25 |
+
compensatory* composite-indicator aggregator is established practice — the
|
| 26 |
+
UN HDI (arithmetic→geometric switch, 2010), the OECD Handbook on
|
| 27 |
+
Constructing Composite Indicators (2008), and the UNECE well-being
|
| 28 |
+
guidelines all use it "to limit the compensation effect". The veto / cut-off
|
| 29 |
+
idea (a single failing criterion blocks a pass regardless of the others) is
|
| 30 |
+
the ELECTRE veto threshold / "satisficing" minimum-threshold screen. The
|
| 31 |
+
13-axis conjunctive form exposed by :func:`yuyay_weights` is SZL's own
|
| 32 |
+
yuyay_v3 "Heart" gate. None of this makes Λ "proven trust"; the gate is
|
| 33 |
+
ADVISORY (a11oy: "the advisory Λ trust score is a research conjecture, not a
|
| 34 |
+
pass/fail oracle").
|
| 35 |
+
|
| 36 |
+
PROVENANCE: backed by the Lean 4 formalization szl-holdings/lutar-lean
|
| 37 |
+
(749 declarations / 14 axioms / 163 tracked sorries),
|
| 38 |
+
DOI 10.5281/zenodo.20434308 (lutar-lean).
|
| 39 |
+
Λ uniqueness = Conjecture 1 (open).
|
| 40 |
+
|
| 41 |
+
Axioms carried (Lutar/Axioms.lean), available below as runtime self-checks:
|
| 42 |
+
A1 IsMonotone — Λ is non-decreasing in each axis
|
| 43 |
+
A2 IsHomogeneous — Λ(t·x) = t·Λ(x) (degree 1)
|
| 44 |
+
A3 IsEgyptianExact — Λ(c,…,c) = c (the uniform-diagonal fixpoint)
|
| 45 |
+
A4 IsBounded(by max) — Λ(x) ≤ maxᵢ xᵢ
|
| 46 |
+
"""
|
| 47 |
+
import warnings
|
| 48 |
+
from typing import Optional
|
| 49 |
+
|
| 50 |
+
import torch
|
| 51 |
+
|
| 52 |
+
# ---- the deprecated implicit threshold ------------------------------------- #
|
| 53 |
+
# lambda_gate / lambda_gate_batch / layers.LambdaGate used to default to 0.5.
|
| 54 |
+
# That default stays, so there is no behaviour change, but omitting the
|
| 55 |
+
# threshold now raises a DeprecationWarning. The admit policy value is
|
| 56 |
+
# policy_tau in frontier/model_admit_contract.v1.json, and the strict
|
| 57 |
+
# szl.lambda/v1 gate (_v1.lambda_v1_gate) takes tau as a required argument.
|
| 58 |
+
_LEGACY_DEFAULT_THRESHOLD = 0.5
|
| 59 |
+
_DEFAULT_THRESHOLD_WARNING = (
|
| 60 |
+
"default threshold 0.5 differs from policy_tau 0.8; pass tau. Omitting the "
|
| 61 |
+
"threshold of lambda_gate / lambda_gate_batch / LambdaGate is deprecated: "
|
| 62 |
+
"pass threshold= explicitly (policy_tau is in "
|
| 63 |
+
"frontier/model_admit_contract.v1.json), or use the strict "
|
| 64 |
+
"lambda_v1_gate(axes, weights, tau). The legacy default 0.5 still applies."
|
| 65 |
+
)
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
def _resolve_threshold(threshold: Optional[float], stacklevel: int) -> float:
|
| 69 |
+
"""``None`` (the threshold was omitted) -> the legacy 0.5, with a DeprecationWarning.
|
| 70 |
+
|
| 71 |
+
``stacklevel`` is what the calling entry point would pass to
|
| 72 |
+
``warnings.warn`` itself (2 = its caller), so the warning names the line
|
| 73 |
+
that omitted the threshold. Dynamo cannot trace ``warnings.warn`` (a
|
| 74 |
+
fullgraph compile would fail), so the warning is skipped while
|
| 75 |
+
``torch.compile`` traces; the default is applied either way.
|
| 76 |
+
"""
|
| 77 |
+
if threshold is not None:
|
| 78 |
+
return threshold
|
| 79 |
+
if not bool(getattr(torch.compiler, "is_compiling", lambda: False)()):
|
| 80 |
+
warnings.warn(_DEFAULT_THRESHOLD_WARNING, DeprecationWarning, stacklevel=stacklevel + 1)
|
| 81 |
+
return _LEGACY_DEFAULT_THRESHOLD
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
# Compute reductions/log-sum in float32 for stability when inputs are low
|
| 85 |
+
# precision; keep float64 inputs in float64 (downcasting would break gradcheck
|
| 86 |
+
# and silently lose precision).
|
| 87 |
+
_SUPPORTED_DTYPES = (torch.float16, torch.bfloat16, torch.float32, torch.float64)
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
def _compute_dtype(in_dtype: torch.dtype) -> torch.dtype:
|
| 91 |
+
return torch.float32 if in_dtype in (torch.float16, torch.bfloat16) else in_dtype
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
def _check_axes(axes: torch.Tensor) -> None:
|
| 95 |
+
"""Cheap, allocation-free metadata guards on the axis-score tensor.
|
| 96 |
+
|
| 97 |
+
Inspects only type / dtype / rank / last-dim, so it constant-folds under
|
| 98 |
+
torch.compile and adds no tensor work on the happy path.
|
| 99 |
+
"""
|
| 100 |
+
if not isinstance(axes, torch.Tensor):
|
| 101 |
+
raise TypeError(f"axes must be a torch.Tensor, got {type(axes).__name__}")
|
| 102 |
+
if axes.dtype not in _SUPPORTED_DTYPES:
|
| 103 |
+
raise TypeError(
|
| 104 |
+
f"axes has unsupported dtype {axes.dtype}; "
|
| 105 |
+
f"expected one of {tuple(str(d) for d in _SUPPORTED_DTYPES)}"
|
| 106 |
+
)
|
| 107 |
+
if axes.dim() < 1:
|
| 108 |
+
raise ValueError(
|
| 109 |
+
"axes must have at least 1 dimension (the k axis scores live on "
|
| 110 |
+
f"the last dim); got a {axes.dim()}-d tensor"
|
| 111 |
+
)
|
| 112 |
+
if axes.shape[-1] < 1:
|
| 113 |
+
raise ValueError("axes last dimension (k = number of axes) must be >= 1")
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
def _resolve_weights(
|
| 117 |
+
axes: torch.Tensor,
|
| 118 |
+
weights: Optional[torch.Tensor],
|
| 119 |
+
cdt: torch.dtype,
|
| 120 |
+
) -> torch.Tensor:
|
| 121 |
+
"""Return a normalized (Σw = 1) weight vector of shape (k,) in compute dtype.
|
| 122 |
+
|
| 123 |
+
``weights=None`` -> uniform 1/k (the Egyptian-exact diagonal). Otherwise the
|
| 124 |
+
weights must be 1-D of length k, strictly positive, with a positive sum;
|
| 125 |
+
they are normalized so Σwᵢ = 1.
|
| 126 |
+
"""
|
| 127 |
+
k = axes.shape[-1]
|
| 128 |
+
if weights is None:
|
| 129 |
+
return torch.full((k,), 1.0 / k, dtype=cdt, device=axes.device)
|
| 130 |
+
if not isinstance(weights, torch.Tensor):
|
| 131 |
+
raise TypeError(f"weights must be a torch.Tensor or None, got {type(weights).__name__}")
|
| 132 |
+
if weights.device != axes.device:
|
| 133 |
+
raise ValueError(
|
| 134 |
+
f"weights is on device {weights.device} but axes is on {axes.device}; "
|
| 135 |
+
"move them to the same device"
|
| 136 |
+
)
|
| 137 |
+
if weights.dim() != 1 or weights.shape[0] != k:
|
| 138 |
+
raise ValueError(
|
| 139 |
+
f"weights must be 1-D with shape ({k},) to match the last dim of axes; "
|
| 140 |
+
f"got shape {tuple(weights.shape)}"
|
| 141 |
+
)
|
| 142 |
+
wf = weights.to(cdt)
|
| 143 |
+
compiling = bool(getattr(torch.compiler, "is_compiling", lambda: False)())
|
| 144 |
+
if not compiling:
|
| 145 |
+
if not bool(torch.all(torch.isfinite(wf))):
|
| 146 |
+
raise ValueError("weights must all be finite (no NaN/Inf)")
|
| 147 |
+
if bool(torch.any(wf <= 0.0)):
|
| 148 |
+
raise ValueError("weights must be strictly positive (wᵢ > 0)")
|
| 149 |
+
sw = wf.sum()
|
| 150 |
+
if not bool(sw > 0.0):
|
| 151 |
+
raise ValueError("weights must sum to a positive value")
|
| 152 |
+
return wf / sw
|
| 153 |
+
# Compiled path stays in tensor-land. Non-positive weights are a misuse;
|
| 154 |
+
# clamp them away from zero so the graph does not break, then normalize.
|
| 155 |
+
# This path is outside szl.lambda/v1; _v1.lambda_v1 validates before it
|
| 156 |
+
# gets here and is not meant to run under torch.compile.
|
| 157 |
+
wf = torch.where(torch.isfinite(wf), wf, torch.ones_like(wf))
|
| 158 |
+
wf = torch.clamp(wf, min=torch.finfo(wf.dtype).tiny)
|
| 159 |
+
return wf / wf.sum()
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
def lambda_aggregate(
|
| 163 |
+
axes: torch.Tensor,
|
| 164 |
+
weights: Optional[torch.Tensor] = None,
|
| 165 |
+
) -> torch.Tensor:
|
| 166 |
+
"""Weighted geometric mean Λ(x) = ∏ xᵢ^{wᵢ} over the last dim of ``axes``.
|
| 167 |
+
|
| 168 |
+
Λ is the (ADVISORY) Lambda-Spine aggregator. Axis scores are expected in
|
| 169 |
+
[0,1] and are clamped into [0,1]; uniform weights (1/k) are used when
|
| 170 |
+
``weights`` is None — the Egyptian-exact diagonal. Computed via logs in
|
| 171 |
+
float32 (or float64 for float64 inputs) for numerical stability:
|
| 172 |
+
|
| 173 |
+
Λ(x) = exp( Σᵢ wᵢ · log(clamp(xᵢ, 0, 1)) )
|
| 174 |
+
|
| 175 |
+
Non-compensatory zero-routing (A4-consistent): any axis that is zero, OR
|
| 176 |
+
that is NON-FINITE (NaN / ±Inf), is treated as a FAILING axis and drives
|
| 177 |
+
the whole aggregate to exactly 0. This is the conservative governance
|
| 178 |
+
choice — a garbage/invalid axis must never silently pass as a "perfect"
|
| 179 |
+
(clamped-to-1) axis, and the output (and its gradient) stay finite and in
|
| 180 |
+
[0,1] for every input. Zeros/non-finite axes are routed explicitly so
|
| 181 |
+
log(0) = -inf and log(NaN) = NaN never produce a NaN value or gradient.
|
| 182 |
+
|
| 183 |
+
Args:
|
| 184 |
+
axes: tensor of shape (..., k) of axis scores in [0,1]. Batched:
|
| 185 |
+
the reduction is over the last dim, leading dims are batch.
|
| 186 |
+
weights: optional 1-D tensor of shape (k,); None -> uniform. Normalized
|
| 187 |
+
internally so Σwᵢ = 1.
|
| 188 |
+
|
| 189 |
+
Returns:
|
| 190 |
+
tensor of shape (...) — Λ(x) ∈ [0,1] per batch row. Differentiable
|
| 191 |
+
w.r.t. ``axes`` (and ``weights``).
|
| 192 |
+
|
| 193 |
+
NOT the szl.lambda/v1 contract: this function clamps, zero-routes NaN/±Inf
|
| 194 |
+
and renormalises (pinned in tests/test_lambda_v1_torch_divergence.py). For
|
| 195 |
+
validated, coded errors use :func:`szl_lambda_gate._v1.lambda_v1`.
|
| 196 |
+
|
| 197 |
+
HONESTY: this is a non-compensatory governance roll-up, NOT proven trust.
|
| 198 |
+
Λ-uniqueness is Conjecture 1 (open).
|
| 199 |
+
"""
|
| 200 |
+
_check_axes(axes)
|
| 201 |
+
in_dtype = axes.dtype
|
| 202 |
+
cdt = _compute_dtype(in_dtype)
|
| 203 |
+
xf = axes.to(cdt)
|
| 204 |
+
w = _resolve_weights(axes, weights, cdt) # (k,), Σw=1
|
| 205 |
+
|
| 206 |
+
# A "bad" axis is one that fails non-compensatorily: a non-positive score
|
| 207 |
+
# OR a non-finite value (NaN / ±Inf). clamp(+inf)=1 would otherwise count a
|
| 208 |
+
# garbage axis as perfect, and clamp(NaN)=NaN would poison the product — we
|
| 209 |
+
# treat BOTH as failing (zeroing) axes. Detect non-finite on the RAW input.
|
| 210 |
+
finite_mask = torch.isfinite(xf)
|
| 211 |
+
xc = xf.clamp(0.0, 1.0)
|
| 212 |
+
bad_mask = (~finite_mask) | (xc <= 0.0)
|
| 213 |
+
any_bad = torch.any(bad_mask, dim=-1) # (...)
|
| 214 |
+
|
| 215 |
+
# Replace bad axes with 1.0 before the log purely to keep log finite and the
|
| 216 |
+
# gradient well-defined; the bad-axis contribution is reinstated via any_bad.
|
| 217 |
+
safe = torch.where(bad_mask, torch.ones_like(xc), xc)
|
| 218 |
+
logx = torch.log(safe) # (..., k)
|
| 219 |
+
acc = (logx * w).sum(dim=-1) # (...) weighted log-sum
|
| 220 |
+
val = torch.exp(acc) # (...) Λ before zero-routing
|
| 221 |
+
|
| 222 |
+
out = torch.where(any_bad, torch.zeros_like(val), val)
|
| 223 |
+
out = out.clamp(0.0, 1.0)
|
| 224 |
+
return out.to(in_dtype)
|
| 225 |
+
|
| 226 |
+
|
| 227 |
+
def lambda_gate(
|
| 228 |
+
axes: torch.Tensor,
|
| 229 |
+
weights: Optional[torch.Tensor] = None,
|
| 230 |
+
threshold: Optional[float] = None,
|
| 231 |
+
):
|
| 232 |
+
"""ADVISORY governance gate over Λ(x): score plus a pass/fail vs threshold.
|
| 233 |
+
|
| 234 |
+
Computes Λ(x) (see :func:`lambda_aggregate`) and compares it to
|
| 235 |
+
``threshold``: pass := Λ(x) >= threshold.
|
| 236 |
+
|
| 237 |
+
DEPRECATED DEFAULT: omitting ``threshold`` (or passing ``None``) still uses
|
| 238 |
+
the legacy 0.5 but raises a ``DeprecationWarning``, because 0.5 differs
|
| 239 |
+
from the admit contract's policy_tau (0.8). Pass the threshold explicitly,
|
| 240 |
+
or use the strict szl.lambda/v1 gate
|
| 241 |
+
:func:`szl_lambda_gate._v1.lambda_v1_gate`, where tau is required.
|
| 242 |
+
|
| 243 |
+
``threshold`` must be a finite float within Λ's range ``[0, 1]`` (Λ is the
|
| 244 |
+
weighted geometric mean over [0,1]). This bound is enforced: a threshold
|
| 245 |
+
below 0 or above 1 is meaningless for the advisory gate and is rejected —
|
| 246 |
+
see the non-compensatory rationale below. The domain edges are valid:
|
| 247 |
+
``0.0`` admits every candidate (a permissive "no-gate" boundary) and
|
| 248 |
+
``1.0`` admits only a Λ == 1 candidate.
|
| 249 |
+
|
| 250 |
+
Returns a :class:`LambdaGateResult` namedtuple with fields:
|
| 251 |
+
score — Λ(x) tensor of shape (...), in [0,1]
|
| 252 |
+
passed — boolean tensor of shape (...), Λ(x) >= threshold
|
| 253 |
+
threshold — the float threshold used
|
| 254 |
+
advisory — always True; a STANDING reminder that this is a
|
| 255 |
+
non-compensatory governance signal, NOT proven trust.
|
| 256 |
+
|
| 257 |
+
Non-compensatory threshold hardening: because a failing/garbage candidate
|
| 258 |
+
(a zero, NaN, or ±Inf axis) is routed to Λ = 0, a NEGATIVE threshold would
|
| 259 |
+
advisory-"pass" exactly those fully-failing candidates (0 >= t for t < 0) —
|
| 260 |
+
the opposite of a conservative admission gate. A threshold above 1 can
|
| 261 |
+
never pass. Both are misconfigurations, so the [0,1] domain is enforced up
|
| 262 |
+
front rather than silently producing a wrong pass mask.
|
| 263 |
+
|
| 264 |
+
HONESTY: a "pass" is an ADVISORY signal only. Λ is the weighted-geometric-
|
| 265 |
+
mean aggregator; its uniqueness is Conjecture 1 (open). Do not treat a
|
| 266 |
+
pass as proven trust or a closed theorem.
|
| 267 |
+
"""
|
| 268 |
+
threshold = _resolve_threshold(threshold, stacklevel=2)
|
| 269 |
+
t = float(threshold)
|
| 270 |
+
if t != t or t == float("inf") or t == float("-inf"):
|
| 271 |
+
raise ValueError(f"threshold must be a finite float, got {threshold!r}")
|
| 272 |
+
if t < 0.0 or t > 1.0:
|
| 273 |
+
raise ValueError(
|
| 274 |
+
"threshold must be within Λ's range [0, 1] (Λ is the weighted "
|
| 275 |
+
f"geometric mean over [0,1]); got {t!r}. A threshold below 0 would "
|
| 276 |
+
"advisory-pass a fully-failing (Λ=0) candidate and one above 1 can "
|
| 277 |
+
"never pass — both signal a misconfigured gate."
|
| 278 |
+
)
|
| 279 |
+
score = lambda_aggregate(axes, weights)
|
| 280 |
+
passed = score >= t
|
| 281 |
+
return LambdaGateResult(score=score, passed=passed, threshold=t, advisory=True)
|
| 282 |
+
|
| 283 |
+
|
| 284 |
+
def lambda_gate_batch(
|
| 285 |
+
candidates: torch.Tensor,
|
| 286 |
+
weights: Optional[torch.Tensor] = None,
|
| 287 |
+
threshold: Optional[float] = None,
|
| 288 |
+
):
|
| 289 |
+
"""ADVISORY batch gate: score MANY candidate action-vectors in one call.
|
| 290 |
+
|
| 291 |
+
This is the realistic way a model/agent uses the gate — one call per
|
| 292 |
+
inference step that scores every proposed action-vector at once and returns
|
| 293 |
+
the advisory pass mask (which candidates clear the threshold).
|
| 294 |
+
|
| 295 |
+
``candidates`` is a tensor of shape (..., N, k): the last dim ``k`` holds
|
| 296 |
+
the per-axis scores of a single candidate, and the second-to-last dim ``N``
|
| 297 |
+
enumerates the candidates (any leading dims are extra batch). Equivalent to
|
| 298 |
+
calling :func:`lambda_gate` on the whole tensor — the reduction is over the
|
| 299 |
+
last dim — but named to make the agent-loop intent explicit. ``threshold``
|
| 300 |
+
inherits the same [0,1] domain guard as :func:`lambda_gate` (a threshold
|
| 301 |
+
outside Λ's range is a misconfiguration and is rejected). Omitting it uses
|
| 302 |
+
the legacy 0.5 with a ``DeprecationWarning``, as in :func:`lambda_gate`.
|
| 303 |
+
|
| 304 |
+
Returns a :class:`LambdaGateResult` with:
|
| 305 |
+
score — Λ tensor of shape (..., N), one score per candidate
|
| 306 |
+
passed — boolean mask of shape (..., N): score >= threshold
|
| 307 |
+
threshold — the float threshold used
|
| 308 |
+
advisory — always True (NOT proven trust)
|
| 309 |
+
|
| 310 |
+
HONESTY: the pass mask is an ADVISORY, non-compensatory signal. A "pass"
|
| 311 |
+
is not proven trust; Λ-uniqueness is Conjecture 1 (open).
|
| 312 |
+
"""
|
| 313 |
+
threshold = _resolve_threshold(threshold, stacklevel=2)
|
| 314 |
+
_check_axes(candidates)
|
| 315 |
+
if candidates.dim() < 2:
|
| 316 |
+
raise ValueError(
|
| 317 |
+
"candidates must be at least 2-D, shape (..., N, k): the last dim is "
|
| 318 |
+
f"the k axis scores and the one before it enumerates the N candidates; "
|
| 319 |
+
f"got a {candidates.dim()}-d tensor"
|
| 320 |
+
)
|
| 321 |
+
# Reuse the single-call gate — its reduction over the last dim already gives
|
| 322 |
+
# one score per candidate, so the (..., N) layout falls out for free.
|
| 323 |
+
return lambda_gate(candidates, weights=weights, threshold=threshold)
|
| 324 |
+
|
| 325 |
+
|
| 326 |
+
# ---- A1..A4 axiom RUNTIME self-checks (real, verifiable) ------------------- #
|
| 327 |
+
# These are honest empirical checks callers can run on concrete inputs. They
|
| 328 |
+
# verify the carried axioms hold for THIS implementation on the given data —
|
| 329 |
+
# they are NOT a proof of Λ-uniqueness (that is Conjecture 1, open).
|
| 330 |
+
|
| 331 |
+
def is_egyptian_exact(
|
| 332 |
+
c: float,
|
| 333 |
+
k: int = 3,
|
| 334 |
+
weights: Optional[torch.Tensor] = None,
|
| 335 |
+
tol: float = 1e-5,
|
| 336 |
+
) -> bool:
|
| 337 |
+
"""A3 IsEgyptianExact: Λ(c, …, c) = c for a constant axis vector of length k.
|
| 338 |
+
|
| 339 |
+
Builds the uniform vector (c repeated k times) and checks Λ equals c within
|
| 340 |
+
``tol``. ``c`` is clamped into [0,1] to match the aggregator's domain.
|
| 341 |
+
"""
|
| 342 |
+
if k < 1:
|
| 343 |
+
raise ValueError("k must be >= 1")
|
| 344 |
+
cc = min(max(float(c), 0.0), 1.0)
|
| 345 |
+
axes = torch.full((k,), cc, dtype=torch.float64)
|
| 346 |
+
val = lambda_aggregate(axes, weights)
|
| 347 |
+
return bool(torch.abs(val - cc) <= tol)
|
| 348 |
+
|
| 349 |
+
|
| 350 |
+
def is_bounded_by_max(
|
| 351 |
+
axes: torch.Tensor,
|
| 352 |
+
weights: Optional[torch.Tensor] = None,
|
| 353 |
+
tol: float = 1e-6,
|
| 354 |
+
) -> bool:
|
| 355 |
+
"""A4 IsBounded: Λ(x) ≤ maxᵢ xᵢ (over the last dim), within ``tol``.
|
| 356 |
+
|
| 357 |
+
Returns True iff the bound holds for every batch row. Non-finite axis
|
| 358 |
+
values are clamped/zero-routed the same way the aggregator treats them, so
|
| 359 |
+
the bound is checked on the conservative (finite) domain.
|
| 360 |
+
"""
|
| 361 |
+
_check_axes(axes)
|
| 362 |
+
val = lambda_aggregate(axes, weights) # (...)
|
| 363 |
+
xf = axes.to(_compute_dtype(axes.dtype))
|
| 364 |
+
# Mirror the aggregator: non-finite axes are failing (treated as 0) for the
|
| 365 |
+
# purposes of the max bound, so the check matches the routed semantics.
|
| 366 |
+
xf = torch.where(torch.isfinite(xf), xf, torch.zeros_like(xf))
|
| 367 |
+
mx = xf.clamp(0.0, 1.0).amax(dim=-1) # (...)
|
| 368 |
+
return bool(torch.all(val.to(mx.dtype) <= mx + tol))
|
| 369 |
+
|
| 370 |
+
|
| 371 |
+
def is_homogeneous(
|
| 372 |
+
axes: torch.Tensor,
|
| 373 |
+
t: float,
|
| 374 |
+
weights: Optional[torch.Tensor] = None,
|
| 375 |
+
tol: float = 1e-5,
|
| 376 |
+
) -> bool:
|
| 377 |
+
"""A2 IsHomogeneous (degree 1): Λ(t·x) = t·Λ(x) for scalar t in [0,1].
|
| 378 |
+
|
| 379 |
+
Verified on the clamped domain: both ``axes`` and ``t*axes`` must remain in
|
| 380 |
+
[0,1] for the identity to be meaningful, so ``axes`` is clamped to [0,1] and
|
| 381 |
+
``t`` to [0,1] before the comparison.
|
| 382 |
+
"""
|
| 383 |
+
_check_axes(axes)
|
| 384 |
+
tt = min(max(float(t), 0.0), 1.0)
|
| 385 |
+
x = axes.to(torch.float64).clamp(0.0, 1.0)
|
| 386 |
+
lhs = lambda_aggregate(x * tt, weights)
|
| 387 |
+
rhs = tt * lambda_aggregate(x, weights)
|
| 388 |
+
return bool(torch.all(torch.abs(lhs - rhs) <= tol))
|
| 389 |
+
|
| 390 |
+
|
| 391 |
+
def is_monotone(
|
| 392 |
+
axes: torch.Tensor,
|
| 393 |
+
weights: Optional[torch.Tensor] = None,
|
| 394 |
+
delta: float = 0.05,
|
| 395 |
+
tol: float = 1e-7,
|
| 396 |
+
) -> bool:
|
| 397 |
+
"""A1 IsMonotone: Λ is non-decreasing in each axis.
|
| 398 |
+
|
| 399 |
+
For each axis j, nudges that axis UP by ``delta`` (clamped to stay ≤ 1) on
|
| 400 |
+
every batch row and checks Λ does not decrease (within ``tol``). Rows that
|
| 401 |
+
cannot move (already at 1) are skipped for that axis. A real check on the
|
| 402 |
+
given data — not a symbolic proof.
|
| 403 |
+
"""
|
| 404 |
+
_check_axes(axes)
|
| 405 |
+
x = axes.to(torch.float64).clamp(0.0, 1.0)
|
| 406 |
+
base = lambda_aggregate(x, weights)
|
| 407 |
+
k = x.shape[-1]
|
| 408 |
+
ok = True
|
| 409 |
+
for j in range(k):
|
| 410 |
+
bumped = x.clone()
|
| 411 |
+
bumped[..., j] = (bumped[..., j] + float(delta)).clamp(0.0, 1.0)
|
| 412 |
+
bumped_val = lambda_aggregate(bumped, weights)
|
| 413 |
+
# Λ must not go DOWN when an axis goes UP.
|
| 414 |
+
ok = ok and bool(torch.all(bumped_val - base >= -tol))
|
| 415 |
+
return ok
|
| 416 |
+
|
| 417 |
+
|
| 418 |
+
# ---- Adversarial axiom search (honest: a falsification attempt) ------------ #
|
| 419 |
+
def find_axiom_violation(
|
| 420 |
+
k: int = 5,
|
| 421 |
+
trials: int = 200,
|
| 422 |
+
weights: Optional[torch.Tensor] = None,
|
| 423 |
+
seed: Optional[int] = 0,
|
| 424 |
+
tol: float = 1e-6,
|
| 425 |
+
):
|
| 426 |
+
"""Random-search for ANY A1–A4 violation on random axis/weight draws.
|
| 427 |
+
|
| 428 |
+
Returns the first ``(axiom, axes, weights)`` triple that violates a carried
|
| 429 |
+
axiom within ``tol``, or ``None`` if none is found in ``trials`` draws. This
|
| 430 |
+
is an honest FALSIFICATION attempt on this implementation — finding nothing
|
| 431 |
+
is empirical evidence, NOT a proof (Λ-uniqueness is Conjecture 1, open).
|
| 432 |
+
"""
|
| 433 |
+
gen = torch.Generator()
|
| 434 |
+
if seed is not None:
|
| 435 |
+
gen.manual_seed(int(seed))
|
| 436 |
+
for _ in range(int(trials)):
|
| 437 |
+
x = torch.rand(k, generator=gen, dtype=torch.float64)
|
| 438 |
+
w = weights
|
| 439 |
+
if w is None:
|
| 440 |
+
w = torch.rand(k, generator=gen, dtype=torch.float64) + 1e-3
|
| 441 |
+
# A3 on a constant draw
|
| 442 |
+
c = float(torch.rand(1, generator=gen).item())
|
| 443 |
+
if not is_egyptian_exact(c, k=k, weights=w, tol=max(tol, 1e-5)):
|
| 444 |
+
return ("A3_IsEgyptianExact", torch.full((k,), c, dtype=torch.float64), w)
|
| 445 |
+
# A4 bounded-by-max
|
| 446 |
+
if not is_bounded_by_max(x, w, tol=max(tol, 1e-6)):
|
| 447 |
+
return ("A4_IsBounded", x, w)
|
| 448 |
+
# A2 homogeneous at a random t
|
| 449 |
+
t = float(torch.rand(1, generator=gen).item())
|
| 450 |
+
if not is_homogeneous(x, t, weights=w, tol=max(tol, 1e-5)):
|
| 451 |
+
return ("A2_IsHomogeneous", x, w)
|
| 452 |
+
# A1 monotone (leave headroom so an up-bump stays in range)
|
| 453 |
+
if not is_monotone(x * 0.9, w, tol=max(tol, 1e-7)):
|
| 454 |
+
return ("A1_IsMonotone", x * 0.9, w)
|
| 455 |
+
return None
|
| 456 |
+
|
| 457 |
+
|
| 458 |
+
# ---- Canonical 13-axis Yuyay preset (ADVISORY ONLY) ------------------------ #
|
| 459 |
+
# SZL's own yuyay_v3 "Heart" gate is a 13-axis CONJUNCTIVE-AND screen (each axis
|
| 460 |
+
# independently clears its floor — no compensation). We expose its published
|
| 461 |
+
# axis NAMES and per-axis FLOORS as advisory metadata, and a uniform Λ weight
|
| 462 |
+
# vector over the 13 axes. This is ADVISORY: Λ here is still the weighted
|
| 463 |
+
# geometric mean, and a "pass" is a research-conjecture signal, NOT proven
|
| 464 |
+
# trust. Source: yuyay_v3 spec (Lutar, 2026).
|
| 465 |
+
YUYAY_AXES = (
|
| 466 |
+
"moralGrounding",
|
| 467 |
+
"measurabilityHonesty",
|
| 468 |
+
"empiricalGrounding",
|
| 469 |
+
"logicalConsistency",
|
| 470 |
+
"sourceTransparency",
|
| 471 |
+
"reproducibility",
|
| 472 |
+
"licenseHygiene",
|
| 473 |
+
"scopeDiscipline",
|
| 474 |
+
"claimCalibration",
|
| 475 |
+
"evalAwareness",
|
| 476 |
+
"deceptionKeywords",
|
| 477 |
+
"conflictingDirectives",
|
| 478 |
+
"reversalDirective",
|
| 479 |
+
)
|
| 480 |
+
# Published per-axis advisory floors for the CONJUNCTIVE screen: two "sacred"
|
| 481 |
+
# axes at 0.95, seven "structural" at 0.90, four "introspection" at 0.90.
|
| 482 |
+
YUYAY_FLOORS = (
|
| 483 |
+
0.95, 0.95, # sacred
|
| 484 |
+
0.90, 0.90, 0.90, 0.90, 0.90, 0.90, 0.90, # structural (7)
|
| 485 |
+
0.90, 0.90, 0.90, 0.90, # introspection (4)
|
| 486 |
+
)
|
| 487 |
+
|
| 488 |
+
|
| 489 |
+
def yuyay_weights(
|
| 490 |
+
dtype: torch.dtype = torch.float64,
|
| 491 |
+
device: Optional[torch.device] = None,
|
| 492 |
+
) -> torch.Tensor:
|
| 493 |
+
"""Canonical 13-axis Yuyay Λ weight vector (uniform 1/13), ADVISORY only.
|
| 494 |
+
|
| 495 |
+
Returns a length-13 weight tensor for use as the ``weights`` argument to
|
| 496 |
+
:func:`lambda_aggregate` / :func:`lambda_gate` over the 13 :data:`YUYAY_AXES`.
|
| 497 |
+
Uniform by default (the Egyptian-exact diagonal). The published yuyay_v3
|
| 498 |
+
gate is a conjunctive AND with per-axis floors (:data:`YUYAY_FLOORS`); the
|
| 499 |
+
Λ roll-up here is the weighted geometric mean and is ADVISORY — NOT proven
|
| 500 |
+
trust (Λ-uniqueness is Conjecture 1, open).
|
| 501 |
+
"""
|
| 502 |
+
k = len(YUYAY_AXES)
|
| 503 |
+
return torch.full((k,), 1.0 / k, dtype=dtype, device=device)
|
| 504 |
+
|
| 505 |
+
|
| 506 |
+
# ---- Kernel self-check surface --------------------------------------------- #
|
| 507 |
+
def selfcheck(
|
| 508 |
+
k: int = 5,
|
| 509 |
+
trials: int = 64,
|
| 510 |
+
seed: Optional[int] = 0,
|
| 511 |
+
) -> dict:
|
| 512 |
+
"""Run the A1–A4 empirical self-checks and report a verdict + version.
|
| 513 |
+
|
| 514 |
+
Returns a dict:
|
| 515 |
+
version — kernel version string
|
| 516 |
+
axioms — {A1..A4: bool} empirical pass on sampled inputs
|
| 517 |
+
all_axioms_hold — bool, every sampled axiom check passed
|
| 518 |
+
adversarial — {trials, violation} from a random falsification search
|
| 519 |
+
(violation is None when no violation was found)
|
| 520 |
+
advisory — always True
|
| 521 |
+
lambda_status — Conjecture 1 (open) honesty string
|
| 522 |
+
|
| 523 |
+
HONESTY: these are EMPIRICAL checks on sampled inputs, NOT a proof of
|
| 524 |
+
Λ-uniqueness (Conjecture 1, open). A clean run is evidence, not proof.
|
| 525 |
+
"""
|
| 526 |
+
x = torch.rand(k, dtype=torch.float64) * 0.9 # headroom for the A1 up-bump
|
| 527 |
+
w = torch.rand(k, dtype=torch.float64) + 1e-3
|
| 528 |
+
axioms = {
|
| 529 |
+
"A1_IsMonotone": is_monotone(x, w),
|
| 530 |
+
"A2_IsHomogeneous": is_homogeneous(x, float(torch.rand(1).item()), weights=w),
|
| 531 |
+
"A3_IsEgyptianExact": is_egyptian_exact(float(torch.rand(1).item()), k=k, weights=w),
|
| 532 |
+
"A4_IsBounded": is_bounded_by_max(x, w),
|
| 533 |
+
}
|
| 534 |
+
violation = find_axiom_violation(k=k, trials=trials, seed=seed)
|
| 535 |
+
return {
|
| 536 |
+
"version": __version__,
|
| 537 |
+
"axioms": axioms,
|
| 538 |
+
"all_axioms_hold": all(axioms.values()) and violation is None,
|
| 539 |
+
"adversarial": {"trials": int(trials), "violation": violation},
|
| 540 |
+
"advisory": True,
|
| 541 |
+
"lambda_status": "Conjecture 1 (open) — uniqueness unproven; advisory only",
|
| 542 |
+
}
|
| 543 |
+
|
| 544 |
+
|
| 545 |
+
# Kept in sync with the package __version__ (single source of truth lives in
|
| 546 |
+
# __init__; duplicated here so _lambda is importable/selfcheck-able standalone).
|
| 547 |
+
__version__ = "0.2.0"
|
| 548 |
+
|
| 549 |
+
|
| 550 |
+
# Namedtuple result type for the gate. Defined after functions so docstrings
|
| 551 |
+
# above can reference it; imported by __init__ and layers.
|
| 552 |
+
from collections import namedtuple # noqa: E402
|
| 553 |
+
|
| 554 |
+
LambdaGateResult = namedtuple(
|
| 555 |
+
"LambdaGateResult", ["score", "passed", "threshold", "advisory"]
|
| 556 |
+
)
|
build/torch-universal/szl_lambda_gate/_ops.py
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
+
# Auto-style ops namespace shim for the universal kernel. Unique suffix lets
|
| 3 |
+
# multiple versions load in the same process (Kernel Hub requirement).
|
| 4 |
+
import torch
|
| 5 |
+
|
| 6 |
+
ops = torch.ops._szl_lambda_gate_20260623081355
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
def add_op_namespace_prefix(op_name: str) -> str:
|
| 10 |
+
return f"_szl_lambda_gate_20260623081355::{op_name}"
|
build/torch-universal/szl_lambda_gate/_v1.py
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
+
# © 2026 SZL Holdings · Stephen P. Lutar · ORCID 0009-0001-0110-4173
|
| 3 |
+
"""szl.lambda/v1 strict kernel entry: Λ and its gate on tensors, with no clamping or defaults.
|
| 4 |
+
|
| 5 |
+
This is the torch port of ``reference/szl_lambda_v1.py`` and follows
|
| 6 |
+
``spec/szl.lambda.v1.json``. An input outside the contract makes ``lambda_v1``
|
| 7 |
+
raise ``LambdaV1Error(code)`` and makes ``lambda_v1_gate`` return verdict BLOCK
|
| 8 |
+
with that code. Nothing is clamped, renormalised, defaulted or rounded before
|
| 9 |
+
the compare::
|
| 10 |
+
|
| 11 |
+
lambda_v1(axes, weights) -> 0-d float64 tensor, Λ in [0, 1]
|
| 12 |
+
lambda_v1_gate(axes, weights, tau) -> LambdaV1GateResult(verdict, code, score, tau, advisory)
|
| 13 |
+
|
| 14 |
+
The legacy ``lambda_aggregate`` / ``lambda_gate`` are unchanged. They clamp
|
| 15 |
+
x > 1 to 1, route NaN and ±Inf to 0 and renormalise weights, and
|
| 16 |
+
``lambda_gate`` falls back to a threshold of 0.5, which is now deprecated.
|
| 17 |
+
Use this module when a verdict must follow szl.lambda/v1.
|
| 18 |
+
|
| 19 |
+
Inputs
|
| 20 |
+
``axes`` and ``weights`` are 1-D tensors of a real dtype (floating or
|
| 21 |
+
integer). bool and complex are not numbers. A Python list, None, or a 0-d
|
| 22 |
+
or 2-D tensor is LAMBDA_TYPE_INVALID; build the tensor with
|
| 23 |
+
``torch.tensor(values, dtype=torch.float64)``. ``weights`` is moved to the
|
| 24 |
+
device of ``axes``. Inputs are read and never modified.
|
| 25 |
+
|
| 26 |
+
``tau`` is a Python int or float (not bool), finite, with 0 < tau <= 1. It
|
| 27 |
+
is required and has no default. The policy value is ``policy_tau`` in
|
| 28 |
+
frontier/model_admit_contract.v1.json.
|
| 29 |
+
|
| 30 |
+
Checks run in phases, in the reference's order, so the reported code does not
|
| 31 |
+
depend on axis order::
|
| 32 |
+
|
| 33 |
+
LAMBDA_TYPE_INVALID (container) > LAMBDA_EMPTY > LAMBDA_LENGTH_MISMATCH
|
| 34 |
+
> LAMBDA_TYPE_INVALID (dtype) > LAMBDA_NONFINITE_AXIS
|
| 35 |
+
> LAMBDA_AXIS_OUT_OF_RANGE > LAMBDA_NONFINITE_WEIGHT
|
| 36 |
+
> LAMBDA_WEIGHT_NONPOSITIVE > LAMBDA_WEIGHT_SUM
|
| 37 |
+
|
| 38 |
+
The gate checks tau first (LAMBDA_TAU_INVALID). The element checks and the
|
| 39 |
+
weight sum (``math.fsum``) run on the float64 values exactly as the reference
|
| 40 |
+
runs them, so every error code agrees with the reference.
|
| 41 |
+
|
| 42 |
+
How the numbers are computed
|
| 43 |
+
* Λ, which is ``lambda_v1`` and the gate's ``score``, comes from
|
| 44 |
+
``_lambda.lambda_aggregate`` in float64, so it stays a differentiable
|
| 45 |
+
tensor. On validated input that function's clamp and NaN routing change
|
| 46 |
+
nothing, and a zero axis gives exactly 0. Its renormalisation divides by
|
| 47 |
+
a sum within 1e-12 of 1, which moves Λ by at most Λ·|log Λ|·1e-12
|
| 48 |
+
(<= 3.7e-13) plus float64 rounding. The vectors' value_tol is 1e-12.
|
| 49 |
+
* The verdict comes from log Λ = fsum(w_k · log x_k) over the same float64
|
| 50 |
+
values, as in the reference, so it never depends on how Λ rounds (a
|
| 51 |
+
subnormal Λ has little relative precision). A zero axis gives
|
| 52 |
+
log Λ = -inf and the verdict NO_GO / ZERO_VETO, a veto rather than an
|
| 53 |
+
error. |log Λ - log tau| <= TIE_EPS gives ABSTAIN / NUMERIC_TIE. Above
|
| 54 |
+
the band the verdict is GO, and below it NO_GO / BELOW_TAU.
|
| 55 |
+
|
| 56 |
+
The checks depend on the data and the verdict is a Python string, so this
|
| 57 |
+
entry is not meant to run inside ``torch.compile``. The legacy compiled weight
|
| 58 |
+
path in ``_lambda._resolve_weights``, which clamps weights to ``finfo.tiny``,
|
| 59 |
+
is outside szl.lambda/v1.
|
| 60 |
+
|
| 61 |
+
Λ is advisory. Λ uniqueness is Conjecture 1 (open), and nothing here depends on it.
|
| 62 |
+
"""
|
| 63 |
+
from __future__ import annotations
|
| 64 |
+
|
| 65 |
+
import math
|
| 66 |
+
from collections import namedtuple
|
| 67 |
+
from typing import Any, List, Optional, Tuple
|
| 68 |
+
|
| 69 |
+
import torch
|
| 70 |
+
|
| 71 |
+
from ._lambda import lambda_aggregate
|
| 72 |
+
|
| 73 |
+
SCHEMA = "szl.lambda/v1"
|
| 74 |
+
UNIQUENESS = "CONJECTURE_1_NOT_USED"
|
| 75 |
+
WEIGHT_SUM_TOL = 1e-12
|
| 76 |
+
TIE_EPS = 1e-9
|
| 77 |
+
|
| 78 |
+
TYPE_INVALID = "LAMBDA_TYPE_INVALID"
|
| 79 |
+
EMPTY = "LAMBDA_EMPTY"
|
| 80 |
+
LENGTH_MISMATCH = "LAMBDA_LENGTH_MISMATCH"
|
| 81 |
+
NONFINITE_AXIS = "LAMBDA_NONFINITE_AXIS"
|
| 82 |
+
AXIS_OUT_OF_RANGE = "LAMBDA_AXIS_OUT_OF_RANGE"
|
| 83 |
+
NONFINITE_WEIGHT = "LAMBDA_NONFINITE_WEIGHT"
|
| 84 |
+
WEIGHT_NONPOSITIVE = "LAMBDA_WEIGHT_NONPOSITIVE"
|
| 85 |
+
WEIGHT_SUM = "LAMBDA_WEIGHT_SUM"
|
| 86 |
+
TAU_INVALID = "LAMBDA_TAU_INVALID"
|
| 87 |
+
|
| 88 |
+
#: Every error code, in precedence order (tau is checked first by the gate).
|
| 89 |
+
ERROR_CODES = (
|
| 90 |
+
TYPE_INVALID,
|
| 91 |
+
EMPTY,
|
| 92 |
+
LENGTH_MISMATCH,
|
| 93 |
+
NONFINITE_AXIS,
|
| 94 |
+
AXIS_OUT_OF_RANGE,
|
| 95 |
+
NONFINITE_WEIGHT,
|
| 96 |
+
WEIGHT_NONPOSITIVE,
|
| 97 |
+
WEIGHT_SUM,
|
| 98 |
+
TAU_INVALID,
|
| 99 |
+
)
|
| 100 |
+
|
| 101 |
+
GO = "GO"
|
| 102 |
+
NO_GO = "NO_GO"
|
| 103 |
+
ABSTAIN = "ABSTAIN"
|
| 104 |
+
BLOCK = "BLOCK"
|
| 105 |
+
VERDICTS = (GO, NO_GO, ABSTAIN, BLOCK)
|
| 106 |
+
|
| 107 |
+
ZERO_VETO = "ZERO_VETO"
|
| 108 |
+
BELOW_TAU = "BELOW_TAU"
|
| 109 |
+
NUMERIC_TIE = "NUMERIC_TIE"
|
| 110 |
+
|
| 111 |
+
_INTEGER_DTYPES = (torch.uint8, torch.int8, torch.int16, torch.int32, torch.int64)
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
class LambdaV1Error(ValueError):
|
| 115 |
+
"""An input outside the szl.lambda/v1 contract. ``code`` is one of ERROR_CODES."""
|
| 116 |
+
|
| 117 |
+
def __init__(self, code: str, detail: str = "") -> None:
|
| 118 |
+
self.code = code
|
| 119 |
+
self.detail = detail
|
| 120 |
+
super().__init__(f"{code}: {detail}" if detail else code)
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
#: verdict in VERDICTS; code as in the spec's gate rules; score is Λ as a 0-d
|
| 124 |
+
#: float64 tensor (None on BLOCK); tau is the validated float (None when tau
|
| 125 |
+
#: itself is invalid); advisory is always True.
|
| 126 |
+
LambdaV1GateResult = namedtuple(
|
| 127 |
+
"LambdaV1GateResult", ["verdict", "code", "score", "tau", "advisory"]
|
| 128 |
+
)
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
def _is_real(value: Any) -> bool:
|
| 132 |
+
return isinstance(value, (int, float)) and not isinstance(value, bool)
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
def _is_real_dtype(dtype: torch.dtype) -> bool:
|
| 136 |
+
return dtype.is_floating_point or dtype in _INTEGER_DTYPES
|
| 137 |
+
|
| 138 |
+
|
| 139 |
+
def _validate(
|
| 140 |
+
axes: Any, weights: Any
|
| 141 |
+
) -> Tuple[torch.Tensor, torch.Tensor, List[float], List[float]]:
|
| 142 |
+
"""Float64 tensors and their values, or LambdaV1Error in the reference's phase order."""
|
| 143 |
+
for name, t in (("axes", axes), ("weights", weights)):
|
| 144 |
+
if not isinstance(t, torch.Tensor):
|
| 145 |
+
raise LambdaV1Error(TYPE_INVALID, f"{name} must be a 1-D torch.Tensor, got {type(t).__name__}")
|
| 146 |
+
if t.dim() != 1:
|
| 147 |
+
raise LambdaV1Error(TYPE_INVALID, f"{name} must be a 1-D tensor, got {t.dim()}-D")
|
| 148 |
+
k, m = axes.shape[0], weights.shape[0]
|
| 149 |
+
if k == 0 or m == 0:
|
| 150 |
+
raise LambdaV1Error(EMPTY, f"len(axes)={k}, len(weights)={m}")
|
| 151 |
+
if k != m:
|
| 152 |
+
raise LambdaV1Error(LENGTH_MISMATCH, f"len(axes)={k} != len(weights)={m}")
|
| 153 |
+
for name, t in (("axes", axes), ("weights", weights)):
|
| 154 |
+
if not _is_real_dtype(t.dtype):
|
| 155 |
+
raise LambdaV1Error(TYPE_INVALID, f"{name} has dtype {t.dtype}, not a real number type")
|
| 156 |
+
x = axes.to(torch.float64)
|
| 157 |
+
w = weights.to(device=x.device, dtype=torch.float64)
|
| 158 |
+
xs, ws = x.tolist(), w.tolist()
|
| 159 |
+
for i, v in enumerate(xs):
|
| 160 |
+
if not math.isfinite(v):
|
| 161 |
+
raise LambdaV1Error(NONFINITE_AXIS, f"axes[{i}]={v!r}")
|
| 162 |
+
for i, v in enumerate(xs):
|
| 163 |
+
if not 0.0 <= v <= 1.0:
|
| 164 |
+
raise LambdaV1Error(AXIS_OUT_OF_RANGE, f"axes[{i}]={v!r} is outside [0, 1]")
|
| 165 |
+
for i, v in enumerate(ws):
|
| 166 |
+
if not math.isfinite(v):
|
| 167 |
+
raise LambdaV1Error(NONFINITE_WEIGHT, f"weights[{i}]={v!r}")
|
| 168 |
+
for i, v in enumerate(ws):
|
| 169 |
+
if not v > 0.0:
|
| 170 |
+
raise LambdaV1Error(WEIGHT_NONPOSITIVE, f"weights[{i}]={v!r} is not > 0")
|
| 171 |
+
try:
|
| 172 |
+
total = math.fsum(ws)
|
| 173 |
+
except OverflowError:
|
| 174 |
+
raise LambdaV1Error(WEIGHT_SUM, "sum of weights overflows a float") from None
|
| 175 |
+
if not abs(total - 1.0) <= WEIGHT_SUM_TOL:
|
| 176 |
+
raise LambdaV1Error(WEIGHT_SUM, f"fsum(weights)={total!r} is not within {WEIGHT_SUM_TOL} of 1")
|
| 177 |
+
return x, w, xs, ws
|
| 178 |
+
|
| 179 |
+
|
| 180 |
+
def lambda_v1(axes: torch.Tensor, weights: torch.Tensor) -> torch.Tensor:
|
| 181 |
+
"""Λ_w(axes) as a 0-d float64 tensor in [0, 1]; exactly 0 iff some axis is 0.
|
| 182 |
+
|
| 183 |
+
Raises ``LambdaV1Error(code)`` for any input outside szl.lambda/v1.
|
| 184 |
+
Differentiable with respect to ``axes``.
|
| 185 |
+
"""
|
| 186 |
+
x, w, _, _ = _validate(axes, weights)
|
| 187 |
+
return lambda_aggregate(x, w)
|
| 188 |
+
|
| 189 |
+
|
| 190 |
+
def _check_tau(tau: Any) -> float:
|
| 191 |
+
if not _is_real(tau):
|
| 192 |
+
raise LambdaV1Error(TAU_INVALID, f"tau is {type(tau).__name__}, not a real number")
|
| 193 |
+
if isinstance(tau, float) and not math.isfinite(tau):
|
| 194 |
+
raise LambdaV1Error(TAU_INVALID, f"tau={tau!r} is not finite")
|
| 195 |
+
if not 0 < tau <= 1:
|
| 196 |
+
raise LambdaV1Error(TAU_INVALID, f"tau={tau!r} is outside (0, 1]")
|
| 197 |
+
return float(tau)
|
| 198 |
+
|
| 199 |
+
|
| 200 |
+
def lambda_v1_gate(axes: torch.Tensor, weights: torch.Tensor, tau: float) -> LambdaV1GateResult:
|
| 201 |
+
"""The szl.lambda/v1 gate. It never raises on bad input; it returns BLOCK with the code.
|
| 202 |
+
|
| 203 |
+
tau is required. The compare is in log space on the unrounded value, and a
|
| 204 |
+
tie within TIE_EPS is ABSTAIN. A verdict is ADVISORY.
|
| 205 |
+
"""
|
| 206 |
+
t: Optional[float] = None
|
| 207 |
+
try:
|
| 208 |
+
t = _check_tau(tau)
|
| 209 |
+
x, w, xs, ws = _validate(axes, weights)
|
| 210 |
+
except LambdaV1Error as err:
|
| 211 |
+
return LambdaV1GateResult(BLOCK, err.code, None, t, True)
|
| 212 |
+
score = lambda_aggregate(x, w)
|
| 213 |
+
if any(v == 0.0 for v in xs):
|
| 214 |
+
return LambdaV1GateResult(NO_GO, ZERO_VETO, score, t, True)
|
| 215 |
+
log_lam = math.fsum(wk * math.log(xk) for xk, wk in zip(xs, ws))
|
| 216 |
+
delta = log_lam - math.log(t)
|
| 217 |
+
if abs(delta) <= TIE_EPS:
|
| 218 |
+
return LambdaV1GateResult(ABSTAIN, NUMERIC_TIE, score, t, True)
|
| 219 |
+
if delta > 0:
|
| 220 |
+
return LambdaV1GateResult(GO, None, score, t, True)
|
| 221 |
+
return LambdaV1GateResult(NO_GO, BELOW_TAU, score, t, True)
|
build/torch-universal/szl_lambda_gate/governed_norm/__init__.py
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
+
# © 2026 SZL Holdings · Stephen P. Lutar · ORCID 0009-0001-0110-4173
|
| 3 |
+
"""szl_lambda_gate.governed_norm — governed normalization kernels (folded in).
|
| 4 |
+
|
| 5 |
+
CONSOLIDATION (Wave D): this subpackage is the ``szl-governed-norm`` universal
|
| 6 |
+
kernel folded into the canonical ``szl-lambda-gate`` kernels package so the two
|
| 7 |
+
duplicate micro-repos become ONE canonical home. The source repo
|
| 8 |
+
``szl-holdings/szl-governed-norm`` is DEPRECATED (see its DEPRECATED.md) and
|
| 9 |
+
points here; nothing was deleted — this is the additive, reversible copy.
|
| 10 |
+
|
| 11 |
+
It provides correctness-verified RMSNorm and LayerNorm that run on CPU and CUDA
|
| 12 |
+
and are torch.compile-friendly, plus an optional *governed* path that emits
|
| 13 |
+
content-addressed, SHA3-256 hash-chained receipts of each call — provenance at
|
| 14 |
+
the kernel layer, in the spirit of the a11oy governed-AI platform
|
| 15 |
+
(https://a-11-oy.com).
|
| 16 |
+
|
| 17 |
+
Usage (as a subpackage of the canonical kernel)::
|
| 18 |
+
|
| 19 |
+
import torch
|
| 20 |
+
from szl_lambda_gate import governed_norm as gn
|
| 21 |
+
|
| 22 |
+
print(gn.selfcheck()) # one-shot correctness + receipt check
|
| 23 |
+
x = torch.randn(4, 1024, dtype=torch.float16)
|
| 24 |
+
y = gn.rms_norm(x, eps=1e-6) # plain path
|
| 25 |
+
y2 = gn.rms_norm(x, eps=1e-6, governed=True) # records to the default chain
|
| 26 |
+
chain = gn.ReceiptChain()
|
| 27 |
+
y3 = gn.rms_norm(x, eps=1e-6, chain=chain) # records into YOUR chain only
|
| 28 |
+
print(chain.verify()) # (ok, depth, first_break_seq)
|
| 29 |
+
|
| 30 |
+
Honesty: this is a universal (pure-Python) kernel — a correctness reference,
|
| 31 |
+
not a hand-tuned CUDA speed record. No fabricated benchmarks. Its
|
| 32 |
+
differentiator is verifiable governance, not raw FLOPs. Λ = Conjecture 1
|
| 33 |
+
(advisory, uniqueness OPEN) — never described as proven trust anywhere.
|
| 34 |
+
|
| 35 |
+
Note on torch.compile: every op is torch.compile(fullgraph=True)-compatible.
|
| 36 |
+
Receipt emission is an eager-only side effect (it hashes materialized tensor
|
| 37 |
+
bytes), so when a *governed* call is captured into a compiled graph the
|
| 38 |
+
numerics are unchanged but NO receipt is recorded — govern at the eager audit
|
| 39 |
+
boundary. This is documented honestly and covered by tests.
|
| 40 |
+
"""
|
| 41 |
+
from typing import Any, Dict, List, Optional, Tuple
|
| 42 |
+
|
| 43 |
+
import torch
|
| 44 |
+
|
| 45 |
+
from . import layers # noqa: F401 (must be importable for Hub layer mapping)
|
| 46 |
+
from ._norm import fused_add_rms_norm as _fused_add_rms_norm
|
| 47 |
+
from ._norm import layer_norm as _layer_norm
|
| 48 |
+
from ._norm import rms_norm as _rms_norm
|
| 49 |
+
from ._receipt import _GENESIS as _GENESIS_HEAD
|
| 50 |
+
from ._receipt import ReceiptChain, default_chain, emit_receipt
|
| 51 |
+
|
| 52 |
+
__all__ = [
|
| 53 |
+
"rms_norm",
|
| 54 |
+
"layer_norm",
|
| 55 |
+
"fused_add_rms_norm",
|
| 56 |
+
"layers",
|
| 57 |
+
"ReceiptChain",
|
| 58 |
+
"emit_receipt",
|
| 59 |
+
"receipt_head",
|
| 60 |
+
"receipt_count",
|
| 61 |
+
"receipt_tail",
|
| 62 |
+
"receipt_verify",
|
| 63 |
+
"selfcheck",
|
| 64 |
+
"DOCTRINE_FOOTER",
|
| 65 |
+
"__version__",
|
| 66 |
+
]
|
| 67 |
+
|
| 68 |
+
__version__ = "0.2.0"
|
| 69 |
+
DOCTRINE_FOOTER = (
|
| 70 |
+
"SZL Holdings · governed normalization · provenance at the kernel layer · "
|
| 71 |
+
"Lambda = Conjecture 1 (advisory) · honesty over checklist"
|
| 72 |
+
)
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
def _is_tracing() -> bool:
|
| 76 |
+
"""True while torch.compile / Dynamo is tracing this code.
|
| 77 |
+
|
| 78 |
+
Receipt emission reads materialized tensor bytes (hashing on CPU), which is
|
| 79 |
+
an inherently eager, side-effecting host operation that cannot live inside
|
| 80 |
+
a traced FX graph — so under torch.compile we skip the emit. This keeps
|
| 81 |
+
EVERY op torch.compile(fullgraph=True)-compatible while remaining honest:
|
| 82 |
+
when a governed call is captured into a compiled graph, NO receipt is
|
| 83 |
+
recorded (the numerics are unchanged and identical to the eager path).
|
| 84 |
+
Governance is intended for the eager audit boundary; record receipts there.
|
| 85 |
+
"""
|
| 86 |
+
is_compiling = getattr(torch.compiler, "is_compiling", None)
|
| 87 |
+
return bool(is_compiling()) if is_compiling is not None else False
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
def _emit(
|
| 91 |
+
chain: Optional[ReceiptChain],
|
| 92 |
+
op: str,
|
| 93 |
+
x: torch.Tensor,
|
| 94 |
+
out: torch.Tensor,
|
| 95 |
+
eps: float,
|
| 96 |
+
sign_key: Optional[Any] = None,
|
| 97 |
+
organ: str = "szl-governed-norm",
|
| 98 |
+
) -> None:
|
| 99 |
+
"""Append a receipt to ``chain`` (or the process default chain if None).
|
| 100 |
+
|
| 101 |
+
No-op while torch.compile is tracing (see ``_is_tracing``). When
|
| 102 |
+
``sign_key`` (a PEM ECDSA-P256 private key) is supplied and szl-receipt is
|
| 103 |
+
installed, the receipt carries an additive DSSE ``signature`` envelope;
|
| 104 |
+
keyless is UNSIGNED-honest.
|
| 105 |
+
"""
|
| 106 |
+
if _is_tracing():
|
| 107 |
+
return
|
| 108 |
+
target = chain if chain is not None else default_chain()
|
| 109 |
+
target.emit(op, x, out, eps, sign_key=sign_key, organ=organ)
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
def rms_norm(
|
| 113 |
+
x: torch.Tensor,
|
| 114 |
+
weight: Optional[torch.Tensor] = None,
|
| 115 |
+
eps: float = 1e-6,
|
| 116 |
+
governed: bool = False,
|
| 117 |
+
chain: Optional[ReceiptChain] = None,
|
| 118 |
+
sign_key: Optional[Any] = None,
|
| 119 |
+
organ: str = "szl-governed-norm",
|
| 120 |
+
) -> torch.Tensor:
|
| 121 |
+
"""RMSNorm over the last dim.
|
| 122 |
+
|
| 123 |
+
If ``governed=True``, append an audit receipt. By default the receipt goes
|
| 124 |
+
to the process-wide default chain (convenient). Pass your own ``chain`` (a
|
| 125 |
+
``ReceiptChain`` instance) to record into a caller-owned chain instead —
|
| 126 |
+
this avoids global-state contention when many threads/requests govern
|
| 127 |
+
independently. Passing ``chain`` implies governance even if
|
| 128 |
+
``governed=False`` is left at its default. Pass ``sign_key`` (PEM
|
| 129 |
+
ECDSA-P256) to additively sign the receipt via szl-receipt.
|
| 130 |
+
"""
|
| 131 |
+
out = _rms_norm(x, weight=weight, eps=eps)
|
| 132 |
+
if governed or chain is not None:
|
| 133 |
+
_emit(chain, "rms_norm", x, out, eps, sign_key=sign_key, organ=organ)
|
| 134 |
+
return out
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
def layer_norm(
|
| 138 |
+
x: torch.Tensor,
|
| 139 |
+
weight: Optional[torch.Tensor] = None,
|
| 140 |
+
bias: Optional[torch.Tensor] = None,
|
| 141 |
+
eps: float = 1e-5,
|
| 142 |
+
governed: bool = False,
|
| 143 |
+
chain: Optional[ReceiptChain] = None,
|
| 144 |
+
sign_key: Optional[Any] = None,
|
| 145 |
+
organ: str = "szl-governed-norm",
|
| 146 |
+
) -> torch.Tensor:
|
| 147 |
+
"""LayerNorm over the last dim.
|
| 148 |
+
|
| 149 |
+
If ``governed=True`` (or a ``chain`` is supplied), append an audit receipt
|
| 150 |
+
to ``chain`` when given, otherwise to the process default chain. See
|
| 151 |
+
``rms_norm`` for the per-call ``chain`` rationale and ``sign_key``.
|
| 152 |
+
"""
|
| 153 |
+
out = _layer_norm(x, weight=weight, bias=bias, eps=eps)
|
| 154 |
+
if governed or chain is not None:
|
| 155 |
+
_emit(chain, "layer_norm", x, out, eps, sign_key=sign_key, organ=organ)
|
| 156 |
+
return out
|
| 157 |
+
|
| 158 |
+
|
| 159 |
+
def fused_add_rms_norm(
|
| 160 |
+
x: torch.Tensor,
|
| 161 |
+
residual: torch.Tensor,
|
| 162 |
+
weight: Optional[torch.Tensor] = None,
|
| 163 |
+
eps: float = 1e-6,
|
| 164 |
+
governed: bool = False,
|
| 165 |
+
chain: Optional[ReceiptChain] = None,
|
| 166 |
+
sign_key: Optional[Any] = None,
|
| 167 |
+
organ: str = "szl-governed-norm",
|
| 168 |
+
) -> Tuple[torch.Tensor, torch.Tensor]:
|
| 169 |
+
"""Residual-add + RMSNorm (transformer block pattern).
|
| 170 |
+
|
| 171 |
+
Returns ``(y, new_residual)`` where ``new_residual = x + residual`` and
|
| 172 |
+
``y = rms_norm(new_residual, weight, eps)``. If ``governed=True`` (or a
|
| 173 |
+
``chain`` is supplied), append an audit receipt over the normalized output
|
| 174 |
+
to ``chain`` when given, otherwise to the process default chain. Pass
|
| 175 |
+
``sign_key`` to additively sign the receipt via szl-receipt.
|
| 176 |
+
"""
|
| 177 |
+
out, new_residual = _fused_add_rms_norm(x, residual, weight=weight, eps=eps)
|
| 178 |
+
if governed or chain is not None:
|
| 179 |
+
_emit(chain, "fused_add_rms_norm", x, out, eps, sign_key=sign_key, organ=organ)
|
| 180 |
+
return out, new_residual
|
| 181 |
+
|
| 182 |
+
|
| 183 |
+
# ---- governance receipt surface (operates on the default in-process chain) --
|
| 184 |
+
def receipt_head() -> str:
|
| 185 |
+
"""SHA3-256 head of the governed-call receipt chain ('0'*64 if empty)."""
|
| 186 |
+
return default_chain().head()
|
| 187 |
+
|
| 188 |
+
|
| 189 |
+
def receipt_count() -> int:
|
| 190 |
+
"""Number of governed calls recorded."""
|
| 191 |
+
return default_chain().count()
|
| 192 |
+
|
| 193 |
+
|
| 194 |
+
def receipt_tail(n: int = 10) -> List[Dict[str, Any]]:
|
| 195 |
+
"""Last n receipts."""
|
| 196 |
+
return default_chain().tail(n)
|
| 197 |
+
|
| 198 |
+
|
| 199 |
+
def receipt_verify() -> Dict[str, Any]:
|
| 200 |
+
"""Re-walk the receipt chain. Returns {ok, depth, first_break_seq}."""
|
| 201 |
+
ok, depth, brk = default_chain().verify()
|
| 202 |
+
return {"ok": ok, "depth": depth, "first_break_seq": brk, "head": default_chain().head()}
|
| 203 |
+
|
| 204 |
+
|
| 205 |
+
# ---- one-shot self-verification --------------------------------------------
|
| 206 |
+
def selfcheck() -> Dict[str, Any]:
|
| 207 |
+
"""Verify correctness + governance in a single call; never raises.
|
| 208 |
+
|
| 209 |
+
Runs a tiny, self-contained, CPU-only smoke test against PyTorch references
|
| 210 |
+
so downstream code (and SZL's own a11oy / hatun-mcp) can confirm the loaded
|
| 211 |
+
kernel is the real, working article before trusting it.
|
| 212 |
+
|
| 213 |
+
Checks (all on a *private, throwaway* ReceiptChain so the process default
|
| 214 |
+
chain is never touched):
|
| 215 |
+
* ``rms_norm`` matches a Llama-style float32 reference,
|
| 216 |
+
* ``layer_norm`` matches ``torch.nn.functional.layer_norm``,
|
| 217 |
+
* ``fused_add_rms_norm`` matches the unfused add-then-norm path,
|
| 218 |
+
* a governed call emits exactly one receipt and the chain verifies.
|
| 219 |
+
|
| 220 |
+
Returns a JSON-able dict:
|
| 221 |
+
``{ok, version, checks: {name: bool}, receipt_ok, receipt_head, error}``
|
| 222 |
+
``ok`` is True iff every check passed. On unexpected failure ``ok`` is
|
| 223 |
+
False and ``error`` carries the message — this function is designed to be
|
| 224 |
+
safe to call in a health probe and will not raise.
|
| 225 |
+
"""
|
| 226 |
+
checks: Dict[str, bool] = {}
|
| 227 |
+
receipt_ok = False
|
| 228 |
+
receipt_head = _GENESIS_HEAD
|
| 229 |
+
error = None
|
| 230 |
+
try:
|
| 231 |
+
torch.manual_seed(0)
|
| 232 |
+
x = torch.randn(4, 64, dtype=torch.float32)
|
| 233 |
+
w = torch.randn(64, dtype=torch.float32)
|
| 234 |
+
b = torch.randn(64, dtype=torch.float32)
|
| 235 |
+
res = torch.randn(4, 64, dtype=torch.float32)
|
| 236 |
+
eps_r, eps_l = 1e-6, 1e-5
|
| 237 |
+
|
| 238 |
+
# rms_norm vs Llama-style fp32 reference
|
| 239 |
+
xf = x.to(torch.float32)
|
| 240 |
+
ref_rms = (xf * torch.rsqrt(xf.pow(2).mean(-1, keepdim=True) + eps_r)) * w
|
| 241 |
+
checks["rms_norm"] = bool(
|
| 242 |
+
torch.allclose(rms_norm(x, weight=w, eps=eps_r), ref_rms, rtol=1e-5, atol=1e-5)
|
| 243 |
+
)
|
| 244 |
+
|
| 245 |
+
# layer_norm vs torch reference
|
| 246 |
+
ref_ln = torch.nn.functional.layer_norm(x, (64,), weight=w, bias=b, eps=eps_l)
|
| 247 |
+
checks["layer_norm"] = bool(
|
| 248 |
+
torch.allclose(layer_norm(x, weight=w, bias=b, eps=eps_l), ref_ln,
|
| 249 |
+
rtol=1e-5, atol=1e-5)
|
| 250 |
+
)
|
| 251 |
+
|
| 252 |
+
# fused_add_rms_norm vs unfused path
|
| 253 |
+
y_f, new_res = fused_add_rms_norm(x, res, weight=w, eps=eps_r)
|
| 254 |
+
h = x.to(torch.float32) + res.to(torch.float32)
|
| 255 |
+
ref_y = rms_norm(h.to(x.dtype), weight=w, eps=eps_r)
|
| 256 |
+
checks["fused_add_rms_norm"] = bool(
|
| 257 |
+
torch.allclose(y_f, ref_y, rtol=1e-5, atol=1e-5)
|
| 258 |
+
and torch.allclose(new_res, h.to(x.dtype), rtol=1e-6, atol=1e-6)
|
| 259 |
+
)
|
| 260 |
+
|
| 261 |
+
# governance on a private chain: one emit, chain verifies
|
| 262 |
+
probe_chain = ReceiptChain()
|
| 263 |
+
rms_norm(x, weight=w, eps=eps_r, chain=probe_chain)
|
| 264 |
+
ok, depth, brk = probe_chain.verify()
|
| 265 |
+
receipt_ok = bool(ok and depth == 1 and brk == -1)
|
| 266 |
+
receipt_head = probe_chain.head()
|
| 267 |
+
checks["governance"] = receipt_ok
|
| 268 |
+
except Exception as exc: # never raise from a health probe
|
| 269 |
+
error = f"{type(exc).__name__}: {exc}"
|
| 270 |
+
|
| 271 |
+
ok = bool(checks) and all(checks.values()) and error is None
|
| 272 |
+
return {
|
| 273 |
+
"ok": ok,
|
| 274 |
+
"version": __version__,
|
| 275 |
+
"checks": checks,
|
| 276 |
+
"receipt_ok": receipt_ok,
|
| 277 |
+
"receipt_head": receipt_head,
|
| 278 |
+
"error": error,
|
| 279 |
+
}
|
build/torch-universal/szl_lambda_gate/governed_norm/_norm.py
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
+
# © 2026 SZL Holdings · Stephen P. Lutar · ORCID 0009-0001-0110-4173
|
| 3 |
+
"""Pure-PyTorch normalization primitives for the SZL governed-norm kernel.
|
| 4 |
+
|
| 5 |
+
These are correctness-verified reference implementations (RMSNorm, LayerNorm,
|
| 6 |
+
and the residual-fused RMSNorm pattern used by transformer blocks) written in
|
| 7 |
+
pure PyTorch. They run on CPU and CUDA, are torch.compile-friendly, and depend
|
| 8 |
+
ONLY on torch + the Python standard library (a Kernel Hub requirement for
|
| 9 |
+
universal kernels).
|
| 10 |
+
|
| 11 |
+
HONESTY: this is a *universal* (pure-Python) kernel. It does NOT ship a
|
| 12 |
+
hand-tuned CUDA/Triton binary, so it is a correctness reference, not a
|
| 13 |
+
speed record. We make no fabricated benchmark claims. Where it adds value
|
| 14 |
+
is the optional *governed* path (see _receipt.py): every normalization call
|
| 15 |
+
can emit a content-addressed, hash-chained receipt of its inputs/outputs so
|
| 16 |
+
the operation is auditable — SZL Holdings' provenance doctrine applied at
|
| 17 |
+
the kernel layer.
|
| 18 |
+
|
| 19 |
+
Numerical convention (all ops): reductions and the normalization math are
|
| 20 |
+
computed in float32 for stability, then the result is cast back to the input
|
| 21 |
+
dtype. This is the standard Llama-style convention and is what makes
|
| 22 |
+
float16 / bfloat16 inputs numerically well-behaved.
|
| 23 |
+
|
| 24 |
+
Validation convention: guards below are cheap, branch-only checks on metadata
|
| 25 |
+
(dtype / ndim / shape / device) — they allocate nothing on the happy path and
|
| 26 |
+
constant-fold away under torch.compile, so they do not perturb traced graphs.
|
| 27 |
+
They exist to turn silent broadcasting / device-mismatch bugs into clear,
|
| 28 |
+
early errors. A zero-size normalized last dimension is rejected (normalizing
|
| 29 |
+
over zero elements is undefined); a single-element last dimension is allowed
|
| 30 |
+
(RMSNorm yields sign(x); LayerNorm yields 0, matching F.layer_norm).
|
| 31 |
+
|
| 32 |
+
Non-finite convention (NaN / Inf inputs): these ops do NOT sanitize their
|
| 33 |
+
input. A NaN or Inf in the input propagates through the reduction and appears
|
| 34 |
+
in the output, exactly as it would in torch.nn.functional.layer_norm / a
|
| 35 |
+
hand-written kernel. We deliberately do NOT silently replace non-finite values
|
| 36 |
+
(that would hide upstream numerical bugs); detecting/handling them is the
|
| 37 |
+
caller's responsibility. This propagation behavior is covered by regression
|
| 38 |
+
tests so it cannot change unnoticed.
|
| 39 |
+
"""
|
| 40 |
+
from typing import Optional
|
| 41 |
+
|
| 42 |
+
import torch
|
| 43 |
+
|
| 44 |
+
# Floating dtypes this kernel supports. Integer / complex inputs are rejected
|
| 45 |
+
# early with a clear message rather than silently producing garbage.
|
| 46 |
+
_SUPPORTED_DTYPES = (torch.float16, torch.bfloat16, torch.float32, torch.float64)
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
def _compute_dtype(in_dtype: torch.dtype) -> torch.dtype:
|
| 50 |
+
"""Reduction/normalization compute dtype.
|
| 51 |
+
|
| 52 |
+
Low-precision inputs (fp16/bf16) are upcast to float32 for stability — the
|
| 53 |
+
standard Llama-style convention. float64 inputs are NOT downcast: doing so
|
| 54 |
+
would silently lose precision (and break gradcheck), so we keep float64.
|
| 55 |
+
"""
|
| 56 |
+
return torch.float32 if in_dtype in (torch.float16, torch.bfloat16) else in_dtype
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def _check_input(x: torch.Tensor, name: str = "x") -> None:
|
| 60 |
+
"""Cheap, allocation-free guards on the primary input tensor.
|
| 61 |
+
|
| 62 |
+
Only inspects metadata (type / dtype / ndim), so it is constant-folded by
|
| 63 |
+
torch.compile and adds no runtime tensor work on the happy path.
|
| 64 |
+
"""
|
| 65 |
+
if not isinstance(x, torch.Tensor):
|
| 66 |
+
raise TypeError(f"{name} must be a torch.Tensor, got {type(x).__name__}")
|
| 67 |
+
if x.dtype not in _SUPPORTED_DTYPES:
|
| 68 |
+
raise TypeError(
|
| 69 |
+
f"{name} has unsupported dtype {x.dtype}; "
|
| 70 |
+
f"expected one of {tuple(str(d) for d in _SUPPORTED_DTYPES)}"
|
| 71 |
+
)
|
| 72 |
+
if x.dim() < 1:
|
| 73 |
+
raise ValueError(
|
| 74 |
+
f"{name} must have at least 1 dimension (the normalized dim); "
|
| 75 |
+
f"got a {x.dim()}-d tensor"
|
| 76 |
+
)
|
| 77 |
+
# A zero-size normalized (last) dimension is mathematically undefined:
|
| 78 |
+
# mean/RMS over zero elements is NaN, so normalization has no meaning.
|
| 79 |
+
# Reject it early with a clear message instead of silently returning an
|
| 80 |
+
# empty/NaN tensor (the classic shape-bug-masquerading-as-success case).
|
| 81 |
+
if x.shape[-1] == 0:
|
| 82 |
+
raise ValueError(
|
| 83 |
+
f"{name} has a zero-size normalized last dimension {tuple(x.shape)}; "
|
| 84 |
+
f"normalization over zero elements is undefined"
|
| 85 |
+
)
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
def _check_affine(
|
| 89 |
+
x: torch.Tensor,
|
| 90 |
+
param: Optional[torch.Tensor],
|
| 91 |
+
name: str,
|
| 92 |
+
) -> None:
|
| 93 |
+
"""Validate an optional affine parameter (weight/bias/residual peer).
|
| 94 |
+
|
| 95 |
+
Enforces that the parameter is 1-D and matches the normalized (last)
|
| 96 |
+
dimension, and lives on the same device as ``x``. This catches the
|
| 97 |
+
classic silent-broadcast bug where a mis-shaped weight would broadcast
|
| 98 |
+
instead of erroring. Metadata-only: no allocations.
|
| 99 |
+
"""
|
| 100 |
+
if param is None:
|
| 101 |
+
return
|
| 102 |
+
if not isinstance(param, torch.Tensor):
|
| 103 |
+
raise TypeError(f"{name} must be a torch.Tensor or None, got {type(param).__name__}")
|
| 104 |
+
if param.device != x.device:
|
| 105 |
+
raise ValueError(
|
| 106 |
+
f"{name} is on device {param.device} but x is on {x.device}; "
|
| 107 |
+
f"move them to the same device"
|
| 108 |
+
)
|
| 109 |
+
last = x.shape[-1]
|
| 110 |
+
if param.dim() != 1 or param.shape[0] != last:
|
| 111 |
+
raise ValueError(
|
| 112 |
+
f"{name} must be 1-D with shape ({last},) to match the normalized "
|
| 113 |
+
f"last dimension of x; got shape {tuple(param.shape)}"
|
| 114 |
+
)
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
def _check_eps(eps: float) -> None:
|
| 118 |
+
"""eps must be a positive, finite scalar (rsqrt(var+eps) must be safe)."""
|
| 119 |
+
e = float(eps)
|
| 120 |
+
if not (e > 0.0) or e != e or e == float("inf"):
|
| 121 |
+
raise ValueError(f"eps must be a positive finite float, got {eps!r}")
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
def rms_norm(
|
| 125 |
+
x: torch.Tensor,
|
| 126 |
+
weight: Optional[torch.Tensor] = None,
|
| 127 |
+
eps: float = 1e-6,
|
| 128 |
+
) -> torch.Tensor:
|
| 129 |
+
"""Root-mean-square layer normalization over the last dimension.
|
| 130 |
+
|
| 131 |
+
y = x / sqrt(mean(x^2, dim=-1) + eps) * weight
|
| 132 |
+
|
| 133 |
+
Computed in float32 for numerical stability, then cast back to the input
|
| 134 |
+
dtype (the standard, correctness-preserving convention used by Llama-style
|
| 135 |
+
RMSNorm). `weight` is optional; when omitted, no affine scale is applied.
|
| 136 |
+
|
| 137 |
+
Raises clear TypeError/ValueError on bad dtype, rank, eps, or a weight
|
| 138 |
+
whose shape/device does not match x's normalized dimension.
|
| 139 |
+
"""
|
| 140 |
+
_check_input(x)
|
| 141 |
+
_check_eps(eps)
|
| 142 |
+
_check_affine(x, weight, "weight")
|
| 143 |
+
|
| 144 |
+
in_dtype = x.dtype
|
| 145 |
+
xf = x.to(_compute_dtype(in_dtype))
|
| 146 |
+
variance = xf.pow(2).mean(dim=-1, keepdim=True)
|
| 147 |
+
xf = xf * torch.rsqrt(variance + eps)
|
| 148 |
+
out = xf.to(in_dtype)
|
| 149 |
+
if weight is not None:
|
| 150 |
+
out = out * weight
|
| 151 |
+
return out
|
| 152 |
+
|
| 153 |
+
|
| 154 |
+
def layer_norm(
|
| 155 |
+
x: torch.Tensor,
|
| 156 |
+
weight: Optional[torch.Tensor] = None,
|
| 157 |
+
bias: Optional[torch.Tensor] = None,
|
| 158 |
+
eps: float = 1e-5,
|
| 159 |
+
) -> torch.Tensor:
|
| 160 |
+
"""Standard layer normalization over the last dimension.
|
| 161 |
+
|
| 162 |
+
Mean/variance computed in float32 for stability, then cast back. Matches
|
| 163 |
+
torch.nn.functional.layer_norm semantics for the normalized-shape = last
|
| 164 |
+
dim case; verified against it in the test suite.
|
| 165 |
+
|
| 166 |
+
Raises clear TypeError/ValueError on bad dtype, rank, eps, or a
|
| 167 |
+
weight/bias whose shape/device does not match x's normalized dimension.
|
| 168 |
+
"""
|
| 169 |
+
_check_input(x)
|
| 170 |
+
_check_eps(eps)
|
| 171 |
+
_check_affine(x, weight, "weight")
|
| 172 |
+
_check_affine(x, bias, "bias")
|
| 173 |
+
|
| 174 |
+
in_dtype = x.dtype
|
| 175 |
+
xf = x.to(_compute_dtype(in_dtype))
|
| 176 |
+
mean = xf.mean(dim=-1, keepdim=True)
|
| 177 |
+
# Biased (population) variance = mean of squared deviations. We compute it
|
| 178 |
+
# directly rather than via Tensor.var(unbiased=False): torch's .var emits a
|
| 179 |
+
# "degrees of freedom <= 0" UserWarning when the normalized dim has a single
|
| 180 |
+
# element, even though unbiased=False is well-defined there (variance 0).
|
| 181 |
+
# Computing it ourselves matches F.layer_norm exactly and stays silent and
|
| 182 |
+
# torch.compile(fullgraph=True)-clean for the single-element edge case.
|
| 183 |
+
centered = xf - mean
|
| 184 |
+
var = centered.pow(2).mean(dim=-1, keepdim=True)
|
| 185 |
+
xf = centered * torch.rsqrt(var + eps)
|
| 186 |
+
out = xf.to(in_dtype)
|
| 187 |
+
if weight is not None:
|
| 188 |
+
out = out * weight
|
| 189 |
+
if bias is not None:
|
| 190 |
+
out = out + bias
|
| 191 |
+
return out
|
| 192 |
+
|
| 193 |
+
|
| 194 |
+
def fused_add_rms_norm(
|
| 195 |
+
x: torch.Tensor,
|
| 196 |
+
residual: torch.Tensor,
|
| 197 |
+
weight: Optional[torch.Tensor] = None,
|
| 198 |
+
eps: float = 1e-6,
|
| 199 |
+
):
|
| 200 |
+
"""Residual-add followed by RMSNorm — the canonical transformer block pattern.
|
| 201 |
+
|
| 202 |
+
h = x + residual # updated residual stream
|
| 203 |
+
y = rms_norm(h, weight, eps)
|
| 204 |
+
return y, h
|
| 205 |
+
|
| 206 |
+
This mirrors the `fused_add_rms_norm` used in real LLM inference stacks
|
| 207 |
+
(e.g. the pre-norm transformer block: the normalized output `y` feeds the
|
| 208 |
+
sublayer, while the un-normalized sum `h` is carried forward as the next
|
| 209 |
+
residual). We return BOTH so callers can thread the residual stream, which
|
| 210 |
+
is exactly why the fused form exists.
|
| 211 |
+
|
| 212 |
+
HONESTY: "fused" here means *logically* fused (one Python op, one float32
|
| 213 |
+
cast path, the add done in float32 alongside the norm) — it is a correct,
|
| 214 |
+
allocation-conscious pure-PyTorch reference, not a hand-written fused CUDA
|
| 215 |
+
kernel. No speed claims are made.
|
| 216 |
+
|
| 217 |
+
The add is performed in float32 so that, for float16/bfloat16 inputs, the
|
| 218 |
+
residual accumulation does not lose precision before normalization — this
|
| 219 |
+
matches high-quality reference implementations.
|
| 220 |
+
"""
|
| 221 |
+
_check_input(x, "x")
|
| 222 |
+
_check_input(residual, "residual")
|
| 223 |
+
_check_eps(eps)
|
| 224 |
+
if residual.shape != x.shape:
|
| 225 |
+
raise ValueError(
|
| 226 |
+
f"residual shape {tuple(residual.shape)} must equal x shape "
|
| 227 |
+
f"{tuple(x.shape)} for the residual add"
|
| 228 |
+
)
|
| 229 |
+
if residual.device != x.device:
|
| 230 |
+
raise ValueError(
|
| 231 |
+
f"residual is on device {residual.device} but x is on {x.device}; "
|
| 232 |
+
f"move them to the same device"
|
| 233 |
+
)
|
| 234 |
+
_check_affine(x, weight, "weight")
|
| 235 |
+
|
| 236 |
+
in_dtype = x.dtype
|
| 237 |
+
cdt = _compute_dtype(in_dtype)
|
| 238 |
+
# Add in compute dtype, keep both the normalized output and the residual.
|
| 239 |
+
hf = x.to(cdt) + residual.to(cdt)
|
| 240 |
+
new_residual = hf.to(in_dtype)
|
| 241 |
+
variance = hf.pow(2).mean(dim=-1, keepdim=True)
|
| 242 |
+
yf = hf * torch.rsqrt(variance + eps)
|
| 243 |
+
out = yf.to(in_dtype)
|
| 244 |
+
if weight is not None:
|
| 245 |
+
out = out * weight
|
| 246 |
+
return out, new_residual
|
build/torch-universal/szl_lambda_gate/governed_norm/_receipt.py
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
+
# © 2026 SZL Holdings · Stephen P. Lutar · ORCID 0009-0001-0110-4173
|
| 3 |
+
"""Content-addressed governance receipts for normalization calls.
|
| 4 |
+
|
| 5 |
+
SZL Holdings' provenance doctrine applied at the kernel layer: when a
|
| 6 |
+
normalization runs in *governed* mode, it emits a small, deterministic
|
| 7 |
+
receipt describing the call — input shape/dtype, eps, and a SHA3-256 digest
|
| 8 |
+
of the (quantized) output tensor — and hash-chains it to the previous
|
| 9 |
+
receipt. This makes a sequence of kernel calls independently auditable
|
| 10 |
+
without trusting the caller.
|
| 11 |
+
|
| 12 |
+
HONESTY:
|
| 13 |
+
- The digest is a real SHA3-256 over the output bytes (rounded to a fixed
|
| 14 |
+
decimal precision so it is reproducible across runs/devices). It is an
|
| 15 |
+
integrity fingerprint, NOT a cryptographic signature — we never claim
|
| 16 |
+
it proves authorship. DSSE signing is a separate, out-of-band concern.
|
| 17 |
+
- Receipts are kept in an in-process, append-only chain. Nothing is written
|
| 18 |
+
to disk or the network from inside the kernel.
|
| 19 |
+
- Stdlib + torch (+ numpy for the output digest) only — Kernel Hub
|
| 20 |
+
universal-kernel requirement.
|
| 21 |
+
- The canonical szl-receipt v0.2.0 evidence binding (``emit_receipt``) is
|
| 22 |
+
ADDITIVE and IMPORT-GUARDED: with szl-receipt absent it returns ``None`` and
|
| 23 |
+
the kernel runs unchanged. It binds subject / input-digest / output-digest /
|
| 24 |
+
policy-id / energy; energy is the literal string "UNAVAILABLE" because this
|
| 25 |
+
kernel measures NO joules — a value is never fabricated. Like the SHA3-256
|
| 26 |
+
chain, it is an EVIDENCE trail, NOT a proof of correctness.
|
| 27 |
+
"""
|
| 28 |
+
import hashlib
|
| 29 |
+
import json
|
| 30 |
+
import threading
|
| 31 |
+
import time
|
| 32 |
+
from typing import Any, Dict, List, Optional, Union
|
| 33 |
+
|
| 34 |
+
import torch
|
| 35 |
+
|
| 36 |
+
_GENESIS = "0" * 64
|
| 37 |
+
|
| 38 |
+
# Logical signing-authority label stamped onto signature envelopes.
|
| 39 |
+
_ORGAN = "szl-governed-norm"
|
| 40 |
+
|
| 41 |
+
# Governing policy id bound into every canonical szl-receipt evidence binding.
|
| 42 |
+
_POLICY_ID = "szl-governed-norm/provenance@v1"
|
| 43 |
+
|
| 44 |
+
# This universal kernel measures NO joules. The honesty doctrine forbids
|
| 45 |
+
# fabricating an energy value, so the canonical binding records the literal
|
| 46 |
+
# string "UNAVAILABLE" rather than a placeholder number.
|
| 47 |
+
_ENERGY_UNAVAILABLE = "UNAVAILABLE"
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
def _maybe_sign(
|
| 51 |
+
body: Dict[str, Any],
|
| 52 |
+
sign_key: Optional[Union[str, bytes]],
|
| 53 |
+
organ: str,
|
| 54 |
+
) -> Optional[Dict[str, Any]]:
|
| 55 |
+
"""ADDITIVE szl-receipt signature layer over the receipt *body*.
|
| 56 |
+
|
| 57 |
+
Returns a DSSE envelope (from ``szl_receipt.sign_receipt``) covering the
|
| 58 |
+
exact canonical body, or ``None`` when szl-receipt is not installed (the
|
| 59 |
+
kernel then behaves exactly as before). Doctrine: with no *sign_key* the
|
| 60 |
+
envelope is UNSIGNED-honest (``signed=False``); a signature is NEVER
|
| 61 |
+
fabricated. This is distinct from and additive to the SHA3-256 chain
|
| 62 |
+
integrity hash (``digest``) — szl-receipt's envelope carries its own
|
| 63 |
+
SHA-256 ``digest``/``algo`` so the two integrity hashes are explicit.
|
| 64 |
+
"""
|
| 65 |
+
try:
|
| 66 |
+
from szl_receipt import Receipt, sign_receipt
|
| 67 |
+
except Exception: # noqa: BLE001 - signing is optional; absence is honest
|
| 68 |
+
return None
|
| 69 |
+
env = sign_receipt(Receipt(kind="governed-norm", body=body),
|
| 70 |
+
sign_key, organ=organ)
|
| 71 |
+
return env
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
def _tensor_digest(t: torch.Tensor, decimals: int = 6) -> str:
|
| 75 |
+
"""Deterministic SHA3-256 over a tensor's rounded float32 contents.
|
| 76 |
+
|
| 77 |
+
Rounding to a fixed number of decimals makes the digest stable across
|
| 78 |
+
devices/dtypes for the same logical values (tiny FP noise won't change
|
| 79 |
+
it). This is an integrity fingerprint, not a signature.
|
| 80 |
+
"""
|
| 81 |
+
flat = t.detach().to(torch.float32).reshape(-1)
|
| 82 |
+
# Round to `decimals` places, integerize, hash the raw bytes. CPU move is
|
| 83 |
+
# required to read bytes; kept O(n) and allocation-light.
|
| 84 |
+
scaled = torch.round(flat * (10 ** decimals)).to(torch.int64).cpu().numpy().tobytes()
|
| 85 |
+
h = hashlib.sha3_256()
|
| 86 |
+
h.update(scaled)
|
| 87 |
+
return h.hexdigest()
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
def _input_digest(x: torch.Tensor, eps: float) -> str:
|
| 91 |
+
"""SHA3-256 over the canonical JSON of a call's input spec.
|
| 92 |
+
|
| 93 |
+
Binds {input shape, dtype, eps} — the *shape* of the call, not the input
|
| 94 |
+
bytes — so the receipt is a compact fingerprint of what produced the
|
| 95 |
+
output. Deterministic and stdlib-only (json + hashlib).
|
| 96 |
+
"""
|
| 97 |
+
spec = {
|
| 98 |
+
"in_shape": list(x.shape),
|
| 99 |
+
"in_dtype": str(x.dtype).replace("torch.", ""),
|
| 100 |
+
"eps": float(eps),
|
| 101 |
+
}
|
| 102 |
+
raw = json.dumps(spec, sort_keys=True, separators=(",", ":")).encode("utf-8")
|
| 103 |
+
return hashlib.sha3_256(raw).hexdigest()
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
def emit_receipt(
|
| 107 |
+
op: str,
|
| 108 |
+
x: torch.Tensor,
|
| 109 |
+
out: torch.Tensor,
|
| 110 |
+
eps: float,
|
| 111 |
+
subject: Optional[str] = None,
|
| 112 |
+
policy_id: str = _POLICY_ID,
|
| 113 |
+
sign_key: Optional[Union[str, bytes]] = None,
|
| 114 |
+
organ: str = _ORGAN,
|
| 115 |
+
) -> Optional[Dict[str, Any]]:
|
| 116 |
+
"""Canonical szl-receipt v0.2.0 evidence binding for a governed-norm call.
|
| 117 |
+
|
| 118 |
+
ADDITIVE and IMPORT-GUARDED: returns ``None`` when szl-receipt is not
|
| 119 |
+
installed, so this universal Kernel-Hub kernel still imports and runs on
|
| 120 |
+
stdlib + torch + numpy alone. When szl-receipt is present it binds an
|
| 121 |
+
EVIDENCE trail and wraps it in a DSSE envelope via ``sign_receipt``:
|
| 122 |
+
|
| 123 |
+
subject organ / norm-call id (who/what emitted this)
|
| 124 |
+
input_digest SHA3-256 over canonical {input shape, dtype, eps}
|
| 125 |
+
output_digest the EXISTING SHA3-256 rounded-tensor digest of ``out``
|
| 126 |
+
policy_id the governing policy id
|
| 127 |
+
energy the literal string "UNAVAILABLE"
|
| 128 |
+
|
| 129 |
+
Doctrine (non-negotiable):
|
| 130 |
+
* A receipt is an integrity/EVIDENCE trail, NOT a proof of correctness.
|
| 131 |
+
* ``energy == "UNAVAILABLE"`` — this kernel measures NO joules; a joule is
|
| 132 |
+
NEVER fabricated.
|
| 133 |
+
* Keyless => UNSIGNED-honest (``signature["signed"] is False``); a
|
| 134 |
+
signature is NEVER fabricated. A real ``sign_key`` yields a real DSSE
|
| 135 |
+
signature over the exact canonical binding.
|
| 136 |
+
|
| 137 |
+
Returns the binding dict (subject/input_digest/output_digest/policy_id/
|
| 138 |
+
energy) with the DSSE envelope under ``signature``, or ``None`` when
|
| 139 |
+
szl-receipt is absent.
|
| 140 |
+
"""
|
| 141 |
+
try:
|
| 142 |
+
from szl_receipt import Receipt, sign_receipt
|
| 143 |
+
except Exception: # noqa: BLE001 - canonical binding is optional; absence is honest
|
| 144 |
+
return None
|
| 145 |
+
body = {
|
| 146 |
+
"subject": subject if subject is not None else f"{organ}/{op}",
|
| 147 |
+
"input_digest": _input_digest(x, eps),
|
| 148 |
+
"output_digest": _tensor_digest(out),
|
| 149 |
+
"policy_id": policy_id,
|
| 150 |
+
"energy": _ENERGY_UNAVAILABLE,
|
| 151 |
+
}
|
| 152 |
+
env = sign_receipt(Receipt(kind="governed-norm", body=body), sign_key, organ=organ)
|
| 153 |
+
return dict(body, signature=env)
|
| 154 |
+
|
| 155 |
+
|
| 156 |
+
class ReceiptChain:
|
| 157 |
+
"""Append-only, SHA3-256 hash-chained log of normalization receipts.
|
| 158 |
+
|
| 159 |
+
Each receipt: {seq, op, in_shape, in_dtype, eps, out_digest, prev, digest, ts}
|
| 160 |
+
digest = SHA3-256 over the canonical JSON body (excluding digest/ts).
|
| 161 |
+
verify() re-walks the chain and returns (ok, depth, first_break_seq).
|
| 162 |
+
"""
|
| 163 |
+
|
| 164 |
+
def __init__(self) -> None:
|
| 165 |
+
self._lock = threading.RLock()
|
| 166 |
+
self._records: List[Dict[str, Any]] = []
|
| 167 |
+
|
| 168 |
+
@staticmethod
|
| 169 |
+
def _digest_body(body: Dict[str, Any]) -> str:
|
| 170 |
+
raw = json.dumps(body, sort_keys=True, separators=(",", ":")).encode("utf-8")
|
| 171 |
+
return hashlib.sha3_256(raw).hexdigest()
|
| 172 |
+
|
| 173 |
+
def emit(
|
| 174 |
+
self,
|
| 175 |
+
op: str,
|
| 176 |
+
x: torch.Tensor,
|
| 177 |
+
out: torch.Tensor,
|
| 178 |
+
eps: float,
|
| 179 |
+
sign_key: Optional[Union[str, bytes]] = None,
|
| 180 |
+
organ: str = _ORGAN,
|
| 181 |
+
policy_id: str = _POLICY_ID,
|
| 182 |
+
) -> Dict[str, Any]:
|
| 183 |
+
with self._lock:
|
| 184 |
+
prev = self._records[-1]["digest"] if self._records else _GENESIS
|
| 185 |
+
seq = len(self._records)
|
| 186 |
+
body = {
|
| 187 |
+
"seq": seq,
|
| 188 |
+
"op": op,
|
| 189 |
+
"in_shape": list(x.shape),
|
| 190 |
+
"in_dtype": str(x.dtype).replace("torch.", ""),
|
| 191 |
+
"eps": float(eps),
|
| 192 |
+
"out_digest": _tensor_digest(out),
|
| 193 |
+
"prev": prev,
|
| 194 |
+
}
|
| 195 |
+
digest = self._digest_body(body)
|
| 196 |
+
rec = dict(body, digest=digest, ts=time.time())
|
| 197 |
+
sig = _maybe_sign(body, sign_key, organ)
|
| 198 |
+
if sig is not None:
|
| 199 |
+
rec["signature"] = sig
|
| 200 |
+
# ADDITIVE canonical szl-receipt v0.2.0 evidence binding. Import-
|
| 201 |
+
# guarded: None when szl-receipt is absent, so the universal kernel
|
| 202 |
+
# keeps working on stdlib + torch + numpy only. Binds subject /
|
| 203 |
+
# input-digest / output-digest / policy-id / energy; energy is
|
| 204 |
+
# "UNAVAILABLE" (no joules measured here). It does NOT enter the
|
| 205 |
+
# SHA3-256 chain body, so verify() is unaffected.
|
| 206 |
+
binding = emit_receipt(
|
| 207 |
+
op, x, out, eps,
|
| 208 |
+
subject=f"{organ}/{op}#{seq}",
|
| 209 |
+
policy_id=policy_id,
|
| 210 |
+
sign_key=sign_key,
|
| 211 |
+
organ=organ,
|
| 212 |
+
)
|
| 213 |
+
if binding is not None:
|
| 214 |
+
rec["receipt"] = binding
|
| 215 |
+
self._records.append(rec)
|
| 216 |
+
return rec
|
| 217 |
+
|
| 218 |
+
def head(self) -> str:
|
| 219 |
+
with self._lock:
|
| 220 |
+
return self._records[-1]["digest"] if self._records else _GENESIS
|
| 221 |
+
|
| 222 |
+
def count(self) -> int:
|
| 223 |
+
with self._lock:
|
| 224 |
+
return len(self._records)
|
| 225 |
+
|
| 226 |
+
def tail(self, n: int = 10) -> List[Dict[str, Any]]:
|
| 227 |
+
with self._lock:
|
| 228 |
+
return list(self._records[-n:])
|
| 229 |
+
|
| 230 |
+
def verify(self):
|
| 231 |
+
"""Re-walk the chain. Returns (ok: bool, depth: int, first_break: int)."""
|
| 232 |
+
with self._lock:
|
| 233 |
+
prev = _GENESIS
|
| 234 |
+
for i, rec in enumerate(self._records):
|
| 235 |
+
body = {k: rec[k] for k in
|
| 236 |
+
("seq", "op", "in_shape", "in_dtype", "eps", "out_digest", "prev")}
|
| 237 |
+
if rec["prev"] != prev or rec["digest"] != self._digest_body(body):
|
| 238 |
+
return (False, len(self._records), i)
|
| 239 |
+
prev = rec["digest"]
|
| 240 |
+
return (True, len(self._records), -1)
|
| 241 |
+
|
| 242 |
+
|
| 243 |
+
# Module-level default chain (opt-in: only written when governed=True is used).
|
| 244 |
+
_DEFAULT_CHAIN: Optional[ReceiptChain] = None
|
| 245 |
+
_chain_lock = threading.Lock()
|
| 246 |
+
|
| 247 |
+
|
| 248 |
+
def default_chain() -> ReceiptChain:
|
| 249 |
+
global _DEFAULT_CHAIN
|
| 250 |
+
with _chain_lock:
|
| 251 |
+
if _DEFAULT_CHAIN is None:
|
| 252 |
+
_DEFAULT_CHAIN = ReceiptChain()
|
| 253 |
+
return _DEFAULT_CHAIN
|
build/torch-universal/szl_lambda_gate/governed_norm/layers.py
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
+
# © 2026 SZL Holdings · Stephen P. Lutar · ORCID 0009-0001-0110-4173
|
| 3 |
+
"""Hub-compliant kernel layers.
|
| 4 |
+
|
| 5 |
+
Per the Kernel Hub `kernel-requirements`, layers exposed for extension must
|
| 6 |
+
be PURE torch.nn.Module subclasses:
|
| 7 |
+
- no custom __init__,
|
| 8 |
+
- no class variables,
|
| 9 |
+
- only a `forward` method,
|
| 10 |
+
- forward signature compatible with the module it extends.
|
| 11 |
+
|
| 12 |
+
These layers therefore read their parameters (weight/bias/eps) off the
|
| 13 |
+
module instance they are bound to (set by the host model), and only define
|
| 14 |
+
`forward`. They are drop-in replacements for an existing RMSNorm/LayerNorm
|
| 15 |
+
module via the `kernels` layer-mapping mechanism.
|
| 16 |
+
"""
|
| 17 |
+
import torch
|
| 18 |
+
from torch import nn
|
| 19 |
+
|
| 20 |
+
from ._norm import fused_add_rms_norm, layer_norm, rms_norm
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
class RMSNorm(nn.Module):
|
| 24 |
+
"""Pure RMSNorm layer. Expects the host module to provide `self.weight`
|
| 25 |
+
(optional) and `self.variance_epsilon` or `self.eps`."""
|
| 26 |
+
|
| 27 |
+
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
|
| 28 |
+
weight = getattr(self, "weight", None)
|
| 29 |
+
eps = getattr(self, "variance_epsilon", None)
|
| 30 |
+
if eps is None:
|
| 31 |
+
eps = getattr(self, "eps", 1e-6)
|
| 32 |
+
return rms_norm(hidden_states, weight=weight, eps=float(eps))
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
class LayerNorm(nn.Module):
|
| 36 |
+
"""Pure LayerNorm layer. Expects the host module to provide `self.weight`
|
| 37 |
+
(optional), `self.bias` (optional), and `self.eps`."""
|
| 38 |
+
|
| 39 |
+
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
|
| 40 |
+
weight = getattr(self, "weight", None)
|
| 41 |
+
bias = getattr(self, "bias", None)
|
| 42 |
+
eps = getattr(self, "eps", 1e-5)
|
| 43 |
+
return layer_norm(hidden_states, weight=weight, bias=bias, eps=float(eps))
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
class FusedAddRMSNorm(nn.Module):
|
| 47 |
+
"""Pure residual-add + RMSNorm layer for pre-norm transformer blocks.
|
| 48 |
+
|
| 49 |
+
Expects the host module to provide `self.weight` (optional) and
|
| 50 |
+
`self.variance_epsilon` or `self.eps`. Returns `(normalized, new_residual)`
|
| 51 |
+
where `new_residual = hidden_states + residual` is carried forward as the
|
| 52 |
+
next block's residual stream.
|
| 53 |
+
"""
|
| 54 |
+
|
| 55 |
+
def forward(self, hidden_states: torch.Tensor, residual: torch.Tensor):
|
| 56 |
+
weight = getattr(self, "weight", None)
|
| 57 |
+
eps = getattr(self, "variance_epsilon", None)
|
| 58 |
+
if eps is None:
|
| 59 |
+
eps = getattr(self, "eps", 1e-6)
|
| 60 |
+
return fused_add_rms_norm(hidden_states, residual, weight=weight, eps=float(eps))
|
build/torch-universal/szl_lambda_gate/layers.py
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
+
# © 2026 SZL Holdings · Stephen P. Lutar · ORCID 0009-0001-0110-4173
|
| 3 |
+
"""Hub-compliant kernel layer for the szl-lambda-gate kernel.
|
| 4 |
+
|
| 5 |
+
Per the Kernel Hub `kernel-requirements`, layers exposed for extension must be
|
| 6 |
+
PURE torch.nn.Module subclasses:
|
| 7 |
+
- no custom __init__,
|
| 8 |
+
- no class variables,
|
| 9 |
+
- only a `forward` method.
|
| 10 |
+
|
| 11 |
+
The layer therefore reads its parameters (weights / threshold) off the module
|
| 12 |
+
instance it is bound to (set by the host model) and only defines `forward`.
|
| 13 |
+
|
| 14 |
+
HONESTY: `LambdaGate` emits an ADVISORY governance signal (the weighted
|
| 15 |
+
geometric mean Λ plus a pass/fail vs threshold). Λ is NOT proven trust; its
|
| 16 |
+
uniqueness is Conjecture 1 (open).
|
| 17 |
+
"""
|
| 18 |
+
import torch
|
| 19 |
+
from torch import nn
|
| 20 |
+
|
| 21 |
+
from ._lambda import lambda_aggregate, lambda_gate
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
class LambdaGate(nn.Module):
|
| 25 |
+
"""Pure Λ-gate layer.
|
| 26 |
+
|
| 27 |
+
Reads optional ``self.weights`` (1-D, length k) and ``self.threshold``
|
| 28 |
+
(float) off the bound module instance. An unset threshold uses the legacy
|
| 29 |
+
0.5 with a DeprecationWarning (see ``lambda_gate``); set it explicitly.
|
| 30 |
+
|
| 31 |
+
forward(axes) -> LambdaGateResult(score, passed, threshold, advisory) where
|
| 32 |
+
``score`` = Λ(axes) over the last dim and ``passed`` = score >= threshold.
|
| 33 |
+
Differentiable in ``score`` w.r.t. ``axes``.
|
| 34 |
+
"""
|
| 35 |
+
|
| 36 |
+
def forward(self, axes: torch.Tensor):
|
| 37 |
+
weights = getattr(self, "weights", None)
|
| 38 |
+
threshold = getattr(self, "threshold", None)
|
| 39 |
+
if threshold is not None:
|
| 40 |
+
threshold = float(threshold)
|
| 41 |
+
return lambda_gate(axes, weights=weights, threshold=threshold)
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
class LambdaAggregate(nn.Module):
|
| 45 |
+
"""Pure Λ-aggregator layer: forward(axes) -> Λ(axes) tensor in [0,1].
|
| 46 |
+
|
| 47 |
+
Reads optional ``self.weights`` (1-D, length k) off the bound module
|
| 48 |
+
instance; uniform weights when absent. Returns just the score (no gate),
|
| 49 |
+
fully differentiable w.r.t. ``axes``.
|
| 50 |
+
"""
|
| 51 |
+
|
| 52 |
+
def forward(self, axes: torch.Tensor) -> torch.Tensor:
|
| 53 |
+
weights = getattr(self, "weights", None)
|
| 54 |
+
return lambda_aggregate(axes, weights=weights)
|
distribution.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"files":{"build.toml":"5c5ebbfe83770a898b8b8893e342c2c5547c65cd2b5a96146b14e3abb9411f3b","build/torch-cpu/__init__.py":"36e8a78e55913655a0834cb60fad4711a2ad5bf54e92d4473fcc36363de8a634","build/torch-cpu/metadata.json":"d15c8285af060f9c1e43ff269650508fe0f31a0297cfbd309aff552e9de8d3d4","build/torch-cpu/szl_lambda_gate/__init__.py":"4297e491a72e2941b15e2b6a0582ead273383670ac5fbb8d14e2c76dec9ed15d","build/torch-cpu/szl_lambda_gate/_lambda.py":"f953a38019c91a6fa8a9c411f55baa4f5f2b9e76d7153bba54bb8a5d9469af38","build/torch-cpu/szl_lambda_gate/_ops.py":"6adf1dc42e7a9e6da914e304da9720438e59c234104e11f3e5ccaa45ecee23b6","build/torch-cpu/szl_lambda_gate/_v1.py":"9ac2d8fe0a47ff191eba76e62b1e9dd9b2b54f64ba0c3d86306e0cdd58bc468c","build/torch-cpu/szl_lambda_gate/governed_norm/__init__.py":"50fe4fb09a165d0ae9a781f0ef3515388c752a14cde95f73687854f59a7dfadc","build/torch-cpu/szl_lambda_gate/governed_norm/_norm.py":"cf72ace281ec86c504426f75e23eebdb661a1e3a12e4dfcc6c8b9fa634b03942","build/torch-cpu/szl_lambda_gate/governed_norm/_receipt.py":"db1244bd184affdca929bd38ac728b2696f82ad565aaf82d14ca3845d4d748a9","build/torch-cpu/szl_lambda_gate/governed_norm/layers.py":"d79c83355cb2332b9d0e9d98584b86434e486532e27d30652df10158b30fc37b","build/torch-cpu/szl_lambda_gate/layers.py":"51da478e185ee4d16daa9971bdcbdcfcbb0c0c5b7837a002b97cd05cb968ea75","build/torch-universal/__init__.py":"36e8a78e55913655a0834cb60fad4711a2ad5bf54e92d4473fcc36363de8a634","build/torch-universal/metadata.json":"448ed5fb0c866455d045cdc7a75e1bbcaccc8d9f323039daf62cda1f5a35e0b4","build/torch-universal/szl_lambda_gate/__init__.py":"4297e491a72e2941b15e2b6a0582ead273383670ac5fbb8d14e2c76dec9ed15d","build/torch-universal/szl_lambda_gate/_lambda.py":"f953a38019c91a6fa8a9c411f55baa4f5f2b9e76d7153bba54bb8a5d9469af38","build/torch-universal/szl_lambda_gate/_ops.py":"6adf1dc42e7a9e6da914e304da9720438e59c234104e11f3e5ccaa45ecee23b6","build/torch-universal/szl_lambda_gate/_v1.py":"9ac2d8fe0a47ff191eba76e62b1e9dd9b2b54f64ba0c3d86306e0cdd58bc468c","build/torch-universal/szl_lambda_gate/governed_norm/__init__.py":"50fe4fb09a165d0ae9a781f0ef3515388c752a14cde95f73687854f59a7dfadc","build/torch-universal/szl_lambda_gate/governed_norm/_norm.py":"cf72ace281ec86c504426f75e23eebdb661a1e3a12e4dfcc6c8b9fa634b03942","build/torch-universal/szl_lambda_gate/governed_norm/_receipt.py":"db1244bd184affdca929bd38ac728b2696f82ad565aaf82d14ca3845d4d748a9","build/torch-universal/szl_lambda_gate/governed_norm/layers.py":"d79c83355cb2332b9d0e9d98584b86434e486532e27d30652df10158b30fc37b","build/torch-universal/szl_lambda_gate/layers.py":"51da478e185ee4d16daa9971bdcbdcfcbb0c0c5b7837a002b97cd05cb968ea75","reference/szl_lambda_v1.py":"57b264fa96056d1f2d77b797ba80eab087a729283f16416196fdd1423c9c532f","source/LICENSE":"bc8cef3450d423b046a3ba2b1a03089c8c795302f502cabbdbef055a68f0b1e7","spec/lambda_v1_vectors.json":"460bb966ff2bec4fd99e03d267b721f22c5fad731e0667723407a6909bd4dcec","spec/szl.lambda.v1.json":"04a8368568d59509c8603cdc45cf80f6b1395e751c77eabb1957bbe12906f2d9"},"packager":"scripts/build_lambda_distribution.py","schema":"szl.lambda/distribution.v1","source_commit":"7cb79cba7ecb5dbb9da59b2d6b516a98d2a114d6","source_files":{"LICENSE":"bc8cef3450d423b046a3ba2b1a03089c8c795302f502cabbdbef055a68f0b1e7","build.toml":"5c5ebbfe83770a898b8b8893e342c2c5547c65cd2b5a96146b14e3abb9411f3b","reference/szl_lambda_v1.py":"57b264fa96056d1f2d77b797ba80eab087a729283f16416196fdd1423c9c532f","spec/lambda_v1_vectors.json":"460bb966ff2bec4fd99e03d267b721f22c5fad731e0667723407a6909bd4dcec","spec/szl.lambda.v1.json":"04a8368568d59509c8603cdc45cf80f6b1395e751c77eabb1957bbe12906f2d9","torch-ext/szl_lambda_gate/__init__.py":"4297e491a72e2941b15e2b6a0582ead273383670ac5fbb8d14e2c76dec9ed15d","torch-ext/szl_lambda_gate/_lambda.py":"f953a38019c91a6fa8a9c411f55baa4f5f2b9e76d7153bba54bb8a5d9469af38","torch-ext/szl_lambda_gate/_ops.py":"6adf1dc42e7a9e6da914e304da9720438e59c234104e11f3e5ccaa45ecee23b6","torch-ext/szl_lambda_gate/_v1.py":"9ac2d8fe0a47ff191eba76e62b1e9dd9b2b54f64ba0c3d86306e0cdd58bc468c","torch-ext/szl_lambda_gate/governed_norm/__init__.py":"50fe4fb09a165d0ae9a781f0ef3515388c752a14cde95f73687854f59a7dfadc","torch-ext/szl_lambda_gate/governed_norm/_norm.py":"cf72ace281ec86c504426f75e23eebdb661a1e3a12e4dfcc6c8b9fa634b03942","torch-ext/szl_lambda_gate/governed_norm/_receipt.py":"db1244bd184affdca929bd38ac728b2696f82ad565aaf82d14ca3845d4d748a9","torch-ext/szl_lambda_gate/governed_norm/layers.py":"d79c83355cb2332b9d0e9d98584b86434e486532e27d30652df10158b30fc37b","torch-ext/szl_lambda_gate/layers.py":"51da478e185ee4d16daa9971bdcbdcfcbb0c0c5b7837a002b97cd05cb968ea75"},"source_repository":"https://github.com/szl-holdings/szl-lambda-gate","vectors_count":60,"vectors_sha256":"61bfb0410b9f0eaab0eb9f22f29cb7cb13cfde8c083fe308d895565d6ba9ebd4"}
|
reference/szl_lambda_v1.py
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
+
# © 2026 SZL Holdings · Stephen P. Lutar · ORCID 0009-0001-0110-4173
|
| 3 |
+
"""szl.lambda/v1 stdlib reference: Λ and its gate, exactly as spec/szl.lambda.v1.json says.
|
| 4 |
+
|
| 5 |
+
Λ_w(x) = ∏_k x_k^{w_k} = exp(fsum_k(w_k · log x_k)), Λ = 0 if some x_k == 0
|
| 6 |
+
|
| 7 |
+
Contract (every violation raises ``LambdaV1Error(code)``; nothing is clamped,
|
| 8 |
+
renormalised, defaulted or rounded):
|
| 9 |
+
|
| 10 |
+
* ``axes`` and ``weights`` are lists or tuples of real numbers (``bool`` is not
|
| 11 |
+
a number), of equal, non-zero length.
|
| 12 |
+
* every axis is finite and 0 <= x_k <= 1;
|
| 13 |
+
* every weight is finite and w_k > 0, and |fsum(w) - 1| <= 1e-12.
|
| 14 |
+
|
| 15 |
+
Checks run in phases, each over every element, so the code reported for an
|
| 16 |
+
input with several faults does not depend on axis order::
|
| 17 |
+
|
| 18 |
+
LAMBDA_TYPE_INVALID (container) > LAMBDA_EMPTY > LAMBDA_LENGTH_MISMATCH
|
| 19 |
+
> LAMBDA_TYPE_INVALID (element) > LAMBDA_NONFINITE_AXIS
|
| 20 |
+
> LAMBDA_AXIS_OUT_OF_RANGE > LAMBDA_NONFINITE_WEIGHT
|
| 21 |
+
> LAMBDA_WEIGHT_NONPOSITIVE > LAMBDA_WEIGHT_SUM
|
| 22 |
+
|
| 23 |
+
``gate_v1(axes, weights, tau)`` takes tau as a required argument (no implicit
|
| 24 |
+
default; the policy value lives in frontier/model_admit_contract.v1.json) and
|
| 25 |
+
returns ``(verdict, code)``:
|
| 26 |
+
|
| 27 |
+
* BLOCK with the error code if tau is invalid (checked first: real, finite,
|
| 28 |
+
0 < tau <= 1) or Λ raises;
|
| 29 |
+
* NO_GO / ZERO_VETO if some axis is 0 (log Λ = -inf; a veto, not an error);
|
| 30 |
+
* ABSTAIN / NUMERIC_TIE if |log Λ - log tau| <= TIE_EPS (1e-9);
|
| 31 |
+
* GO / None if log Λ > log tau, otherwise NO_GO / BELOW_TAU.
|
| 32 |
+
|
| 33 |
+
The compare is in log space on the unrounded value. Λ is advisory. Λ
|
| 34 |
+
uniqueness is Conjecture 1 (open) and nothing here depends on it.
|
| 35 |
+
|
| 36 |
+
Stdlib only. Other implementations port this file; they must agree on every
|
| 37 |
+
error code and verdict, and on values within each vector's ``value_tol``.
|
| 38 |
+
"""
|
| 39 |
+
from __future__ import annotations
|
| 40 |
+
|
| 41 |
+
import hashlib
|
| 42 |
+
import json
|
| 43 |
+
import math
|
| 44 |
+
import struct
|
| 45 |
+
from typing import Any, Optional, Tuple
|
| 46 |
+
|
| 47 |
+
SCHEMA = "szl.lambda/v1"
|
| 48 |
+
WEIGHT_SUM_TOL = 1e-12
|
| 49 |
+
TIE_EPS = 1e-9
|
| 50 |
+
|
| 51 |
+
TYPE_INVALID = "LAMBDA_TYPE_INVALID"
|
| 52 |
+
EMPTY = "LAMBDA_EMPTY"
|
| 53 |
+
LENGTH_MISMATCH = "LAMBDA_LENGTH_MISMATCH"
|
| 54 |
+
NONFINITE_AXIS = "LAMBDA_NONFINITE_AXIS"
|
| 55 |
+
AXIS_OUT_OF_RANGE = "LAMBDA_AXIS_OUT_OF_RANGE"
|
| 56 |
+
NONFINITE_WEIGHT = "LAMBDA_NONFINITE_WEIGHT"
|
| 57 |
+
WEIGHT_NONPOSITIVE = "LAMBDA_WEIGHT_NONPOSITIVE"
|
| 58 |
+
WEIGHT_SUM = "LAMBDA_WEIGHT_SUM"
|
| 59 |
+
TAU_INVALID = "LAMBDA_TAU_INVALID"
|
| 60 |
+
|
| 61 |
+
#: Every error code, in precedence order (tau is checked first by the gate).
|
| 62 |
+
ERROR_CODES = (
|
| 63 |
+
TYPE_INVALID,
|
| 64 |
+
EMPTY,
|
| 65 |
+
LENGTH_MISMATCH,
|
| 66 |
+
NONFINITE_AXIS,
|
| 67 |
+
AXIS_OUT_OF_RANGE,
|
| 68 |
+
NONFINITE_WEIGHT,
|
| 69 |
+
WEIGHT_NONPOSITIVE,
|
| 70 |
+
WEIGHT_SUM,
|
| 71 |
+
TAU_INVALID,
|
| 72 |
+
)
|
| 73 |
+
|
| 74 |
+
GO = "GO"
|
| 75 |
+
NO_GO = "NO_GO"
|
| 76 |
+
ABSTAIN = "ABSTAIN"
|
| 77 |
+
BLOCK = "BLOCK"
|
| 78 |
+
VERDICTS = (GO, NO_GO, ABSTAIN, BLOCK)
|
| 79 |
+
|
| 80 |
+
ZERO_VETO = "ZERO_VETO"
|
| 81 |
+
BELOW_TAU = "BELOW_TAU"
|
| 82 |
+
NUMERIC_TIE = "NUMERIC_TIE"
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
class LambdaV1Error(ValueError):
|
| 86 |
+
"""An input outside the szl.lambda/v1 contract. ``code`` is one of ERROR_CODES."""
|
| 87 |
+
|
| 88 |
+
def __init__(self, code: str, detail: str = "") -> None:
|
| 89 |
+
self.code = code
|
| 90 |
+
self.detail = detail
|
| 91 |
+
super().__init__(f"{code}: {detail}" if detail else code)
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
def _is_real(value: Any) -> bool:
|
| 95 |
+
return isinstance(value, (int, float)) and not isinstance(value, bool)
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
def _is_nonfinite(value: Any) -> bool:
|
| 99 |
+
# ints are always finite; only floats can be NaN or ±Inf.
|
| 100 |
+
return isinstance(value, float) and not math.isfinite(value)
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
def _validate(axes: Any, weights: Any) -> None:
|
| 104 |
+
for name, seq in (("axes", axes), ("weights", weights)):
|
| 105 |
+
if not isinstance(seq, (list, tuple)):
|
| 106 |
+
raise LambdaV1Error(TYPE_INVALID, f"{name} must be a list or tuple, got {type(seq).__name__}")
|
| 107 |
+
if len(axes) == 0 or len(weights) == 0:
|
| 108 |
+
raise LambdaV1Error(EMPTY, f"len(axes)={len(axes)}, len(weights)={len(weights)}")
|
| 109 |
+
if len(axes) != len(weights):
|
| 110 |
+
raise LambdaV1Error(LENGTH_MISMATCH, f"len(axes)={len(axes)} != len(weights)={len(weights)}")
|
| 111 |
+
for name, seq in (("axes", axes), ("weights", weights)):
|
| 112 |
+
for i, value in enumerate(seq):
|
| 113 |
+
if not _is_real(value):
|
| 114 |
+
raise LambdaV1Error(TYPE_INVALID, f"{name}[{i}] is {type(value).__name__}, not a real number")
|
| 115 |
+
for i, x in enumerate(axes):
|
| 116 |
+
if _is_nonfinite(x):
|
| 117 |
+
raise LambdaV1Error(NONFINITE_AXIS, f"axes[{i}]={x!r}")
|
| 118 |
+
for i, x in enumerate(axes):
|
| 119 |
+
if not 0 <= x <= 1:
|
| 120 |
+
raise LambdaV1Error(AXIS_OUT_OF_RANGE, f"axes[{i}]={x!r} is outside [0, 1]")
|
| 121 |
+
for i, w in enumerate(weights):
|
| 122 |
+
if _is_nonfinite(w):
|
| 123 |
+
raise LambdaV1Error(NONFINITE_WEIGHT, f"weights[{i}]={w!r}")
|
| 124 |
+
for i, w in enumerate(weights):
|
| 125 |
+
if not w > 0:
|
| 126 |
+
raise LambdaV1Error(WEIGHT_NONPOSITIVE, f"weights[{i}]={w!r} is not > 0")
|
| 127 |
+
try:
|
| 128 |
+
total = math.fsum(weights)
|
| 129 |
+
except OverflowError:
|
| 130 |
+
raise LambdaV1Error(WEIGHT_SUM, "sum of weights overflows a float") from None
|
| 131 |
+
if not abs(total - 1.0) <= WEIGHT_SUM_TOL:
|
| 132 |
+
raise LambdaV1Error(WEIGHT_SUM, f"fsum(weights)={total!r} is not within {WEIGHT_SUM_TOL} of 1")
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
def log_lambda_v1(axes: Any, weights: Any) -> float:
|
| 136 |
+
"""log Λ_w(x) = fsum(w_k · log x_k); -inf if some axis is 0. Raises LambdaV1Error."""
|
| 137 |
+
_validate(axes, weights)
|
| 138 |
+
if any(x == 0 for x in axes):
|
| 139 |
+
return -math.inf
|
| 140 |
+
return math.fsum(float(w) * math.log(float(x)) for x, w in zip(axes, weights))
|
| 141 |
+
|
| 142 |
+
|
| 143 |
+
def lambda_v1(axes: Any, weights: Any) -> float:
|
| 144 |
+
"""Λ_w(x) in [0, 1]; exactly 0.0 iff some axis is 0. Raises LambdaV1Error."""
|
| 145 |
+
log_lam = log_lambda_v1(axes, weights)
|
| 146 |
+
if log_lam == -math.inf:
|
| 147 |
+
return 0.0
|
| 148 |
+
return math.exp(log_lam)
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
def _check_tau(tau: Any) -> float:
|
| 152 |
+
if not _is_real(tau):
|
| 153 |
+
raise LambdaV1Error(TAU_INVALID, f"tau is {type(tau).__name__}, not a real number")
|
| 154 |
+
if _is_nonfinite(tau):
|
| 155 |
+
raise LambdaV1Error(TAU_INVALID, f"tau={tau!r} is not finite")
|
| 156 |
+
if not 0 < tau <= 1:
|
| 157 |
+
raise LambdaV1Error(TAU_INVALID, f"tau={tau!r} is outside (0, 1]")
|
| 158 |
+
return float(tau)
|
| 159 |
+
|
| 160 |
+
|
| 161 |
+
def gate_v1(axes: Any, weights: Any, tau: Any) -> Tuple[str, Optional[str]]:
|
| 162 |
+
"""(verdict, code) for Λ_w(axes) against tau. Never raises on bad input: it BLOCKs."""
|
| 163 |
+
try:
|
| 164 |
+
t = _check_tau(tau)
|
| 165 |
+
log_lam = log_lambda_v1(axes, weights)
|
| 166 |
+
except LambdaV1Error as err:
|
| 167 |
+
return BLOCK, err.code
|
| 168 |
+
if log_lam == -math.inf:
|
| 169 |
+
return NO_GO, ZERO_VETO
|
| 170 |
+
delta = log_lam - math.log(t)
|
| 171 |
+
if abs(delta) <= TIE_EPS:
|
| 172 |
+
return ABSTAIN, NUMERIC_TIE
|
| 173 |
+
if delta > 0:
|
| 174 |
+
return GO, None
|
| 175 |
+
return NO_GO, BELOW_TAU
|
| 176 |
+
|
| 177 |
+
|
| 178 |
+
# ------------------------------------------------ canonical numbers and bytes --
|
| 179 |
+
|
| 180 |
+
|
| 181 |
+
def encode_f64(x: float) -> str:
|
| 182 |
+
"""'f64:' + the 16 lowercase hex digits of the IEEE-754 binary64 bits (big-endian)."""
|
| 183 |
+
return "f64:" + struct.pack(">d", float(x)).hex()
|
| 184 |
+
|
| 185 |
+
|
| 186 |
+
def decode_f64(text: str) -> float:
|
| 187 |
+
"""Inverse of encode_f64. Rejects anything that is not exactly 'f64:' + 16 lowercase hex."""
|
| 188 |
+
if not isinstance(text, str) or len(text) != 20 or not text.startswith("f64:"):
|
| 189 |
+
raise ValueError(f"not an f64 literal: {text!r}")
|
| 190 |
+
digits = text[4:]
|
| 191 |
+
if any(c not in "0123456789abcdef" for c in digits):
|
| 192 |
+
raise ValueError(f"not an f64 literal: {text!r}")
|
| 193 |
+
return struct.unpack(">d", bytes.fromhex(digits))[0]
|
| 194 |
+
|
| 195 |
+
|
| 196 |
+
def canonical_json_bytes(obj: Any) -> bytes:
|
| 197 |
+
"""Canonical JSON: sorted keys, compact separators, UTF-8, no NaN/Infinity."""
|
| 198 |
+
return json.dumps(
|
| 199 |
+
obj, sort_keys=True, separators=(",", ":"), ensure_ascii=False, allow_nan=False
|
| 200 |
+
).encode("utf-8")
|
| 201 |
+
|
| 202 |
+
|
| 203 |
+
def canonical_sha256(obj: Any) -> str:
|
| 204 |
+
"""SHA-256 hex over canonical_json_bytes(obj): stable across CRLF/LF checkouts."""
|
| 205 |
+
return hashlib.sha256(canonical_json_bytes(obj)).hexdigest()
|
source/LICENSE
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
Apache License
|
| 3 |
+
Version 2.0, January 2004
|
| 4 |
+
http://www.apache.org/licenses/
|
| 5 |
+
|
| 6 |
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
| 7 |
+
|
| 8 |
+
1. Definitions.
|
| 9 |
+
|
| 10 |
+
"License" shall mean the terms and conditions for use, reproduction,
|
| 11 |
+
and distribution as defined by Sections 1 through 9 of this document.
|
| 12 |
+
|
| 13 |
+
"Licensor" shall mean the copyright owner or entity authorized by
|
| 14 |
+
the copyright owner that is granting the License.
|
| 15 |
+
|
| 16 |
+
"Legal Entity" shall mean the union of the acting entity and all
|
| 17 |
+
other entities that control, are controlled by, or are under common
|
| 18 |
+
control with that entity. For the purposes of this definition,
|
| 19 |
+
"control" means (i) the power, direct or indirect, to cause the
|
| 20 |
+
direction or management of such entity, whether by contract or
|
| 21 |
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
| 22 |
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
| 23 |
+
|
| 24 |
+
"You" (or "Your") shall mean an individual or Legal Entity
|
| 25 |
+
exercising permissions granted by this License.
|
| 26 |
+
|
| 27 |
+
"Source" form shall mean the preferred form for making modifications,
|
| 28 |
+
including but not limited to software source code, documentation
|
| 29 |
+
source, and configuration files.
|
| 30 |
+
|
| 31 |
+
"Object" form shall mean any form resulting from mechanical
|
| 32 |
+
transformation or translation of a Source form, including but
|
| 33 |
+
not limited to compiled object code, generated documentation,
|
| 34 |
+
and conversions to other media types.
|
| 35 |
+
|
| 36 |
+
"Work" shall mean the work of authorship, whether in Source or
|
| 37 |
+
Object form, made available under the License, as indicated by a
|
| 38 |
+
copyright notice that is included in or attached to the work
|
| 39 |
+
(an example is provided in the Appendix below).
|
| 40 |
+
|
| 41 |
+
"Derivative Works" shall mean any work, whether in Source or Object
|
| 42 |
+
form, that is based on (or derived from) the Work and for which the
|
| 43 |
+
editorial revisions, annotations, elaborations, or other modifications
|
| 44 |
+
represent, as a whole, an original work of authorship. For the purposes
|
| 45 |
+
of this License, Derivative Works shall not include works that remain
|
| 46 |
+
separable from, or merely link (or bind by name) to the interfaces of,
|
| 47 |
+
the Work and Derivative Works thereof.
|
| 48 |
+
|
| 49 |
+
"Contribution" shall mean any work of authorship, including
|
| 50 |
+
the original version of the Work and any modifications or additions
|
| 51 |
+
to that Work or Derivative Works thereof, that is intentionally
|
| 52 |
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
| 53 |
+
or by an individual or Legal Entity authorized to submit on behalf of
|
| 54 |
+
the copyright owner. For the purposes of this definition, "submitted"
|
| 55 |
+
means any form of electronic, verbal, or written communication sent
|
| 56 |
+
to the Licensor or its representatives, including but not limited to
|
| 57 |
+
communication on electronic mailing lists, source code control systems,
|
| 58 |
+
and issue tracking systems that are managed by, or on behalf of, the
|
| 59 |
+
Licensor for the purpose of discussing and improving the Work, but
|
| 60 |
+
excluding communication that is conspicuously marked or otherwise
|
| 61 |
+
designated in writing by the copyright owner as "Not a Contribution."
|
| 62 |
+
|
| 63 |
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
| 64 |
+
on behalf of whom a Contribution has been received by Licensor and
|
| 65 |
+
subsequently incorporated within the Work.
|
| 66 |
+
|
| 67 |
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
| 68 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 69 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 70 |
+
copyright license to reproduce, prepare Derivative Works of,
|
| 71 |
+
publicly display, publicly perform, sublicense, and distribute the
|
| 72 |
+
Work and such Derivative Works in Source or Object form.
|
| 73 |
+
|
| 74 |
+
3. Grant of Patent License. Subject to the terms and conditions of
|
| 75 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 76 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 77 |
+
(except as stated in this section) patent license to make, have made,
|
| 78 |
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
| 79 |
+
where such license applies only to those patent claims licensable
|
| 80 |
+
by such Contributor that are necessarily infringed by their
|
| 81 |
+
Contribution(s) alone or by combination of their Contribution(s)
|
| 82 |
+
with the Work to which such Contribution(s) was submitted. If You
|
| 83 |
+
institute patent litigation against any entity (including a
|
| 84 |
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
| 85 |
+
or a Contribution incorporated within the Work constitutes direct
|
| 86 |
+
or contributory patent infringement, then any patent licenses
|
| 87 |
+
granted to You under this License for that Work shall terminate
|
| 88 |
+
as of the date such litigation is filed.
|
| 89 |
+
|
| 90 |
+
4. Redistribution. You may reproduce and distribute copies of the
|
| 91 |
+
Work or Derivative Works thereof in any medium, with or without
|
| 92 |
+
modifications, and in Source or Object form, provided that You
|
| 93 |
+
meet the following conditions:
|
| 94 |
+
|
| 95 |
+
(a) You must give any other recipients of the Work or
|
| 96 |
+
Derivative Works a copy of this License; and
|
| 97 |
+
|
| 98 |
+
(b) You must cause any modified files to carry prominent notices
|
| 99 |
+
stating that You changed the files; and
|
| 100 |
+
|
| 101 |
+
(c) You must retain, in the Source form of any Derivative Works
|
| 102 |
+
that You distribute, all copyright, patent, trademark, and
|
| 103 |
+
attribution notices from the Source form of the Work,
|
| 104 |
+
excluding those notices that do not pertain to any part of
|
| 105 |
+
the Derivative Works; and
|
| 106 |
+
|
| 107 |
+
(d) If the Work includes a "NOTICE" text file as part of its
|
| 108 |
+
distribution, then any Derivative Works that You distribute must
|
| 109 |
+
include a readable copy of the attribution notices contained
|
| 110 |
+
within such NOTICE file, excluding those notices that do not
|
| 111 |
+
pertain to any part of the Derivative Works, in at least one
|
| 112 |
+
of the following places: within a NOTICE text file distributed
|
| 113 |
+
as part of the Derivative Works; within the Source form or
|
| 114 |
+
documentation, if provided along with the Derivative Works; or,
|
| 115 |
+
within a display generated by the Derivative Works, if and
|
| 116 |
+
wherever such third-party notices normally appear. The contents
|
| 117 |
+
of the NOTICE file are for informational purposes only and
|
| 118 |
+
do not modify the License. You may add Your own attribution
|
| 119 |
+
notices within Derivative Works that You distribute, alongside
|
| 120 |
+
or as an addendum to the NOTICE text from the Work, provided
|
| 121 |
+
that such additional attribution notices cannot be construed
|
| 122 |
+
as modifying the License.
|
| 123 |
+
|
| 124 |
+
You may add Your own copyright statement to Your modifications and
|
| 125 |
+
may provide additional or different license terms and conditions
|
| 126 |
+
for use, reproduction, or distribution of Your modifications, or
|
| 127 |
+
for any such Derivative Works as a whole, provided Your use,
|
| 128 |
+
reproduction, and distribution of the Work otherwise complies with
|
| 129 |
+
the conditions stated in this License.
|
| 130 |
+
|
| 131 |
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
| 132 |
+
any Contribution intentionally submitted for inclusion in the Work
|
| 133 |
+
by You to the Licensor shall be under the terms and conditions of
|
| 134 |
+
this License, without any additional terms or conditions.
|
| 135 |
+
Notwithstanding the above, nothing herein shall supersede or modify
|
| 136 |
+
the terms of any separate license agreement you may have executed
|
| 137 |
+
with Licensor regarding such Contributions.
|
| 138 |
+
|
| 139 |
+
6. Trademarks. This License does not grant permission to use the trade
|
| 140 |
+
names, trademarks, service marks, or product names of the Licensor,
|
| 141 |
+
except as required for reasonable and customary use in describing the
|
| 142 |
+
origin of the Work and reproducing the content of the NOTICE file.
|
| 143 |
+
|
| 144 |
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
| 145 |
+
agreed to in writing, Licensor provides the Work (and each
|
| 146 |
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
| 147 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
| 148 |
+
implied, including, without limitation, any warranties or conditions
|
| 149 |
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
| 150 |
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
| 151 |
+
appropriateness of using or redistributing the Work and assume any
|
| 152 |
+
risks associated with Your exercise of permissions under this License.
|
| 153 |
+
|
| 154 |
+
8. Limitation of Liability. In no event and under no legal theory,
|
| 155 |
+
whether in tort (including negligence), contract, or otherwise,
|
| 156 |
+
unless required by applicable law (such as deliberate and grossly
|
| 157 |
+
negligent acts) or agreed to in writing, shall any Contributor be
|
| 158 |
+
liable to You for damages, including any direct, indirect, special,
|
| 159 |
+
incidental, or consequential damages of any character arising as a
|
| 160 |
+
result of this License or out of the use or inability to use the
|
| 161 |
+
Work (including but not limited to damages for loss of goodwill,
|
| 162 |
+
work stoppage, computer failure or malfunction, or any and all
|
| 163 |
+
other commercial damages or losses), even if such Contributor
|
| 164 |
+
has been advised of the possibility of such damages.
|
| 165 |
+
|
| 166 |
+
9. Accepting Warranty or Additional Liability. While redistributing
|
| 167 |
+
the Work or Derivative Works thereof, You may choose to offer,
|
| 168 |
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
| 169 |
+
or other liability obligations and/or rights consistent with this
|
| 170 |
+
License. However, in accepting such obligations, You may act only
|
| 171 |
+
on Your own behalf and on Your sole responsibility, not on behalf
|
| 172 |
+
of any other Contributor, and only if You agree to indemnify,
|
| 173 |
+
defend, and hold each Contributor harmless for any liability
|
| 174 |
+
incurred by, or claims asserted against, such Contributor by reason
|
| 175 |
+
of your accepting any such warranty or additional liability.
|
| 176 |
+
|
| 177 |
+
END OF TERMS AND CONDITIONS
|
| 178 |
+
|
| 179 |
+
APPENDIX: How to apply the Apache License to your work.
|
| 180 |
+
|
| 181 |
+
To apply the Apache License to your work, attach the following
|
| 182 |
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
| 183 |
+
replaced with your own identifying information. (Don't include
|
| 184 |
+
the brackets!) The text should be enclosed in the appropriate
|
| 185 |
+
comment syntax for the file format. We also recommend that a
|
| 186 |
+
file or class name and description of purpose be included on the
|
| 187 |
+
same "printed page" as the copyright notice for easier
|
| 188 |
+
identification within third-party archives.
|
| 189 |
+
|
| 190 |
+
Copyright 2026 SZL Holdings
|
| 191 |
+
|
| 192 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
| 193 |
+
you may not use this file except in compliance with the License.
|
| 194 |
+
You may obtain a copy of the License at
|
| 195 |
+
|
| 196 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
| 197 |
+
|
| 198 |
+
Unless required by applicable law or agreed to in writing, software
|
| 199 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
| 200 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 201 |
+
See the License for the specific language governing permissions and
|
| 202 |
+
limitations under the License.
|
spec/lambda_v1_vectors.json
ADDED
|
@@ -0,0 +1,730 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"schema": "szl.lambda/v1.vectors",
|
| 3 |
+
"spec": "spec/szl.lambda.v1.json",
|
| 4 |
+
"float_encoding": "Every float is \"f64:<16 lowercase hex digits of the IEEE-754 binary64 bits, big-endian>\"; the *_decimal fields are Python repr() for humans. A JSON value that is not an f64 string (true, null, another string, an integer) is passed to the implementation unchanged.",
|
| 5 |
+
"expect": "value_f64 or error describes lambda_v1(axes, weights); verdict and code describe gate_v1(axes, weights, tau).",
|
| 6 |
+
"value_tol": "Absolute tolerance on Λ for implementations other than reference/szl_lambda_v1.py, which must match value_f64 bit for bit. Error codes and verdicts are always exact.",
|
| 7 |
+
"vectors": [
|
| 8 |
+
{
|
| 9 |
+
"id": "nominal",
|
| 10 |
+
"source": "szl-math-core.md §3 differential table (nominal)",
|
| 11 |
+
"axes": ["f64:3fee666666666666", "f64:3fed70a3d70a3d71", "f64:3fec28f5c28f5c29", "f64:3feccccccccccccd"],
|
| 12 |
+
"axes_decimal": ["0.95", "0.92", "0.88", "0.9"],
|
| 13 |
+
"weights": ["f64:3fd0000000000000", "f64:3fd0000000000000", "f64:3fd0000000000000", "f64:3fd0000000000000"],
|
| 14 |
+
"weights_decimal": ["0.25", "0.25", "0.25", "0.25"],
|
| 15 |
+
"tau": "f64:3fe999999999999a",
|
| 16 |
+
"tau_decimal": "0.8",
|
| 17 |
+
"value_tol": 1e-12,
|
| 18 |
+
"expect": {"value_f64": "f64:3fed3035e27f23fe", "value_decimal": "0.9121350692522581", "verdict": "GO", "code": null}
|
| 19 |
+
},
|
| 20 |
+
{
|
| 21 |
+
"id": "hidden_weak",
|
| 22 |
+
"source": "szl-math-core.md §3 differential table (hidden_weak); admit contract hidden_weak_lock gm 0.7653",
|
| 23 |
+
"note": "the arithmetic mean 0.8125 would pass tau 0.8; the geometric mean does not",
|
| 24 |
+
"axes": ["f64:3fee666666666666", "f64:3fee666666666666", "f64:3fee666666666666", "f64:3fd999999999999a"],
|
| 25 |
+
"axes_decimal": ["0.95", "0.95", "0.95", "0.4"],
|
| 26 |
+
"weights": ["f64:3fd0000000000000", "f64:3fd0000000000000", "f64:3fd0000000000000", "f64:3fd0000000000000"],
|
| 27 |
+
"weights_decimal": ["0.25", "0.25", "0.25", "0.25"],
|
| 28 |
+
"tau": "f64:3fe999999999999a",
|
| 29 |
+
"tau_decimal": "0.8",
|
| 30 |
+
"value_tol": 1e-12,
|
| 31 |
+
"expect": {"value_f64": "f64:3fe87cfdb14e60f2", "value_decimal": "0.7652576888094968", "verdict": "NO_GO", "code": "BELOW_TAU"}
|
| 32 |
+
},
|
| 33 |
+
{
|
| 34 |
+
"id": "hidden_weak_permuted",
|
| 35 |
+
"source": "szl.lambda/v1 domain: a permutation of hidden_weak gives identical bits (fsum)",
|
| 36 |
+
"axes": ["f64:3fd999999999999a", "f64:3fee666666666666", "f64:3fee666666666666", "f64:3fee666666666666"],
|
| 37 |
+
"axes_decimal": ["0.4", "0.95", "0.95", "0.95"],
|
| 38 |
+
"weights": ["f64:3fd0000000000000", "f64:3fd0000000000000", "f64:3fd0000000000000", "f64:3fd0000000000000"],
|
| 39 |
+
"weights_decimal": ["0.25", "0.25", "0.25", "0.25"],
|
| 40 |
+
"tau": "f64:3fe999999999999a",
|
| 41 |
+
"tau_decimal": "0.8",
|
| 42 |
+
"value_tol": 1e-12,
|
| 43 |
+
"expect": {"value_f64": "f64:3fe87cfdb14e60f2", "value_decimal": "0.7652576888094968", "verdict": "NO_GO", "code": "BELOW_TAU"}
|
| 44 |
+
},
|
| 45 |
+
{
|
| 46 |
+
"id": "x_gt_1",
|
| 47 |
+
"source": "szl-math-core.md §3 differential table (x>1 [1.5, .9])",
|
| 48 |
+
"note": "torch and the legacy Python source clamp to 0.948683; szl-formulas and szl-khipu return 1.1619",
|
| 49 |
+
"axes": ["f64:3ff8000000000000", "f64:3feccccccccccccd"],
|
| 50 |
+
"axes_decimal": ["1.5", "0.9"],
|
| 51 |
+
"weights": ["f64:3fe0000000000000", "f64:3fe0000000000000"],
|
| 52 |
+
"weights_decimal": ["0.5", "0.5"],
|
| 53 |
+
"tau": "f64:3fe999999999999a",
|
| 54 |
+
"tau_decimal": "0.8",
|
| 55 |
+
"value_tol": 1e-12,
|
| 56 |
+
"expect": {"error": "LAMBDA_AXIS_OUT_OF_RANGE", "verdict": "BLOCK", "code": "LAMBDA_AXIS_OUT_OF_RANGE"}
|
| 57 |
+
},
|
| 58 |
+
{
|
| 59 |
+
"id": "nan_axis",
|
| 60 |
+
"source": "szl-math-core.md §3 differential table (NaN axis)",
|
| 61 |
+
"axes": ["f64:7ff8000000000000", "f64:3feccccccccccccd"],
|
| 62 |
+
"axes_decimal": ["nan", "0.9"],
|
| 63 |
+
"weights": ["f64:3fe0000000000000", "f64:3fe0000000000000"],
|
| 64 |
+
"weights_decimal": ["0.5", "0.5"],
|
| 65 |
+
"tau": "f64:3fe999999999999a",
|
| 66 |
+
"tau_decimal": "0.8",
|
| 67 |
+
"value_tol": 1e-12,
|
| 68 |
+
"expect": {"error": "LAMBDA_NONFINITE_AXIS", "verdict": "BLOCK", "code": "LAMBDA_NONFINITE_AXIS"}
|
| 69 |
+
},
|
| 70 |
+
{
|
| 71 |
+
"id": "pos_inf_axis",
|
| 72 |
+
"source": "szl-math-core.md §3 differential table (+Inf axis)",
|
| 73 |
+
"note": "the legacy Python source treats +Inf as 1 (0.948683)",
|
| 74 |
+
"axes": ["f64:7ff0000000000000", "f64:3feccccccccccccd"],
|
| 75 |
+
"axes_decimal": ["inf", "0.9"],
|
| 76 |
+
"weights": ["f64:3fe0000000000000", "f64:3fe0000000000000"],
|
| 77 |
+
"weights_decimal": ["0.5", "0.5"],
|
| 78 |
+
"tau": "f64:3fe999999999999a",
|
| 79 |
+
"tau_decimal": "0.8",
|
| 80 |
+
"value_tol": 1e-12,
|
| 81 |
+
"expect": {"error": "LAMBDA_NONFINITE_AXIS", "verdict": "BLOCK", "code": "LAMBDA_NONFINITE_AXIS"}
|
| 82 |
+
},
|
| 83 |
+
{
|
| 84 |
+
"id": "neg_inf_axis",
|
| 85 |
+
"source": "szl-math-core.md §3 differential table (-Inf axis)",
|
| 86 |
+
"axes": ["f64:fff0000000000000", "f64:3feccccccccccccd"],
|
| 87 |
+
"axes_decimal": ["-inf", "0.9"],
|
| 88 |
+
"weights": ["f64:3fe0000000000000", "f64:3fe0000000000000"],
|
| 89 |
+
"weights_decimal": ["0.5", "0.5"],
|
| 90 |
+
"tau": "f64:3fe999999999999a",
|
| 91 |
+
"tau_decimal": "0.8",
|
| 92 |
+
"value_tol": 1e-12,
|
| 93 |
+
"expect": {"error": "LAMBDA_NONFINITE_AXIS", "verdict": "BLOCK", "code": "LAMBDA_NONFINITE_AXIS"}
|
| 94 |
+
},
|
| 95 |
+
{
|
| 96 |
+
"id": "negative_axis",
|
| 97 |
+
"source": "szl-math-core.md §3 differential table (negative axis)",
|
| 98 |
+
"axes": ["f64:bfb999999999999a", "f64:3feccccccccccccd"],
|
| 99 |
+
"axes_decimal": ["-0.1", "0.9"],
|
| 100 |
+
"weights": ["f64:3fe0000000000000", "f64:3fe0000000000000"],
|
| 101 |
+
"weights_decimal": ["0.5", "0.5"],
|
| 102 |
+
"tau": "f64:3fe999999999999a",
|
| 103 |
+
"tau_decimal": "0.8",
|
| 104 |
+
"value_tol": 1e-12,
|
| 105 |
+
"expect": {"error": "LAMBDA_AXIS_OUT_OF_RANGE", "verdict": "BLOCK", "code": "LAMBDA_AXIS_OUT_OF_RANGE"}
|
| 106 |
+
},
|
| 107 |
+
{
|
| 108 |
+
"id": "zero_axis",
|
| 109 |
+
"source": "szl-math-core.md §3 differential table (zero axis)",
|
| 110 |
+
"note": "a veto, not an error: log Λ = -inf gives NO_GO",
|
| 111 |
+
"axes": ["f64:0000000000000000", "f64:3feccccccccccccd"],
|
| 112 |
+
"axes_decimal": ["0.0", "0.9"],
|
| 113 |
+
"weights": ["f64:3fe0000000000000", "f64:3fe0000000000000"],
|
| 114 |
+
"weights_decimal": ["0.5", "0.5"],
|
| 115 |
+
"tau": "f64:3fe999999999999a",
|
| 116 |
+
"tau_decimal": "0.8",
|
| 117 |
+
"value_tol": 1e-12,
|
| 118 |
+
"expect": {"value_f64": "f64:0000000000000000", "value_decimal": "0.0", "verdict": "NO_GO", "code": "ZERO_VETO"}
|
| 119 |
+
},
|
| 120 |
+
{
|
| 121 |
+
"id": "negative_zero_axis",
|
| 122 |
+
"source": "szl.lambda/v1 domain: -0.0 is a zero axis",
|
| 123 |
+
"axes": ["f64:8000000000000000", "f64:3feccccccccccccd"],
|
| 124 |
+
"axes_decimal": ["-0.0", "0.9"],
|
| 125 |
+
"weights": ["f64:3fe0000000000000", "f64:3fe0000000000000"],
|
| 126 |
+
"weights_decimal": ["0.5", "0.5"],
|
| 127 |
+
"tau": "f64:3fe999999999999a",
|
| 128 |
+
"tau_decimal": "0.8",
|
| 129 |
+
"value_tol": 1e-12,
|
| 130 |
+
"expect": {"value_f64": "f64:0000000000000000", "value_decimal": "0.0", "verdict": "NO_GO", "code": "ZERO_VETO"}
|
| 131 |
+
},
|
| 132 |
+
{
|
| 133 |
+
"id": "zero_axis_min_subnormal_tau",
|
| 134 |
+
"source": "szl.lambda/v1 domain: the zero-axis veto holds for every valid tau",
|
| 135 |
+
"axes": ["f64:0000000000000000", "f64:3ff0000000000000"],
|
| 136 |
+
"axes_decimal": ["0.0", "1.0"],
|
| 137 |
+
"weights": ["f64:3fe0000000000000", "f64:3fe0000000000000"],
|
| 138 |
+
"weights_decimal": ["0.5", "0.5"],
|
| 139 |
+
"tau": "f64:0000000000000001",
|
| 140 |
+
"tau_decimal": "5e-324",
|
| 141 |
+
"value_tol": 1e-12,
|
| 142 |
+
"expect": {"value_f64": "f64:0000000000000000", "value_decimal": "0.0", "verdict": "NO_GO", "code": "ZERO_VETO"}
|
| 143 |
+
},
|
| 144 |
+
{
|
| 145 |
+
"id": "w_unnormalised_2_2",
|
| 146 |
+
"source": "szl-math-core.md §3 differential table (unnormalised w=[2,2] on [.81,.64])",
|
| 147 |
+
"note": "never renormalised (torch returns 0.72)",
|
| 148 |
+
"axes": ["f64:3fe9eb851eb851ec", "f64:3fe47ae147ae147b"],
|
| 149 |
+
"axes_decimal": ["0.81", "0.64"],
|
| 150 |
+
"weights": ["f64:4000000000000000", "f64:4000000000000000"],
|
| 151 |
+
"weights_decimal": ["2.0", "2.0"],
|
| 152 |
+
"tau": "f64:3fe999999999999a",
|
| 153 |
+
"tau_decimal": "0.8",
|
| 154 |
+
"value_tol": 1e-12,
|
| 155 |
+
"expect": {"error": "LAMBDA_WEIGHT_SUM", "verdict": "BLOCK", "code": "LAMBDA_WEIGHT_SUM"}
|
| 156 |
+
},
|
| 157 |
+
{
|
| 158 |
+
"id": "w_zero_weight",
|
| 159 |
+
"source": "szl-math-core.md §3 differential table (zero weight w=[1,0] on [.5,.9])",
|
| 160 |
+
"axes": ["f64:3fe0000000000000", "f64:3feccccccccccccd"],
|
| 161 |
+
"axes_decimal": ["0.5", "0.9"],
|
| 162 |
+
"weights": ["f64:3ff0000000000000", "f64:0000000000000000"],
|
| 163 |
+
"weights_decimal": ["1.0", "0.0"],
|
| 164 |
+
"tau": "f64:3fe999999999999a",
|
| 165 |
+
"tau_decimal": "0.8",
|
| 166 |
+
"value_tol": 1e-12,
|
| 167 |
+
"expect": {"error": "LAMBDA_WEIGHT_NONPOSITIVE", "verdict": "BLOCK", "code": "LAMBDA_WEIGHT_NONPOSITIVE"}
|
| 168 |
+
},
|
| 169 |
+
{
|
| 170 |
+
"id": "empty",
|
| 171 |
+
"source": "szl-math-core.md §3 differential table (empty)",
|
| 172 |
+
"note": "never 0.0",
|
| 173 |
+
"axes": [],
|
| 174 |
+
"axes_decimal": [],
|
| 175 |
+
"weights": [],
|
| 176 |
+
"weights_decimal": [],
|
| 177 |
+
"tau": "f64:3fe999999999999a",
|
| 178 |
+
"tau_decimal": "0.8",
|
| 179 |
+
"value_tol": 1e-12,
|
| 180 |
+
"expect": {"error": "LAMBDA_EMPTY", "verdict": "BLOCK", "code": "LAMBDA_EMPTY"}
|
| 181 |
+
},
|
| 182 |
+
{
|
| 183 |
+
"id": "empty_axes_nonempty_weights",
|
| 184 |
+
"source": "szl.lambda/v1 domain: empty axes",
|
| 185 |
+
"axes": [],
|
| 186 |
+
"axes_decimal": [],
|
| 187 |
+
"weights": ["f64:3ff0000000000000"],
|
| 188 |
+
"weights_decimal": ["1.0"],
|
| 189 |
+
"tau": "f64:3fe999999999999a",
|
| 190 |
+
"tau_decimal": "0.8",
|
| 191 |
+
"value_tol": 1e-12,
|
| 192 |
+
"expect": {"error": "LAMBDA_EMPTY", "verdict": "BLOCK", "code": "LAMBDA_EMPTY"}
|
| 193 |
+
},
|
| 194 |
+
{
|
| 195 |
+
"id": "e5_round_4dp_not_a_pass",
|
| 196 |
+
"source": "FUSION_BRIEF.md E5 (4-dp rounding before the compare)",
|
| 197 |
+
"note": "round(Λ, 4) = 0.65 would pass tau 0.65; the unrounded Λ is below it",
|
| 198 |
+
"axes": ["f64:3fe4cc660a201472"],
|
| 199 |
+
"axes_decimal": ["0.649951"],
|
| 200 |
+
"weights": ["f64:3ff0000000000000"],
|
| 201 |
+
"weights_decimal": ["1.0"],
|
| 202 |
+
"tau": "f64:3fe4cccccccccccd",
|
| 203 |
+
"tau_decimal": "0.65",
|
| 204 |
+
"value_tol": 1e-12,
|
| 205 |
+
"expect": {"value_f64": "f64:3fe4cc660a201472", "value_decimal": "0.649951", "verdict": "NO_GO", "code": "BELOW_TAU"}
|
| 206 |
+
},
|
| 207 |
+
{
|
| 208 |
+
"id": "e5_dropped_axis_present",
|
| 209 |
+
"source": "FUSION_BRIEF.md E5 (triage weights, integrity = 0.1 present)",
|
| 210 |
+
"note": "weights from szl-typesafe-triage src/szl_triage/data/triage_policy.v3.json",
|
| 211 |
+
"axes": ["f64:3feb26e978d4fdf4", "f64:3feb26e978d4fdf4", "f64:3fb999999999999a", "f64:3feb26e978d4fdf4"],
|
| 212 |
+
"axes_decimal": ["0.8485", "0.8485", "0.1", "0.8485"],
|
| 213 |
+
"weights": ["f64:3fd0000000000000", "f64:3fd999999999999a", "f64:3fc999999999999a", "f64:3fc3333333333333"],
|
| 214 |
+
"weights_decimal": ["0.25", "0.4", "0.2", "0.15"],
|
| 215 |
+
"tau": "f64:3fe999999999999a",
|
| 216 |
+
"tau_decimal": "0.8",
|
| 217 |
+
"value_tol": 1e-12,
|
| 218 |
+
"expect": {"value_f64": "f64:3fe1b4397d7da1fc", "value_decimal": "0.5532500697127598", "verdict": "NO_GO", "code": "BELOW_TAU"}
|
| 219 |
+
},
|
| 220 |
+
{
|
| 221 |
+
"id": "e5_dropped_axis_renormalised",
|
| 222 |
+
"source": "FUSION_BRIEF.md E5 (integrity axis dropped)",
|
| 223 |
+
"note": "renormalising over the present axes would give 0.8485",
|
| 224 |
+
"axes": ["f64:3feb26e978d4fdf4", "f64:3feb26e978d4fdf4", "f64:3feb26e978d4fdf4"],
|
| 225 |
+
"axes_decimal": ["0.8485", "0.8485", "0.8485"],
|
| 226 |
+
"weights": ["f64:3fd0000000000000", "f64:3fd999999999999a", "f64:3fc999999999999a", "f64:3fc3333333333333"],
|
| 227 |
+
"weights_decimal": ["0.25", "0.4", "0.2", "0.15"],
|
| 228 |
+
"tau": "f64:3fe999999999999a",
|
| 229 |
+
"tau_decimal": "0.8",
|
| 230 |
+
"value_tol": 1e-12,
|
| 231 |
+
"expect": {"error": "LAMBDA_LENGTH_MISMATCH", "verdict": "BLOCK", "code": "LAMBDA_LENGTH_MISMATCH"}
|
| 232 |
+
},
|
| 233 |
+
{
|
| 234 |
+
"id": "e5_negative_weight",
|
| 235 |
+
"source": "FUSION_BRIEF.md E5 (w = (1.5, -0.5) breaks monotonicity)",
|
| 236 |
+
"note": "the weights sum to 1; szl-formulas accepts them and returns 0.3727",
|
| 237 |
+
"axes": ["f64:3fe0000000000000", "f64:3feccccccccccccd"],
|
| 238 |
+
"axes_decimal": ["0.5", "0.9"],
|
| 239 |
+
"weights": ["f64:3ff8000000000000", "f64:bfe0000000000000"],
|
| 240 |
+
"weights_decimal": ["1.5", "-0.5"],
|
| 241 |
+
"tau": "f64:3fe999999999999a",
|
| 242 |
+
"tau_decimal": "0.8",
|
| 243 |
+
"value_tol": 1e-12,
|
| 244 |
+
"expect": {"error": "LAMBDA_WEIGHT_NONPOSITIVE", "verdict": "BLOCK", "code": "LAMBDA_WEIGHT_NONPOSITIVE"}
|
| 245 |
+
},
|
| 246 |
+
{
|
| 247 |
+
"id": "length_mismatch_extra_weight",
|
| 248 |
+
"source": "szl.lambda/v1 domain: more weights than axes",
|
| 249 |
+
"axes": ["f64:3feccccccccccccd"],
|
| 250 |
+
"axes_decimal": ["0.9"],
|
| 251 |
+
"weights": ["f64:3fe0000000000000", "f64:3fe0000000000000"],
|
| 252 |
+
"weights_decimal": ["0.5", "0.5"],
|
| 253 |
+
"tau": "f64:3fe999999999999a",
|
| 254 |
+
"tau_decimal": "0.8",
|
| 255 |
+
"value_tol": 1e-12,
|
| 256 |
+
"expect": {"error": "LAMBDA_LENGTH_MISMATCH", "verdict": "BLOCK", "code": "LAMBDA_LENGTH_MISMATCH"}
|
| 257 |
+
},
|
| 258 |
+
{
|
| 259 |
+
"id": "tie_exact",
|
| 260 |
+
"source": "INVENTION.md §2.3 tie band (TIE_EPS = 1e-9, log space)",
|
| 261 |
+
"axes": ["f64:3fe999999999999a"],
|
| 262 |
+
"axes_decimal": ["0.8"],
|
| 263 |
+
"weights": ["f64:3ff0000000000000"],
|
| 264 |
+
"weights_decimal": ["1.0"],
|
| 265 |
+
"tau": "f64:3fe999999999999a",
|
| 266 |
+
"tau_decimal": "0.8",
|
| 267 |
+
"value_tol": 1e-12,
|
| 268 |
+
"expect": {"value_f64": "f64:3fe999999999999a", "value_decimal": "0.8", "verdict": "ABSTAIN", "code": "NUMERIC_TIE"}
|
| 269 |
+
},
|
| 270 |
+
{
|
| 271 |
+
"id": "tie_inside_above",
|
| 272 |
+
"source": "INVENTION.md §2.3 tie band (TIE_EPS = 1e-9, log space): log Λ - log tau ≈ +5e-10",
|
| 273 |
+
"axes": ["f64:3fe9999999d0935a"],
|
| 274 |
+
"axes_decimal": ["0.8000000004000001"],
|
| 275 |
+
"weights": ["f64:3ff0000000000000"],
|
| 276 |
+
"weights_decimal": ["1.0"],
|
| 277 |
+
"tau": "f64:3fe999999999999a",
|
| 278 |
+
"tau_decimal": "0.8",
|
| 279 |
+
"value_tol": 1e-12,
|
| 280 |
+
"expect": {"value_f64": "f64:3fe9999999d0935a", "value_decimal": "0.8000000004000001", "verdict": "ABSTAIN", "code": "NUMERIC_TIE"}
|
| 281 |
+
},
|
| 282 |
+
{
|
| 283 |
+
"id": "tie_inside_below",
|
| 284 |
+
"source": "INVENTION.md §2.3 tie band (TIE_EPS = 1e-9, log space): log Λ - log tau ≈ -5e-10",
|
| 285 |
+
"axes": ["f64:3fe9999999629fda"],
|
| 286 |
+
"axes_decimal": ["0.7999999996"],
|
| 287 |
+
"weights": ["f64:3ff0000000000000"],
|
| 288 |
+
"weights_decimal": ["1.0"],
|
| 289 |
+
"tau": "f64:3fe999999999999a",
|
| 290 |
+
"tau_decimal": "0.8",
|
| 291 |
+
"value_tol": 1e-12,
|
| 292 |
+
"expect": {"value_f64": "f64:3fe9999999629fda", "value_decimal": "0.7999999996", "verdict": "ABSTAIN", "code": "NUMERIC_TIE"}
|
| 293 |
+
},
|
| 294 |
+
{
|
| 295 |
+
"id": "tie_outside_above",
|
| 296 |
+
"source": "INVENTION.md §2.3 tie band (TIE_EPS = 1e-9, log space): log Λ - log tau ≈ +2e-9",
|
| 297 |
+
"axes": ["f64:3fe999999a758098"],
|
| 298 |
+
"axes_decimal": ["0.8000000016"],
|
| 299 |
+
"weights": ["f64:3ff0000000000000"],
|
| 300 |
+
"weights_decimal": ["1.0"],
|
| 301 |
+
"tau": "f64:3fe999999999999a",
|
| 302 |
+
"tau_decimal": "0.8",
|
| 303 |
+
"value_tol": 1e-12,
|
| 304 |
+
"expect": {"value_f64": "f64:3fe999999a758098", "value_decimal": "0.8000000016", "verdict": "GO", "code": null}
|
| 305 |
+
},
|
| 306 |
+
{
|
| 307 |
+
"id": "tie_outside_below",
|
| 308 |
+
"source": "INVENTION.md §2.3 tie band (TIE_EPS = 1e-9, log space): log Λ - log tau ≈ -2e-9",
|
| 309 |
+
"axes": ["f64:3fe9999998bdb29b"],
|
| 310 |
+
"axes_decimal": ["0.7999999984"],
|
| 311 |
+
"weights": ["f64:3ff0000000000000"],
|
| 312 |
+
"weights_decimal": ["1.0"],
|
| 313 |
+
"tau": "f64:3fe999999999999a",
|
| 314 |
+
"tau_decimal": "0.8",
|
| 315 |
+
"value_tol": 1e-12,
|
| 316 |
+
"expect": {"value_f64": "f64:3fe9999998bdb29b", "value_decimal": "0.7999999984", "verdict": "NO_GO", "code": "BELOW_TAU"}
|
| 317 |
+
},
|
| 318 |
+
{
|
| 319 |
+
"id": "tie_multi_axis_at_own_value",
|
| 320 |
+
"source": "INVENTION.md §2.3 tie band (TIE_EPS = 1e-9, log space): hidden_weak against tau = its own Λ",
|
| 321 |
+
"axes": ["f64:3fee666666666666", "f64:3fee666666666666", "f64:3fee666666666666", "f64:3fd999999999999a"],
|
| 322 |
+
"axes_decimal": ["0.95", "0.95", "0.95", "0.4"],
|
| 323 |
+
"weights": ["f64:3fd0000000000000", "f64:3fd0000000000000", "f64:3fd0000000000000", "f64:3fd0000000000000"],
|
| 324 |
+
"weights_decimal": ["0.25", "0.25", "0.25", "0.25"],
|
| 325 |
+
"tau": "f64:3fe87cfdb14e60f2",
|
| 326 |
+
"tau_decimal": "0.7652576888094968",
|
| 327 |
+
"value_tol": 1e-12,
|
| 328 |
+
"expect": {"value_f64": "f64:3fe87cfdb14e60f2", "value_decimal": "0.7652576888094968", "verdict": "ABSTAIN", "code": "NUMERIC_TIE"}
|
| 329 |
+
},
|
| 330 |
+
{
|
| 331 |
+
"id": "all_ones_thirds",
|
| 332 |
+
"source": "szl.lambda/v1 domain: weights 1/3 each (fsum within 1e-12 of 1)",
|
| 333 |
+
"axes": ["f64:3ff0000000000000", "f64:3ff0000000000000", "f64:3ff0000000000000"],
|
| 334 |
+
"axes_decimal": ["1.0", "1.0", "1.0"],
|
| 335 |
+
"weights": ["f64:3fd5555555555555", "f64:3fd5555555555555", "f64:3fd5555555555555"],
|
| 336 |
+
"weights_decimal": ["0.3333333333333333", "0.3333333333333333", "0.3333333333333333"],
|
| 337 |
+
"tau": "f64:3fe999999999999a",
|
| 338 |
+
"tau_decimal": "0.8",
|
| 339 |
+
"value_tol": 1e-12,
|
| 340 |
+
"expect": {"value_f64": "f64:3ff0000000000000", "value_decimal": "1.0", "verdict": "GO", "code": null}
|
| 341 |
+
},
|
| 342 |
+
{
|
| 343 |
+
"id": "int_axes_accepted",
|
| 344 |
+
"source": "szl.lambda/v1 type contract: integers are real numbers",
|
| 345 |
+
"axes": [1, 1],
|
| 346 |
+
"weights": ["f64:3fe0000000000000", "f64:3fe0000000000000"],
|
| 347 |
+
"weights_decimal": ["0.5", "0.5"],
|
| 348 |
+
"tau": "f64:3fe999999999999a",
|
| 349 |
+
"tau_decimal": "0.8",
|
| 350 |
+
"value_tol": 1e-12,
|
| 351 |
+
"expect": {"value_f64": "f64:3ff0000000000000", "value_decimal": "1.0", "verdict": "GO", "code": null}
|
| 352 |
+
},
|
| 353 |
+
{
|
| 354 |
+
"id": "subnormal_axis",
|
| 355 |
+
"source": "szl.lambda/v1 domain: the smallest positive axis is not a zero axis",
|
| 356 |
+
"axes": ["f64:0000000000000001", "f64:3ff0000000000000"],
|
| 357 |
+
"axes_decimal": ["5e-324", "1.0"],
|
| 358 |
+
"weights": ["f64:3fe0000000000000", "f64:3fe0000000000000"],
|
| 359 |
+
"weights_decimal": ["0.5", "0.5"],
|
| 360 |
+
"tau": "f64:3fe999999999999a",
|
| 361 |
+
"tau_decimal": "0.8",
|
| 362 |
+
"value_tol": 1e-12,
|
| 363 |
+
"expect": {"value_f64": "f64:1e60000000000064", "value_decimal": "2.222758749485127e-162", "verdict": "NO_GO", "code": "BELOW_TAU"}
|
| 364 |
+
},
|
| 365 |
+
{
|
| 366 |
+
"id": "weight_sum_within_tol",
|
| 367 |
+
"source": "szl.lambda/v1 domain: |fsum(w) - 1| = 4e-13 <= 1e-12",
|
| 368 |
+
"axes": ["f64:3feccccccccccccd", "f64:3fe0000000000000"],
|
| 369 |
+
"axes_decimal": ["0.9", "0.5"],
|
| 370 |
+
"weights": ["f64:3fe0000000000e13", "f64:3fe0000000000000"],
|
| 371 |
+
"weights_decimal": ["0.5000000000004", "0.5"],
|
| 372 |
+
"tau": "f64:3fe999999999999a",
|
| 373 |
+
"tau_decimal": "0.8",
|
| 374 |
+
"value_tol": 1e-12,
|
| 375 |
+
"expect": {"value_f64": "f64:3fe5775c544ff164", "value_decimal": "0.6708203932499086", "verdict": "NO_GO", "code": "BELOW_TAU"}
|
| 376 |
+
},
|
| 377 |
+
{
|
| 378 |
+
"id": "weight_sum_outside_tol",
|
| 379 |
+
"source": "szl.lambda/v1 domain: |fsum(w) - 1| = 3e-12 > 1e-12",
|
| 380 |
+
"axes": ["f64:3feccccccccccccd", "f64:3fe0000000000000"],
|
| 381 |
+
"axes_decimal": ["0.9", "0.5"],
|
| 382 |
+
"weights": ["f64:3fe000000000698e", "f64:3fe0000000000000"],
|
| 383 |
+
"weights_decimal": ["0.500000000003", "0.5"],
|
| 384 |
+
"tau": "f64:3fe999999999999a",
|
| 385 |
+
"tau_decimal": "0.8",
|
| 386 |
+
"value_tol": 1e-12,
|
| 387 |
+
"expect": {"error": "LAMBDA_WEIGHT_SUM", "verdict": "BLOCK", "code": "LAMBDA_WEIGHT_SUM"}
|
| 388 |
+
},
|
| 389 |
+
{
|
| 390 |
+
"id": "weight_nan",
|
| 391 |
+
"source": "szl.lambda/v1 domain: NaN weight",
|
| 392 |
+
"axes": ["f64:3fe0000000000000", "f64:3feccccccccccccd"],
|
| 393 |
+
"axes_decimal": ["0.5", "0.9"],
|
| 394 |
+
"weights": ["f64:7ff8000000000000", "f64:3fe0000000000000"],
|
| 395 |
+
"weights_decimal": ["nan", "0.5"],
|
| 396 |
+
"tau": "f64:3fe999999999999a",
|
| 397 |
+
"tau_decimal": "0.8",
|
| 398 |
+
"value_tol": 1e-12,
|
| 399 |
+
"expect": {"error": "LAMBDA_NONFINITE_WEIGHT", "verdict": "BLOCK", "code": "LAMBDA_NONFINITE_WEIGHT"}
|
| 400 |
+
},
|
| 401 |
+
{
|
| 402 |
+
"id": "weight_pos_inf",
|
| 403 |
+
"source": "szl.lambda/v1 domain: +Inf weight",
|
| 404 |
+
"axes": ["f64:3fe0000000000000", "f64:3feccccccccccccd"],
|
| 405 |
+
"axes_decimal": ["0.5", "0.9"],
|
| 406 |
+
"weights": ["f64:7ff0000000000000", "f64:3fe0000000000000"],
|
| 407 |
+
"weights_decimal": ["inf", "0.5"],
|
| 408 |
+
"tau": "f64:3fe999999999999a",
|
| 409 |
+
"tau_decimal": "0.8",
|
| 410 |
+
"value_tol": 1e-12,
|
| 411 |
+
"expect": {"error": "LAMBDA_NONFINITE_WEIGHT", "verdict": "BLOCK", "code": "LAMBDA_NONFINITE_WEIGHT"}
|
| 412 |
+
},
|
| 413 |
+
{
|
| 414 |
+
"id": "weight_neg_inf",
|
| 415 |
+
"source": "szl.lambda/v1 domain: -Inf weight",
|
| 416 |
+
"axes": ["f64:3fe0000000000000", "f64:3feccccccccccccd"],
|
| 417 |
+
"axes_decimal": ["0.5", "0.9"],
|
| 418 |
+
"weights": ["f64:fff0000000000000", "f64:3fe0000000000000"],
|
| 419 |
+
"weights_decimal": ["-inf", "0.5"],
|
| 420 |
+
"tau": "f64:3fe999999999999a",
|
| 421 |
+
"tau_decimal": "0.8",
|
| 422 |
+
"value_tol": 1e-12,
|
| 423 |
+
"expect": {"error": "LAMBDA_NONFINITE_WEIGHT", "verdict": "BLOCK", "code": "LAMBDA_NONFINITE_WEIGHT"}
|
| 424 |
+
},
|
| 425 |
+
{
|
| 426 |
+
"id": "precedence_nonfinite_before_range_a",
|
| 427 |
+
"source": "szl.lambda/v1 error precedence (phases, permutation-invariant)",
|
| 428 |
+
"axes": ["f64:3ff8000000000000", "f64:7ff8000000000000"],
|
| 429 |
+
"axes_decimal": ["1.5", "nan"],
|
| 430 |
+
"weights": ["f64:3fe0000000000000", "f64:3fe0000000000000"],
|
| 431 |
+
"weights_decimal": ["0.5", "0.5"],
|
| 432 |
+
"tau": "f64:3fe999999999999a",
|
| 433 |
+
"tau_decimal": "0.8",
|
| 434 |
+
"value_tol": 1e-12,
|
| 435 |
+
"expect": {"error": "LAMBDA_NONFINITE_AXIS", "verdict": "BLOCK", "code": "LAMBDA_NONFINITE_AXIS"}
|
| 436 |
+
},
|
| 437 |
+
{
|
| 438 |
+
"id": "precedence_nonfinite_before_range_b",
|
| 439 |
+
"source": "szl.lambda/v1 error precedence (phases, permutation-invariant)",
|
| 440 |
+
"axes": ["f64:7ff8000000000000", "f64:3ff8000000000000"],
|
| 441 |
+
"axes_decimal": ["nan", "1.5"],
|
| 442 |
+
"weights": ["f64:3fe0000000000000", "f64:3fe0000000000000"],
|
| 443 |
+
"weights_decimal": ["0.5", "0.5"],
|
| 444 |
+
"tau": "f64:3fe999999999999a",
|
| 445 |
+
"tau_decimal": "0.8",
|
| 446 |
+
"value_tol": 1e-12,
|
| 447 |
+
"expect": {"error": "LAMBDA_NONFINITE_AXIS", "verdict": "BLOCK", "code": "LAMBDA_NONFINITE_AXIS"}
|
| 448 |
+
},
|
| 449 |
+
{
|
| 450 |
+
"id": "precedence_axis_before_weight",
|
| 451 |
+
"source": "szl.lambda/v1 error precedence (phases, permutation-invariant)",
|
| 452 |
+
"axes": ["f64:3ff8000000000000", "f64:3feccccccccccccd"],
|
| 453 |
+
"axes_decimal": ["1.5", "0.9"],
|
| 454 |
+
"weights": ["f64:4000000000000000", "f64:4000000000000000"],
|
| 455 |
+
"weights_decimal": ["2.0", "2.0"],
|
| 456 |
+
"tau": "f64:3fe999999999999a",
|
| 457 |
+
"tau_decimal": "0.8",
|
| 458 |
+
"value_tol": 1e-12,
|
| 459 |
+
"expect": {"error": "LAMBDA_AXIS_OUT_OF_RANGE", "verdict": "BLOCK", "code": "LAMBDA_AXIS_OUT_OF_RANGE"}
|
| 460 |
+
},
|
| 461 |
+
{
|
| 462 |
+
"id": "precedence_zero_does_not_mask_nan_a",
|
| 463 |
+
"source": "szl.lambda/v1 error precedence (phases, permutation-invariant): every axis is validated before the zero veto (szl-receipt#38, FF-03b)",
|
| 464 |
+
"axes": ["f64:0000000000000000", "f64:7ff8000000000000"],
|
| 465 |
+
"axes_decimal": ["0.0", "nan"],
|
| 466 |
+
"weights": ["f64:3fe0000000000000", "f64:3fe0000000000000"],
|
| 467 |
+
"weights_decimal": ["0.5", "0.5"],
|
| 468 |
+
"tau": "f64:3fe0000000000000",
|
| 469 |
+
"tau_decimal": "0.5",
|
| 470 |
+
"value_tol": 1e-12,
|
| 471 |
+
"expect": {"error": "LAMBDA_NONFINITE_AXIS", "verdict": "BLOCK", "code": "LAMBDA_NONFINITE_AXIS"}
|
| 472 |
+
},
|
| 473 |
+
{
|
| 474 |
+
"id": "precedence_zero_does_not_mask_nan_b",
|
| 475 |
+
"source": "szl.lambda/v1 error precedence (phases, permutation-invariant): every axis is validated before the zero veto (szl-receipt#38, FF-03b)",
|
| 476 |
+
"axes": ["f64:7ff8000000000000", "f64:0000000000000000"],
|
| 477 |
+
"axes_decimal": ["nan", "0.0"],
|
| 478 |
+
"weights": ["f64:3fe0000000000000", "f64:3fe0000000000000"],
|
| 479 |
+
"weights_decimal": ["0.5", "0.5"],
|
| 480 |
+
"tau": "f64:3fe0000000000000",
|
| 481 |
+
"tau_decimal": "0.5",
|
| 482 |
+
"value_tol": 1e-12,
|
| 483 |
+
"expect": {"error": "LAMBDA_NONFINITE_AXIS", "verdict": "BLOCK", "code": "LAMBDA_NONFINITE_AXIS"}
|
| 484 |
+
},
|
| 485 |
+
{
|
| 486 |
+
"id": "precedence_zero_does_not_mask_pos_inf_a",
|
| 487 |
+
"source": "szl.lambda/v1 error precedence (phases, permutation-invariant): every axis is validated before the zero veto (szl-receipt#38, FF-03b)",
|
| 488 |
+
"axes": ["f64:0000000000000000", "f64:7ff0000000000000"],
|
| 489 |
+
"axes_decimal": ["0.0", "inf"],
|
| 490 |
+
"weights": ["f64:3fe0000000000000", "f64:3fe0000000000000"],
|
| 491 |
+
"weights_decimal": ["0.5", "0.5"],
|
| 492 |
+
"tau": "f64:3fe0000000000000",
|
| 493 |
+
"tau_decimal": "0.5",
|
| 494 |
+
"value_tol": 1e-12,
|
| 495 |
+
"expect": {"error": "LAMBDA_NONFINITE_AXIS", "verdict": "BLOCK", "code": "LAMBDA_NONFINITE_AXIS"}
|
| 496 |
+
},
|
| 497 |
+
{
|
| 498 |
+
"id": "precedence_zero_does_not_mask_pos_inf_b",
|
| 499 |
+
"source": "szl.lambda/v1 error precedence (phases, permutation-invariant): every axis is validated before the zero veto (szl-receipt#38, FF-03b)",
|
| 500 |
+
"axes": ["f64:7ff0000000000000", "f64:0000000000000000"],
|
| 501 |
+
"axes_decimal": ["inf", "0.0"],
|
| 502 |
+
"weights": ["f64:3fe0000000000000", "f64:3fe0000000000000"],
|
| 503 |
+
"weights_decimal": ["0.5", "0.5"],
|
| 504 |
+
"tau": "f64:3fe0000000000000",
|
| 505 |
+
"tau_decimal": "0.5",
|
| 506 |
+
"value_tol": 1e-12,
|
| 507 |
+
"expect": {"error": "LAMBDA_NONFINITE_AXIS", "verdict": "BLOCK", "code": "LAMBDA_NONFINITE_AXIS"}
|
| 508 |
+
},
|
| 509 |
+
{
|
| 510 |
+
"id": "precedence_zero_does_not_mask_x_gt_1_a",
|
| 511 |
+
"source": "szl.lambda/v1 error precedence (phases, permutation-invariant): every axis is validated before the zero veto (szl-receipt#38, FF-03b)",
|
| 512 |
+
"axes": ["f64:0000000000000000", "f64:3ff8000000000000"],
|
| 513 |
+
"axes_decimal": ["0.0", "1.5"],
|
| 514 |
+
"weights": ["f64:3fe0000000000000", "f64:3fe0000000000000"],
|
| 515 |
+
"weights_decimal": ["0.5", "0.5"],
|
| 516 |
+
"tau": "f64:3fe0000000000000",
|
| 517 |
+
"tau_decimal": "0.5",
|
| 518 |
+
"value_tol": 1e-12,
|
| 519 |
+
"expect": {"error": "LAMBDA_AXIS_OUT_OF_RANGE", "verdict": "BLOCK", "code": "LAMBDA_AXIS_OUT_OF_RANGE"}
|
| 520 |
+
},
|
| 521 |
+
{
|
| 522 |
+
"id": "precedence_zero_does_not_mask_x_gt_1_b",
|
| 523 |
+
"source": "szl.lambda/v1 error precedence (phases, permutation-invariant): every axis is validated before the zero veto (szl-receipt#38, FF-03b)",
|
| 524 |
+
"axes": ["f64:3ff8000000000000", "f64:0000000000000000"],
|
| 525 |
+
"axes_decimal": ["1.5", "0.0"],
|
| 526 |
+
"weights": ["f64:3fe0000000000000", "f64:3fe0000000000000"],
|
| 527 |
+
"weights_decimal": ["0.5", "0.5"],
|
| 528 |
+
"tau": "f64:3fe0000000000000",
|
| 529 |
+
"tau_decimal": "0.5",
|
| 530 |
+
"value_tol": 1e-12,
|
| 531 |
+
"expect": {"error": "LAMBDA_AXIS_OUT_OF_RANGE", "verdict": "BLOCK", "code": "LAMBDA_AXIS_OUT_OF_RANGE"}
|
| 532 |
+
},
|
| 533 |
+
{
|
| 534 |
+
"id": "precedence_zero_does_not_mask_negative_a",
|
| 535 |
+
"source": "szl.lambda/v1 error precedence (phases, permutation-invariant): every axis is validated before the zero veto (szl-receipt#38, FF-03b)",
|
| 536 |
+
"axes": ["f64:0000000000000000", "f64:bfb999999999999a"],
|
| 537 |
+
"axes_decimal": ["0.0", "-0.1"],
|
| 538 |
+
"weights": ["f64:3fe0000000000000", "f64:3fe0000000000000"],
|
| 539 |
+
"weights_decimal": ["0.5", "0.5"],
|
| 540 |
+
"tau": "f64:3fe0000000000000",
|
| 541 |
+
"tau_decimal": "0.5",
|
| 542 |
+
"value_tol": 1e-12,
|
| 543 |
+
"expect": {"error": "LAMBDA_AXIS_OUT_OF_RANGE", "verdict": "BLOCK", "code": "LAMBDA_AXIS_OUT_OF_RANGE"}
|
| 544 |
+
},
|
| 545 |
+
{
|
| 546 |
+
"id": "precedence_zero_does_not_mask_negative_b",
|
| 547 |
+
"source": "szl.lambda/v1 error precedence (phases, permutation-invariant): every axis is validated before the zero veto (szl-receipt#38, FF-03b)",
|
| 548 |
+
"axes": ["f64:bfb999999999999a", "f64:0000000000000000"],
|
| 549 |
+
"axes_decimal": ["-0.1", "0.0"],
|
| 550 |
+
"weights": ["f64:3fe0000000000000", "f64:3fe0000000000000"],
|
| 551 |
+
"weights_decimal": ["0.5", "0.5"],
|
| 552 |
+
"tau": "f64:3fe0000000000000",
|
| 553 |
+
"tau_decimal": "0.5",
|
| 554 |
+
"value_tol": 1e-12,
|
| 555 |
+
"expect": {"error": "LAMBDA_AXIS_OUT_OF_RANGE", "verdict": "BLOCK", "code": "LAMBDA_AXIS_OUT_OF_RANGE"}
|
| 556 |
+
},
|
| 557 |
+
{
|
| 558 |
+
"id": "precedence_zero_does_not_mask_bool_a",
|
| 559 |
+
"source": "szl.lambda/v1 error precedence (phases, permutation-invariant): every axis is validated before the zero veto (szl-receipt#38, FF-03b)",
|
| 560 |
+
"axes": ["f64:0000000000000000", true],
|
| 561 |
+
"weights": ["f64:3fe0000000000000", "f64:3fe0000000000000"],
|
| 562 |
+
"weights_decimal": ["0.5", "0.5"],
|
| 563 |
+
"tau": "f64:3fe0000000000000",
|
| 564 |
+
"tau_decimal": "0.5",
|
| 565 |
+
"value_tol": 1e-12,
|
| 566 |
+
"expect": {"error": "LAMBDA_TYPE_INVALID", "verdict": "BLOCK", "code": "LAMBDA_TYPE_INVALID"}
|
| 567 |
+
},
|
| 568 |
+
{
|
| 569 |
+
"id": "precedence_zero_does_not_mask_bool_b",
|
| 570 |
+
"source": "szl.lambda/v1 error precedence (phases, permutation-invariant): every axis is validated before the zero veto (szl-receipt#38, FF-03b)",
|
| 571 |
+
"axes": [true, "f64:0000000000000000"],
|
| 572 |
+
"weights": ["f64:3fe0000000000000", "f64:3fe0000000000000"],
|
| 573 |
+
"weights_decimal": ["0.5", "0.5"],
|
| 574 |
+
"tau": "f64:3fe0000000000000",
|
| 575 |
+
"tau_decimal": "0.5",
|
| 576 |
+
"value_tol": 1e-12,
|
| 577 |
+
"expect": {"error": "LAMBDA_TYPE_INVALID", "verdict": "BLOCK", "code": "LAMBDA_TYPE_INVALID"}
|
| 578 |
+
},
|
| 579 |
+
{
|
| 580 |
+
"id": "type_bool_axis",
|
| 581 |
+
"source": "szl.lambda/v1 type contract: bool is not a number",
|
| 582 |
+
"axes": [true, "f64:3feccccccccccccd"],
|
| 583 |
+
"weights": ["f64:3fe0000000000000", "f64:3fe0000000000000"],
|
| 584 |
+
"weights_decimal": ["0.5", "0.5"],
|
| 585 |
+
"tau": "f64:3fe999999999999a",
|
| 586 |
+
"tau_decimal": "0.8",
|
| 587 |
+
"value_tol": 1e-12,
|
| 588 |
+
"expect": {"error": "LAMBDA_TYPE_INVALID", "verdict": "BLOCK", "code": "LAMBDA_TYPE_INVALID"}
|
| 589 |
+
},
|
| 590 |
+
{
|
| 591 |
+
"id": "type_null_axis",
|
| 592 |
+
"source": "szl.lambda/v1 type contract: null axis",
|
| 593 |
+
"axes": [null, "f64:3feccccccccccccd"],
|
| 594 |
+
"weights": ["f64:3fe0000000000000", "f64:3fe0000000000000"],
|
| 595 |
+
"weights_decimal": ["0.5", "0.5"],
|
| 596 |
+
"tau": "f64:3fe999999999999a",
|
| 597 |
+
"tau_decimal": "0.8",
|
| 598 |
+
"value_tol": 1e-12,
|
| 599 |
+
"expect": {"error": "LAMBDA_TYPE_INVALID", "verdict": "BLOCK", "code": "LAMBDA_TYPE_INVALID"}
|
| 600 |
+
},
|
| 601 |
+
{
|
| 602 |
+
"id": "type_string_axis",
|
| 603 |
+
"source": "szl.lambda/v1 type contract: a string is not a number",
|
| 604 |
+
"axes": ["0.9", "f64:3feccccccccccccd"],
|
| 605 |
+
"weights": ["f64:3fe0000000000000", "f64:3fe0000000000000"],
|
| 606 |
+
"weights_decimal": ["0.5", "0.5"],
|
| 607 |
+
"tau": "f64:3fe999999999999a",
|
| 608 |
+
"tau_decimal": "0.8",
|
| 609 |
+
"value_tol": 1e-12,
|
| 610 |
+
"expect": {"error": "LAMBDA_TYPE_INVALID", "verdict": "BLOCK", "code": "LAMBDA_TYPE_INVALID"}
|
| 611 |
+
},
|
| 612 |
+
{
|
| 613 |
+
"id": "type_axes_not_array",
|
| 614 |
+
"source": "szl.lambda/v1 type contract: axes must be an array",
|
| 615 |
+
"axes": null,
|
| 616 |
+
"weights": ["f64:3ff0000000000000"],
|
| 617 |
+
"weights_decimal": ["1.0"],
|
| 618 |
+
"tau": "f64:3fe999999999999a",
|
| 619 |
+
"tau_decimal": "0.8",
|
| 620 |
+
"value_tol": 1e-12,
|
| 621 |
+
"expect": {"error": "LAMBDA_TYPE_INVALID", "verdict": "BLOCK", "code": "LAMBDA_TYPE_INVALID"}
|
| 622 |
+
},
|
| 623 |
+
{
|
| 624 |
+
"id": "type_bool_weight",
|
| 625 |
+
"source": "szl.lambda/v1 type contract: bool weight",
|
| 626 |
+
"note": "the element type is checked before the weight value",
|
| 627 |
+
"axes": ["f64:3fe0000000000000", "f64:3feccccccccccccd"],
|
| 628 |
+
"axes_decimal": ["0.5", "0.9"],
|
| 629 |
+
"weights": [true, "f64:0000000000000000"],
|
| 630 |
+
"tau": "f64:3fe999999999999a",
|
| 631 |
+
"tau_decimal": "0.8",
|
| 632 |
+
"value_tol": 1e-12,
|
| 633 |
+
"expect": {"error": "LAMBDA_TYPE_INVALID", "verdict": "BLOCK", "code": "LAMBDA_TYPE_INVALID"}
|
| 634 |
+
},
|
| 635 |
+
{
|
| 636 |
+
"id": "tau_one",
|
| 637 |
+
"source": "szl.lambda/v1 tau domain (0, 1], required, checked first",
|
| 638 |
+
"axes": ["f64:3fee666666666666", "f64:3fed70a3d70a3d71", "f64:3fec28f5c28f5c29", "f64:3feccccccccccccd"],
|
| 639 |
+
"axes_decimal": ["0.95", "0.92", "0.88", "0.9"],
|
| 640 |
+
"weights": ["f64:3fd0000000000000", "f64:3fd0000000000000", "f64:3fd0000000000000", "f64:3fd0000000000000"],
|
| 641 |
+
"weights_decimal": ["0.25", "0.25", "0.25", "0.25"],
|
| 642 |
+
"tau": "f64:3ff0000000000000",
|
| 643 |
+
"tau_decimal": "1.0",
|
| 644 |
+
"value_tol": 1e-12,
|
| 645 |
+
"expect": {"value_f64": "f64:3fed3035e27f23fe", "value_decimal": "0.9121350692522581", "verdict": "NO_GO", "code": "BELOW_TAU"}
|
| 646 |
+
},
|
| 647 |
+
{
|
| 648 |
+
"id": "tau_nan",
|
| 649 |
+
"source": "szl.lambda/v1 tau domain (0, 1], required, checked first",
|
| 650 |
+
"axes": ["f64:3fee666666666666", "f64:3fed70a3d70a3d71", "f64:3fec28f5c28f5c29", "f64:3feccccccccccccd"],
|
| 651 |
+
"axes_decimal": ["0.95", "0.92", "0.88", "0.9"],
|
| 652 |
+
"weights": ["f64:3fd0000000000000", "f64:3fd0000000000000", "f64:3fd0000000000000", "f64:3fd0000000000000"],
|
| 653 |
+
"weights_decimal": ["0.25", "0.25", "0.25", "0.25"],
|
| 654 |
+
"tau": "f64:7ff8000000000000",
|
| 655 |
+
"tau_decimal": "nan",
|
| 656 |
+
"value_tol": 1e-12,
|
| 657 |
+
"expect": {"value_f64": "f64:3fed3035e27f23fe", "value_decimal": "0.9121350692522581", "verdict": "BLOCK", "code": "LAMBDA_TAU_INVALID"}
|
| 658 |
+
},
|
| 659 |
+
{
|
| 660 |
+
"id": "tau_zero_would_admit_veto",
|
| 661 |
+
"source": "szl.lambda/v1 tau domain (0, 1], required, checked first: tau 0 would pass a vetoed Λ = 0",
|
| 662 |
+
"axes": ["f64:0000000000000000", "f64:3feccccccccccccd"],
|
| 663 |
+
"axes_decimal": ["0.0", "0.9"],
|
| 664 |
+
"weights": ["f64:3fe0000000000000", "f64:3fe0000000000000"],
|
| 665 |
+
"weights_decimal": ["0.5", "0.5"],
|
| 666 |
+
"tau": "f64:0000000000000000",
|
| 667 |
+
"tau_decimal": "0.0",
|
| 668 |
+
"value_tol": 1e-12,
|
| 669 |
+
"expect": {"value_f64": "f64:0000000000000000", "value_decimal": "0.0", "verdict": "BLOCK", "code": "LAMBDA_TAU_INVALID"}
|
| 670 |
+
},
|
| 671 |
+
{
|
| 672 |
+
"id": "tau_above_one",
|
| 673 |
+
"source": "szl.lambda/v1 tau domain (0, 1], required, checked first",
|
| 674 |
+
"axes": ["f64:3fee666666666666", "f64:3fed70a3d70a3d71", "f64:3fec28f5c28f5c29", "f64:3feccccccccccccd"],
|
| 675 |
+
"axes_decimal": ["0.95", "0.92", "0.88", "0.9"],
|
| 676 |
+
"weights": ["f64:3fd0000000000000", "f64:3fd0000000000000", "f64:3fd0000000000000", "f64:3fd0000000000000"],
|
| 677 |
+
"weights_decimal": ["0.25", "0.25", "0.25", "0.25"],
|
| 678 |
+
"tau": "f64:3ff8000000000000",
|
| 679 |
+
"tau_decimal": "1.5",
|
| 680 |
+
"value_tol": 1e-12,
|
| 681 |
+
"expect": {"value_f64": "f64:3fed3035e27f23fe", "value_decimal": "0.9121350692522581", "verdict": "BLOCK", "code": "LAMBDA_TAU_INVALID"}
|
| 682 |
+
},
|
| 683 |
+
{
|
| 684 |
+
"id": "tau_negative",
|
| 685 |
+
"source": "szl.lambda/v1 tau domain (0, 1], required, checked first",
|
| 686 |
+
"axes": ["f64:3fee666666666666", "f64:3fed70a3d70a3d71", "f64:3fec28f5c28f5c29", "f64:3feccccccccccccd"],
|
| 687 |
+
"axes_decimal": ["0.95", "0.92", "0.88", "0.9"],
|
| 688 |
+
"weights": ["f64:3fd0000000000000", "f64:3fd0000000000000", "f64:3fd0000000000000", "f64:3fd0000000000000"],
|
| 689 |
+
"weights_decimal": ["0.25", "0.25", "0.25", "0.25"],
|
| 690 |
+
"tau": "f64:bfb999999999999a",
|
| 691 |
+
"tau_decimal": "-0.1",
|
| 692 |
+
"value_tol": 1e-12,
|
| 693 |
+
"expect": {"value_f64": "f64:3fed3035e27f23fe", "value_decimal": "0.9121350692522581", "verdict": "BLOCK", "code": "LAMBDA_TAU_INVALID"}
|
| 694 |
+
},
|
| 695 |
+
{
|
| 696 |
+
"id": "tau_bool",
|
| 697 |
+
"source": "szl.lambda/v1 tau domain (0, 1], required, checked first: bool is not a number",
|
| 698 |
+
"axes": ["f64:3fee666666666666", "f64:3fed70a3d70a3d71", "f64:3fec28f5c28f5c29", "f64:3feccccccccccccd"],
|
| 699 |
+
"axes_decimal": ["0.95", "0.92", "0.88", "0.9"],
|
| 700 |
+
"weights": ["f64:3fd0000000000000", "f64:3fd0000000000000", "f64:3fd0000000000000", "f64:3fd0000000000000"],
|
| 701 |
+
"weights_decimal": ["0.25", "0.25", "0.25", "0.25"],
|
| 702 |
+
"tau": true,
|
| 703 |
+
"value_tol": 1e-12,
|
| 704 |
+
"expect": {"value_f64": "f64:3fed3035e27f23fe", "value_decimal": "0.9121350692522581", "verdict": "BLOCK", "code": "LAMBDA_TAU_INVALID"}
|
| 705 |
+
},
|
| 706 |
+
{
|
| 707 |
+
"id": "tau_null",
|
| 708 |
+
"source": "szl.lambda/v1 tau domain (0, 1], required, checked first: tau is required",
|
| 709 |
+
"axes": ["f64:3fee666666666666", "f64:3fed70a3d70a3d71", "f64:3fec28f5c28f5c29", "f64:3feccccccccccccd"],
|
| 710 |
+
"axes_decimal": ["0.95", "0.92", "0.88", "0.9"],
|
| 711 |
+
"weights": ["f64:3fd0000000000000", "f64:3fd0000000000000", "f64:3fd0000000000000", "f64:3fd0000000000000"],
|
| 712 |
+
"weights_decimal": ["0.25", "0.25", "0.25", "0.25"],
|
| 713 |
+
"tau": null,
|
| 714 |
+
"value_tol": 1e-12,
|
| 715 |
+
"expect": {"value_f64": "f64:3fed3035e27f23fe", "value_decimal": "0.9121350692522581", "verdict": "BLOCK", "code": "LAMBDA_TAU_INVALID"}
|
| 716 |
+
},
|
| 717 |
+
{
|
| 718 |
+
"id": "tau_precedes_axis_error",
|
| 719 |
+
"source": "szl.lambda/v1 tau domain (0, 1], required, checked first: tau is checked before Λ",
|
| 720 |
+
"axes": ["f64:3ff8000000000000", "f64:3feccccccccccccd"],
|
| 721 |
+
"axes_decimal": ["1.5", "0.9"],
|
| 722 |
+
"weights": ["f64:3fe0000000000000", "f64:3fe0000000000000"],
|
| 723 |
+
"weights_decimal": ["0.5", "0.5"],
|
| 724 |
+
"tau": "f64:7ff8000000000000",
|
| 725 |
+
"tau_decimal": "nan",
|
| 726 |
+
"value_tol": 1e-12,
|
| 727 |
+
"expect": {"error": "LAMBDA_AXIS_OUT_OF_RANGE", "verdict": "BLOCK", "code": "LAMBDA_TAU_INVALID"}
|
| 728 |
+
}
|
| 729 |
+
]
|
| 730 |
+
}
|
spec/szl.lambda.v1.json
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"schema": "szl.lambda/v1",
|
| 3 |
+
"title": "Λ, the weighted geometric mean, as a validated contract",
|
| 4 |
+
"status": "ADVISORY",
|
| 5 |
+
"uniqueness": "CONJECTURE_1_NOT_USED",
|
| 6 |
+
"honesty": "Λ is an advisory, non-compensatory roll-up. Λ uniqueness is Conjecture 1 (open); nothing in this contract depends on it.",
|
| 7 |
+
"form": "weighted_geometric_mean",
|
| 8 |
+
"definition": "Λ_w(x) = ∏_k x_k^{w_k}",
|
| 9 |
+
"algorithm": {
|
| 10 |
+
"log_lambda": "-inf if some x_k == 0, else math.fsum(w_k * math.log(x_k))",
|
| 11 |
+
"lambda": "0.0 if log_lambda == -inf, else math.exp(log_lambda)",
|
| 12 |
+
"weight_sum": "math.fsum(weights)",
|
| 13 |
+
"why_fsum": "fsum is correctly rounded, so Λ is bitwise invariant under a permutation of (axes, weights)"
|
| 14 |
+
},
|
| 15 |
+
"domain": {
|
| 16 |
+
"axes": "list or tuple of real numbers (bool is not a number); every x_k finite and 0 <= x_k <= 1",
|
| 17 |
+
"weights": "list or tuple of real numbers, the same length as axes; every w_k finite and w_k > 0; |fsum(weights) - 1| <= weight_sum_tol",
|
| 18 |
+
"empty": "error LAMBDA_EMPTY, never 0.0",
|
| 19 |
+
"renormalisation": "never: weights are used as declared",
|
| 20 |
+
"clamping": "never: a non-finite or out-of-range value is an error",
|
| 21 |
+
"rounding": "never before the compare",
|
| 22 |
+
"output": "Λ in [0, 1]; exactly 0.0 iff some x_k == 0 (a veto, not an error)"
|
| 23 |
+
},
|
| 24 |
+
"weight_sum_tol": 1e-12,
|
| 25 |
+
"error_codes": [
|
| 26 |
+
{
|
| 27 |
+
"code": "LAMBDA_TYPE_INVALID",
|
| 28 |
+
"when": "axes or weights is not a list/tuple (checked first), or an element is not a real number (checked after LAMBDA_LENGTH_MISMATCH)"
|
| 29 |
+
},
|
| 30 |
+
{
|
| 31 |
+
"code": "LAMBDA_EMPTY",
|
| 32 |
+
"when": "axes or weights is empty"
|
| 33 |
+
},
|
| 34 |
+
{
|
| 35 |
+
"code": "LAMBDA_LENGTH_MISMATCH",
|
| 36 |
+
"when": "len(axes) != len(weights); a dropped axis is never renormalised away"
|
| 37 |
+
},
|
| 38 |
+
{
|
| 39 |
+
"code": "LAMBDA_NONFINITE_AXIS",
|
| 40 |
+
"when": "some x_k is NaN, +Inf or -Inf"
|
| 41 |
+
},
|
| 42 |
+
{
|
| 43 |
+
"code": "LAMBDA_AXIS_OUT_OF_RANGE",
|
| 44 |
+
"when": "some x_k < 0 or x_k > 1"
|
| 45 |
+
},
|
| 46 |
+
{
|
| 47 |
+
"code": "LAMBDA_NONFINITE_WEIGHT",
|
| 48 |
+
"when": "some w_k is NaN, +Inf or -Inf"
|
| 49 |
+
},
|
| 50 |
+
{
|
| 51 |
+
"code": "LAMBDA_WEIGHT_NONPOSITIVE",
|
| 52 |
+
"when": "some w_k <= 0"
|
| 53 |
+
},
|
| 54 |
+
{
|
| 55 |
+
"code": "LAMBDA_WEIGHT_SUM",
|
| 56 |
+
"when": "|fsum(weights) - 1| > weight_sum_tol, or the sum overflows"
|
| 57 |
+
},
|
| 58 |
+
{
|
| 59 |
+
"code": "LAMBDA_TAU_INVALID",
|
| 60 |
+
"when": "gate only: tau is not a real number, not finite, or outside (0, 1]; checked before Λ"
|
| 61 |
+
}
|
| 62 |
+
],
|
| 63 |
+
"precedence": "Checks run in the order of error_codes, each over every element, so the reported code does not depend on axis order. The gate checks tau first.",
|
| 64 |
+
"gate": {
|
| 65 |
+
"signature": "gate_v1(axes, weights, tau) -> (verdict, code)",
|
| 66 |
+
"verdicts": [
|
| 67 |
+
"GO",
|
| 68 |
+
"NO_GO",
|
| 69 |
+
"ABSTAIN",
|
| 70 |
+
"BLOCK"
|
| 71 |
+
],
|
| 72 |
+
"tau": {
|
| 73 |
+
"required": true,
|
| 74 |
+
"implicit_default": null,
|
| 75 |
+
"domain": "real, finite, 0 < tau <= 1 (tau = 0 would pass a vetoed Λ = 0)",
|
| 76 |
+
"error": "LAMBDA_TAU_INVALID",
|
| 77 |
+
"default_source": "frontier/model_admit_contract.v1.json#/policy_tau",
|
| 78 |
+
"policy_tau_at_authoring": 0.8
|
| 79 |
+
},
|
| 80 |
+
"compare": "log space on the unrounded value: delta = log_lambda - log(tau)",
|
| 81 |
+
"tie_eps": 1e-09,
|
| 82 |
+
"rules": [
|
| 83 |
+
{
|
| 84 |
+
"if": "tau invalid, or Λ raises",
|
| 85 |
+
"verdict": "BLOCK",
|
| 86 |
+
"code": "the error code"
|
| 87 |
+
},
|
| 88 |
+
{
|
| 89 |
+
"if": "log_lambda == -inf (some axis is 0)",
|
| 90 |
+
"verdict": "NO_GO",
|
| 91 |
+
"code": "ZERO_VETO"
|
| 92 |
+
},
|
| 93 |
+
{
|
| 94 |
+
"if": "abs(delta) <= tie_eps",
|
| 95 |
+
"verdict": "ABSTAIN",
|
| 96 |
+
"code": "NUMERIC_TIE"
|
| 97 |
+
},
|
| 98 |
+
{
|
| 99 |
+
"if": "delta > 0",
|
| 100 |
+
"verdict": "GO",
|
| 101 |
+
"code": null
|
| 102 |
+
},
|
| 103 |
+
{
|
| 104 |
+
"if": "otherwise",
|
| 105 |
+
"verdict": "NO_GO",
|
| 106 |
+
"code": "BELOW_TAU"
|
| 107 |
+
}
|
| 108 |
+
]
|
| 109 |
+
},
|
| 110 |
+
"canonical_float": "f64:<16 lowercase hex digits of the IEEE-754 binary64 bits, big-endian>",
|
| 111 |
+
"vectors": {
|
| 112 |
+
"path": "spec/lambda_v1_vectors.json",
|
| 113 |
+
"count": 60,
|
| 114 |
+
"sha256": "61bfb0410b9f0eaab0eb9f22f29cb7cb13cfde8c083fe308d895565d6ba9ebd4",
|
| 115 |
+
"digest": {
|
| 116 |
+
"algorithm": "sha256",
|
| 117 |
+
"over": "canonical_json",
|
| 118 |
+
"sort_keys": true,
|
| 119 |
+
"separators": [
|
| 120 |
+
",",
|
| 121 |
+
":"
|
| 122 |
+
],
|
| 123 |
+
"ensure_ascii": false,
|
| 124 |
+
"allow_nan": false,
|
| 125 |
+
"encoding": "utf-8"
|
| 126 |
+
}
|
| 127 |
+
},
|
| 128 |
+
"reference": "reference/szl_lambda_v1.py",
|
| 129 |
+
"legacy_not_canonical": [
|
| 130 |
+
"tests/lambda_aggregator_source.py: +Inf counts as 1, NaN leaks, empty gives 0.0, weights are renormalised",
|
| 131 |
+
"torch-ext/szl_lambda_gate/_lambda.py lambda_aggregate: clamps, routes NaN/Inf to 0, renormalises; pinned in tests/test_lambda_v1_torch_divergence.py"
|
| 132 |
+
]
|
| 133 |
+
}
|