Skip to content

Commit 3002094

Browse files
authored
Merge branch 'main' into codex/verifier-integrity-538-500
2 parents 57994df + 8ceb534 commit 3002094

16 files changed

Lines changed: 397 additions & 208 deletions

File tree

.github/scripts/manage-test-timings

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -300,10 +300,16 @@ def merge(
300300
payload = load_json_bytes(path.read_bytes(), str(path))
301301
durations = validate_durations(payload, str(path), suite)
302302
duplicates = merged.keys() & durations.keys()
303-
if duplicates:
304-
duplicate = min(duplicates)
305-
raise ValueError(f"duplicate timing entry across shards: {duplicate}")
306-
merged.update(durations)
303+
for nodeid in sorted(duplicates):
304+
previous = merged[nodeid]
305+
incoming = durations[nodeid]
306+
if incoming > previous:
307+
merged[nodeid] = incoming
308+
warning(
309+
f"duplicate timing entry across shards for {nodeid}; "
310+
f"keeping max({previous}, {incoming})"
311+
)
312+
merged.update({k: v for k, v in durations.items() if k not in duplicates})
307313

308314
envelope = {
309315
"version": 1,

docs/reference/capabilities/polynomial/gaussian-polynomial-moments.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ The request bounds are:
3030
| Nonzero sparse terms | 1–16 |
3131
| Total degree of each input term | at most 8 |
3232
| Fixed moment order \(m\) | 0–16 |
33-
| Raw ordered expansion paths | `term_count ** m <= 4096` |
33+
| Raw ordered expansion paths | `term_count ** m <= 65536` |
3434
| Input rational numerator/denominator | at most 128 decimal digits |
3535

3636
Terms use exponent vectors of length `variable_count`, in strictly increasing
@@ -39,8 +39,10 @@ lexicographic order. A coefficient has separate canonical rational `real` and
3939
exponents, inconsistent dimensions, and requests beyond the complete-expansion
4040
bound fail validation before computation or artifact writes.
4141

42-
The producer exactly expands \(P^m\), merges equal exponent vectors, and removes
43-
zero coefficients. For every remaining exponent vector
42+
The producer exactly expands \(P^m\) via pinned Python-FLINT `fmpq_mpoly`
43+
binary exponentiation over a typed real/imaginary coefficient pair,
44+
merges equal exponent vectors, and removes zero coefficients. For every
45+
remaining exponent vector
4446
\(\alpha=(\alpha_1,\ldots,\alpha_n)\), its ledger records:
4547

4648
- the exact expanded coefficient;

docs/reference/scenarios/certified-smith-integral-homology.md

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
[Documentation home](../../index.md)
44

55
- Status: Current implementation reference; contracts are experimental
6-
- Matrix producer backend: bounded standard-library elementary operations
6+
- Matrix producer backend: SymPy `smith_normal_decomp` over `ZZ`; canonical
7+
diagonal and invariant factors, noncanonical unimodular transformations
78
- Topology producer backend: exact integer chain arithmetic over canonical
8-
simplex bases
9+
simplex bases; integral homology derives cycle bases and generator
10+
coordinates from the certified Smith transformations
911
- Checker backend: isolated standard-library replay with no producer or public
1012
contract imports
1113

@@ -65,13 +67,25 @@ zero dimensions for reuse in chain-complex witnesses. Every output integer is
6567
bounded to 32,768 digits. Complete request validation occurs before computation
6668
or artifact writes.
6769

68-
The implementation uses exact elementary row and column operations and has no
69-
optional-provider availability gate. Python-FLINT 0.9.0's Python API exposes a
70-
diagonal-only `snf()` operation even though current FLINT C documentation also
71-
describes `fmpz_mat_snf_transform`; SymPy's public normal-form API likewise
72-
returns the Smith form without full transformations. Those systems are
73-
independent test oracles for invariant factors, not hidden runtime
74-
dependencies of this producer.
70+
The producer delegates the Smith decomposition to SymPy's
71+
`smith_normal_decomp` over `ZZ` and has no optional-provider availability gate.
72+
The canonical diagonal \(D\) and the invariant factors \(d_1,\ldots,d_r\) are
73+
mathematical invariants: they are determined by the determinantal divisors of
74+
\(A\) and do not depend on the backend. The unimodular transformations
75+
\(U\) and \(V\), and every representative derived from them (kernel bases,
76+
cycle coordinates, generator coordinates, bounding chains), are deterministic
77+
for the pinned SymPy version but are **not** canonical: a different Smith
78+
backend or SymPy release may produce different \(U,V\) that satisfy the same
79+
relation \(D=UAV\). Compatibility is therefore semantic—the relation, both
80+
unimodular determinants, and the positive divisibility diagonal—rather than
81+
byte-identical transformations. The producer fail-closed checks verify all
82+
three before returning, and the independent checker replays them without
83+
calling the producer.
84+
85+
Python-FLINT 0.9.0's Python API exposes a diagonal-only `snf()` operation
86+
even though current FLINT C documentation also describes
87+
`fmpz_mat_snf_transform`. FLINT and SymPy are independent test oracles for
88+
invariant factors, not hidden runtime dependencies of the checker.
7589

7690
## Integral simplicial homology
7791

@@ -189,8 +203,10 @@ and reconsider the bounds only with new artifact-size and runtime evidence.
189203
This family does not provide persistent homology, a homology ring, cup
190204
products, canonical generators independent of the declared simplex
191205
orientation, manifold recognition, a preferred proof strategy, or conclusions
192-
beyond the supplied bounded complex. It does not make optional FLINT or SymPy
193-
providers authoritative checkers.
206+
beyond the supplied bounded complex. SymPy is the producer backend for the
207+
Smith decomposition; it is not an authoritative checker. The independent
208+
checker uses only standard-library integer arithmetic and never imports the
209+
producer or SymPy.
194210

195211
## Primary references
196212

src/jacobian/contracts/certified_snf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,8 @@ class CertifiedSmithNormalFormResult(ContractModel):
143143
certificate: SmithNormalFormCertificate
144144
exactness: Literal["EXACT_INTEGER"] = "EXACT_INTEGER"
145145
determinism: Literal["DETERMINISTIC"] = "DETERMINISTIC"
146-
backend: Literal["jacobian-stdlib-elementary-operations"] = (
147-
"jacobian-stdlib-elementary-operations"
146+
backend: Literal["jacobian-sympy-smith-normal-decomposition"] = (
147+
"jacobian-sympy-smith-normal-decomposition"
148148
)
149149
backend_version: Literal["1"] = "1"
150150
completeness: Literal["FULL_MATRIX_TRANSFORMATIONS"] = "FULL_MATRIX_TRANSFORMATIONS"

src/jacobian/contracts/probability.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
MAX_GAUSSIAN_POLYNOMIAL_TERMS = 16
2222
MAX_GAUSSIAN_TERM_DEGREE = 8
2323
MAX_GAUSSIAN_MOMENT_ORDER = 16
24-
MAX_GAUSSIAN_EXPANSION_PATHS = 4096
24+
MAX_GAUSSIAN_EXPANSION_PATHS = 65536
2525
MAX_GAUSSIAN_RESULT_RATIONAL_DIGITS = 4096
2626
MAX_GRAPH_RELIABILITY_VERTICES = 16
2727
MAX_GRAPH_RELIABILITY_EDGES = 12

src/jacobian/domains/_certified_snf.py

Lines changed: 52 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1-
"""Bounded elementary-operation Smith reduction used by trusted producers."""
1+
"""Smith normal decomposition used by trusted producers.
2+
3+
The canonical Smith diagonal and divisibility chain are mathematical
4+
invariants. The unimodular transformations ``U`` and ``V`` and every
5+
representative derived from them are deterministic for the pinned SymPy
6+
version but are **not** byte-identical across backend versions: compatibility
7+
is semantic (``D = U A V``, unimodularity, canonical diagonal) rather than
8+
representational.
9+
"""
210

311
from __future__ import annotations
412

@@ -173,42 +181,24 @@ def inverse_unimodular(matrix: Matrix) -> Matrix:
173181
return [row[size:] for row in augmented]
174182

175183

176-
def _swap_rows(matrix: Matrix, left: int, right: int) -> None:
177-
matrix[left], matrix[right] = matrix[right], matrix[left]
178-
179-
180-
def _swap_columns(matrix: Matrix, left: int, right: int) -> None:
181-
for row in matrix:
182-
row[left], row[right] = row[right], row[left]
183-
184-
185-
def _add_row_multiple(
186-
matrix: Matrix, target: int, source: int, multiplier: int
187-
) -> None:
188-
matrix[target] = [
189-
value + multiplier * other
190-
for value, other in zip(matrix[target], matrix[source], strict=True)
191-
]
192-
193-
194-
def _add_column_multiple(
195-
matrix: Matrix, target: int, source: int, multiplier: int
196-
) -> None:
197-
for row in matrix:
198-
row[target] += multiplier * row[source]
199-
200-
201-
def _negate_row(matrix: Matrix, row: int) -> None:
202-
matrix[row] = [-value for value in matrix[row]]
203-
204-
205184
def smith_reduce(
206185
source: Matrix,
207186
*,
208187
row_count: int | None = None,
209188
column_count: int | None = None,
210189
) -> SmithReduction:
211-
"""Return a canonical Smith diagonal and explicit elementary transformations."""
190+
"""Return a canonical Smith diagonal and explicit unimodular transformations.
191+
192+
Delegates the decomposition to SymPy's ``smith_normal_decomp`` over ``ZZ``
193+
and converts the result to native integer matrices. The canonical diagonal
194+
and invariant factors are mathematical invariants; ``U`` and ``V`` are
195+
deterministic for the pinned SymPy version but may differ from other
196+
backends. Fail-closed checks verify ``D = U A V``, unimodularity, and the
197+
positive divisibility chain before returning.
198+
"""
199+
200+
import sympy
201+
from sympy.matrices.normalforms import smith_normal_decomp
212202

213203
rows = len(source) if row_count is None else row_count
214204
columns = (
@@ -217,99 +207,51 @@ def smith_reduce(
217207
if len(source) != rows or any(len(row) != columns for row in source):
218208
raise ValueError("source entries do not match the declared matrix shape")
219209
original = [row[:] for row in source]
220-
matrix = [row[:] for row in source]
221-
left = identity_matrix(rows)
222-
right = identity_matrix(columns)
223-
diagonal_count = min(rows, columns)
224210

225-
for pivot in range(diagonal_count):
226-
selected = min(
227-
(
228-
(abs(matrix[row][column]), row, column)
229-
for row in range(pivot, rows)
230-
for column in range(pivot, columns)
231-
if matrix[row][column] != 0
232-
),
233-
default=None,
234-
)
235-
if selected is None:
236-
break
237-
_, selected_row, selected_column = selected
238-
if selected_row != pivot:
239-
_swap_rows(matrix, pivot, selected_row)
240-
_swap_rows(left, pivot, selected_row)
241-
if selected_column != pivot:
242-
_swap_columns(matrix, pivot, selected_column)
243-
_swap_columns(right, pivot, selected_column)
244-
245-
while True:
246-
changed = False
247-
for row in range(pivot + 1, rows):
248-
while matrix[row][pivot] != 0:
249-
quotient = matrix[row][pivot] // matrix[pivot][pivot]
250-
_add_row_multiple(matrix, row, pivot, -quotient)
251-
_add_row_multiple(left, row, pivot, -quotient)
252-
if matrix[row][pivot] != 0 and abs(matrix[row][pivot]) < abs(
253-
matrix[pivot][pivot]
254-
):
255-
_swap_rows(matrix, row, pivot)
256-
_swap_rows(left, row, pivot)
257-
changed = True
258-
for column in range(pivot + 1, columns):
259-
while matrix[pivot][column] != 0:
260-
quotient = matrix[pivot][column] // matrix[pivot][pivot]
261-
_add_column_multiple(matrix, column, pivot, -quotient)
262-
_add_column_multiple(right, column, pivot, -quotient)
263-
if matrix[pivot][column] != 0 and abs(matrix[pivot][column]) < abs(
264-
matrix[pivot][pivot]
265-
):
266-
_swap_columns(matrix, column, pivot)
267-
_swap_columns(right, column, pivot)
268-
changed = True
269-
offender = next(
270-
(
271-
(row, column)
272-
for row in range(pivot + 1, rows)
273-
for column in range(pivot + 1, columns)
274-
if matrix[row][column] % matrix[pivot][pivot]
275-
),
276-
None,
277-
)
278-
if offender is not None:
279-
_add_row_multiple(matrix, pivot, offender[0], 1)
280-
_add_row_multiple(left, pivot, offender[0], 1)
281-
changed = True
282-
continue
283-
if not changed:
284-
break
285-
if all(matrix[row][pivot] == 0 for row in range(pivot + 1, rows)) and all(
286-
matrix[pivot][column] == 0 for column in range(pivot + 1, columns)
287-
):
288-
break
289-
if matrix[pivot][pivot] < 0:
290-
_negate_row(matrix, pivot)
291-
_negate_row(left, pivot)
211+
# smith_normal_decomp accepts a plain SymPy Matrix and handles every shape,
212+
# including 0xm and nx0 matrices, returning identity transformations for
213+
# the empty side.
214+
if rows and columns:
215+
sympy_source = sympy.Matrix([[int(value) for value in row] for row in original])
216+
else:
217+
sympy_source = sympy.Matrix(rows, columns, [])
218+
219+
diagonal, left, right = smith_normal_decomp(sympy_source, domain=sympy.ZZ)
292220

221+
diagonal_matrix = [
222+
[int(diagonal[row, column]) for column in range(columns)] for row in range(rows)
223+
]
224+
left_matrix = [
225+
[int(left[row, column]) for column in range(rows)] for row in range(rows)
226+
]
227+
right_matrix = [
228+
[int(right[row, column]) for column in range(columns)] for row in range(columns)
229+
]
230+
231+
diagonal_count = min(rows, columns)
293232
factors = tuple(
294-
matrix[index][index]
233+
diagonal_matrix[index][index]
295234
for index in range(diagonal_count)
296-
if matrix[index][index] != 0
235+
if diagonal_matrix[index][index] != 0
297236
)
298237
if any(value <= 0 for value in factors) or any(
299238
right_factor % left_factor for left_factor, right_factor in pairwise(factors)
300239
):
301240
raise ArithmeticError("Smith reduction did not produce a canonical diagonal")
302-
if matrix_multiply(matrix_multiply(left, original), right) != matrix:
241+
if (
242+
matrix_multiply(matrix_multiply(left_matrix, original), right_matrix)
243+
!= diagonal_matrix
244+
):
303245
raise ArithmeticError("Smith transformations do not bind the source")
304-
left_determinant = determinant(left)
305-
right_determinant = determinant(right)
246+
left_determinant = determinant(left_matrix)
247+
right_determinant = determinant(right_matrix)
306248
if abs(left_determinant) != 1 or abs(right_determinant) != 1:
307249
raise ArithmeticError("Smith transformations are not unimodular")
308250
return SmithReduction(
309251
source=original,
310-
diagonal=matrix,
311-
left=left,
312-
right=right,
252+
diagonal=diagonal_matrix,
253+
left=left_matrix,
254+
right=right_matrix,
313255
rank=len(factors),
314256
invariant_factors=factors,
315257
left_determinant=left_determinant,

src/jacobian/domains/certified_snf/bundle.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from jacobian.domains.certified_snf.checkers import CERTIFIED_SNF_EXACT_REPLAY_CHECKERS
55
from jacobian.domains.certified_snf.operations import CERTIFIED_SNF_CAPABILITIES
66
from jacobian.operations import DomainBundle, DomainDiagnostics, DomainSemantics
7-
from jacobian.provider_runtime import known_provider_runtime
7+
from jacobian.provider_runtime import SYMPY_VERSION, known_provider_runtime
88

99

1010
def build_certified_snf_bundle() -> DomainBundle:
@@ -31,17 +31,16 @@ def build_certified_snf_bundle() -> DomainBundle:
3131
},
3232
),
3333
provider_runtime=known_provider_runtime(
34-
"jacobian.certified-snf",
34+
"jacobian.sympy",
3535
features=(
3636
"exact-integer",
37-
"elementary-row-operations",
38-
"elementary-column-operations",
37+
"sympy-smith-normal-decomposition",
3938
"left-unimodular-transformation",
4039
"right-unimodular-transformation",
4140
"smith-divisibility-chain",
4241
),
4342
),
44-
backend_version="jacobian.certified-snf/1",
43+
backend_version=SYMPY_VERSION,
4544
capabilities=CERTIFIED_SNF_CAPABILITIES,
4645
diagnostics=DomainDiagnostics(
4746
invalid_request=CapabilityDiagnostic(
@@ -56,12 +55,15 @@ def build_certified_snf_bundle() -> DomainBundle:
5655
),
5756
scope_description="the complete supplied bounded integer matrix",
5857
completeness_basis=(
59-
"elementary row and column operations produced both full basis changes "
60-
"and the complete canonical Smith diagonal"
58+
"SymPy's smith_normal_decomp over ZZ produced both full unimodular "
59+
"basis changes and the complete canonical Smith diagonal; the producer "
60+
"fail-closed checks verified D = U A V, both unimodular determinants, "
61+
"and the positive divisibility chain"
6162
),
6263
assurance_basis=(
63-
"exact integer computation capped at COMPUTED; independent certificate "
64-
"replay is available through matrix.normal_form.smith.certified.verify"
64+
"exact SymPy smith_normal_decomp capped at COMPUTED; independent "
65+
"certificate replay is available through "
66+
"matrix.normal_form.smith.certified.verify"
6567
),
6668
checker_declarations=CERTIFIED_SNF_EXACT_REPLAY_CHECKERS,
6769
)

src/jacobian/domains/certified_snf/checkers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
entrypoint_module="jacobian_checkers.certified_snf",
1313
replay_method="independent Smith transformation-certificate replay",
1414
reason=(
15-
"operator-authorized standard-library checker validates D=UAV, both "
15+
"operator-authorized independent checker validates D=UAV, both "
1616
"unimodular determinants, and the complete canonical divisibility chain"
1717
),
1818
verification_capability_id="matrix.normal_form.smith.certified.verify",

src/jacobian/domains/certified_snf/operations.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def _certified_smith(
6363
},
6464
),
6565
),
66-
version="3",
66+
version="4",
6767
),
6868
)
6969

0 commit comments

Comments
 (0)