Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions src/jacobian/contracts/certified_snf.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from pydantic import Field, StrictInt, model_validator

from jacobian.canonical import parse_canonical_integer
from jacobian.contracts.exact import CanonicalInteger
from jacobian.contracts.results import ContractModel

Expand Down Expand Up @@ -115,16 +116,19 @@ def require_coherent_shapes_and_canonical_diagonal(self) -> Self:
raise ValueError("Smith certificate matrix shapes are incompatible")
diagonal_count = min(rows, columns)
diagonal = tuple(
int(self.diagonal.entries[index][index]) for index in range(diagonal_count)
parse_canonical_integer(self.diagonal.entries[index][index])
for index in range(diagonal_count)
)
if any(
int(self.diagonal.entries[row][column]) != 0
parse_canonical_integer(self.diagonal.entries[row][column]) != 0
for row in range(rows)
for column in range(columns)
if row != column
):
raise ValueError("Smith normal form must be diagonal")
factors = tuple(int(value) for value in self.invariant_factors)
factors = tuple(
parse_canonical_integer(value) for value in self.invariant_factors
)
if (
self.rank != len(factors)
or self.rank > diagonal_count
Expand Down
3 changes: 2 additions & 1 deletion src/jacobian/contracts/combinatorics.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
CanonicalLimits,
canonicalize_json,
format_canonical_integer,
parse_canonical_integer,
)
from jacobian.contracts.exact import CanonicalInteger, CanonicalRational
from jacobian.contracts.results import ContractModel
Expand Down Expand Up @@ -629,7 +630,7 @@ class IntegerListRequest(ContractModel):

@model_validator(mode="after")
def require_nonnegative_parts(self) -> Self:
if any(int(v) < 0 for v in self.values):
if any(parse_canonical_integer(value) < 0 for value in self.values):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Bound multinomial parts before accepting large values

For a multinomial request containing the newly tested 5,001-digit value, this validator now succeeds, but its only consumer passes the parsed value to math.factorial in domains/combinatorics/operations.py:61-62; CPython immediately raises OverflowError once an argument exceeds sys.maxsize. Thus a request accepted by the capability contract still ends in an execution error. Add an explicit supported-magnitude/resource bound here rather than treating every canonical integer as computable.

AGENTS.md reference: AGENTS.md:L16-L17

Useful? React with 👍 / 👎.

raise ValueError("integer list values must be nonnegative")
return self

Expand Down
7 changes: 5 additions & 2 deletions src/jacobian/contracts/matrix_operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from pydantic import Field, StrictInt, field_validator, model_validator

from jacobian.canonical import parse_canonical_integer
from jacobian.contracts.exact import CanonicalInteger, CanonicalRational
from jacobian.contracts.results import ContractModel

Expand Down Expand Up @@ -244,15 +245,17 @@ def require_invariant_factor_chain(self) -> Self:
columns = len(self.normal_form.entries[0])
if self.rank > min(rows, columns):
raise ValueError("Smith rank cannot exceed the matrix dimensions")
factors = tuple(int(value) for value in self.invariant_factors)
factors = tuple(
parse_canonical_integer(value) for value in self.invariant_factors
)
if any(value <= 0 for value in factors):
raise ValueError("Smith invariant factors must be positive")
if any(right % left != 0 for left, right in pairwise(factors)):
raise ValueError("each Smith invariant factor must divide the next")
for row, entries in enumerate(self.normal_form.entries):
for column, value in enumerate(entries):
expected = factors[row] if row == column and row < self.rank else 0
if int(value) != expected:
if parse_canonical_integer(value) != expected:
raise ValueError(
"Smith normal form must contain its positive invariant "
"factors on the leading diagonal and zero elsewhere"
Expand Down
13 changes: 11 additions & 2 deletions src/jacobian/contracts/plugin_matrices.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,17 @@

from pydantic import Field, StrictInt, StrictStr, model_validator

from jacobian.canonical import parse_canonical_integer
from jacobian.contracts.claims import flatten_claim_spec
from jacobian.contracts.exact import CanonicalInteger
from jacobian.contracts.plugin_protocol import PluginRequestContext
from jacobian.contracts.results import ContractModel


def _matrix_integer(value: int | str) -> int:
return value if isinstance(value, int) else parse_canonical_integer(value)


class MatrixScope(ContractModel):
rows: StrictInt = Field(ge=1, le=32)
cols: StrictInt = Field(ge=1, le=32)
Expand Down Expand Up @@ -60,8 +65,12 @@ def validate_for_claim(self, claim: MatrixClaim) -> None:
assert claim.scope is not None
if self.rows != claim.scope.rows or self.cols != claim.scope.cols:
raise ValueError("candidate dimensions do not match claim scope")
allowed = {int(value) for value in claim.scope.entries}
if any(int(entry) not in allowed for row in self.entries for entry in row):
allowed = {_matrix_integer(value) for value in claim.scope.entries}
if any(
_matrix_integer(entry) not in allowed
for row in self.entries
for entry in row
):
raise ValueError("candidate entry is outside claim scope")


Expand Down
8 changes: 6 additions & 2 deletions src/jacobian/contracts/projective_geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from pydantic import Field, StringConstraints, model_validator

from jacobian.canonical import format_canonical_integer, parse_canonical_integer
from jacobian.contracts.exact import CanonicalRational
from jacobian.contracts.results import ContractModel

Expand Down Expand Up @@ -94,10 +95,13 @@ class PrimitiveProjectiveTriple(ContractModel):
@model_validator(mode="after")
def require_canonical_primitive_coordinates(self) -> Self:
try:
values = tuple(int(value) for value in self.coordinates)
values = tuple(parse_canonical_integer(value) for value in self.coordinates)
except ValueError as exc:
raise ValueError("projective coordinates must be integer strings") from exc
if tuple(str(value) for value in values) != self.coordinates:
if (
tuple(format_canonical_integer(value) for value in values)
!= self.coordinates
):
raise ValueError("projective coordinates must be canonical integer strings")
divisor = 0
for value in values:
Expand Down
25 changes: 12 additions & 13 deletions src/jacobian/domains/arithmetic/operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from fractions import Fraction
from typing import Literal

from jacobian.canonical import format_canonical_integer
from jacobian.canonical import format_canonical_integer, parse_canonical_integer
from jacobian.contracts.arithmetic import (
IntegerBaseDigitsRequest,
IntegerBaseDigitsResult,
Expand All @@ -39,16 +39,11 @@


def _int(value: str) -> int:
return int(value)
return parse_canonical_integer(value)


def _canonical(value: int) -> str:
return str(value)


def to_fraction(num: str, den: str) -> Fraction:
"""Build a reduced ``Fraction`` from canonical integer strings."""
return Fraction(int(num), int(den))
return format_canonical_integer(value)


def absolute_value(request: IntegerValueRequest) -> IntegerValueResult:
Expand All @@ -70,12 +65,12 @@ def sign(request: IntegerValueRequest) -> IntegerSignResult:

def decimal_digit_sum(request: IntegerValueRequest) -> IntegerValueResult:
return IntegerValueResult(
value=_canonical(sum(int(digit) for digit in str(abs(_int(request.value)))))
value=_canonical(sum(int(digit) for digit in request.value.lstrip("-")))
)


def decimal_digit_count(request: IntegerValueRequest) -> IntegerValueResult:
return IntegerValueResult(value=_canonical(len(str(abs(_int(request.value))))))
return IntegerValueResult(value=_canonical(len(request.value.lstrip("-"))))


def base_digits(request: IntegerBaseDigitsRequest) -> IntegerBaseDigitsResult:
Expand Down Expand Up @@ -181,11 +176,15 @@ def maximum(request: RationalPairRequest) -> RationalValueResult:


def floor(request: RationalValueRequest) -> RationalIntegerResult:
return RationalIntegerResult(value=str(math.floor(_fraction(request.value))))
return RationalIntegerResult(
value=format_canonical_integer(math.floor(_fraction(request.value)))
)


def ceiling(request: RationalValueRequest) -> RationalIntegerResult:
return RationalIntegerResult(value=str(math.ceil(_fraction(request.value))))
return RationalIntegerResult(
value=format_canonical_integer(math.ceil(_fraction(request.value)))
)


def continued_fraction(
Expand All @@ -197,7 +196,7 @@ def continued_fraction(
value = _fraction(request.value)
terms = sympy_continued_fraction(SympyRational(value.numerator, value.denominator))
return RationalContinuedFractionResult(
terms=tuple(str(int(term)) for term in terms)
terms=tuple(format_canonical_integer(int(term)) for term in terms)
)


Expand Down
6 changes: 3 additions & 3 deletions src/jacobian/domains/combinatorics/operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from functools import reduce
from operator import mul

from jacobian.canonical import format_canonical_integer
from jacobian.canonical import format_canonical_integer, parse_canonical_integer
from jacobian.contracts.combinatorics import (
FibonacciPairRequest,
FibonacciPairResult,
Expand All @@ -21,7 +21,7 @@


def _integer_result(value: int) -> IntegerResult:
return IntegerResult(value=str(int(value)))
return IntegerResult(value=format_canonical_integer(value))


def factorial(request: NonnegativeIntegerRequest) -> IntegerResult:
Expand Down Expand Up @@ -57,7 +57,7 @@ def binomial(request: NonnegativePairRequest) -> IntegerResult:
def multinomial(request: IntegerListRequest) -> IntegerResult:
import math

values = [int(v) for v in request.values]
values = [parse_canonical_integer(value) for value in request.values]
numerator = math.factorial(sum(values))
denominator = reduce(mul, (math.factorial(v) for v in values), 1)
return _integer_result(numerator // denominator)
Expand Down
33 changes: 25 additions & 8 deletions src/jacobian/domains/matrix_lattice/operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from fractions import Fraction
from typing import Any

from jacobian.canonical import format_canonical_integer
from jacobian.canonical import format_canonical_integer, parse_canonical_integer
from jacobian.contracts.matrix_operations import (
CharacteristicPolynomialResult,
IntegerMatrixRequest,
Expand Down Expand Up @@ -115,7 +115,10 @@ def compute_smith_normal_form(
from sympy.matrices.normalforms import smith_normal_form

source = sympy.Matrix(
[[int(value) for value in row] for row in request.matrix.entries]
[
[parse_canonical_integer(value) for value in row]
for row in request.matrix.entries
]
)
raw = smith_normal_form(source, domain=sympy.ZZ)
diagonal_count = min(raw.rows, raw.cols)
Expand All @@ -133,20 +136,28 @@ def compute_smith_normal_form(
return SmithNormalFormResult(
normal_form=IntegerOutputMatrix(
entries=tuple(
tuple(str(int(canonical[row, column])) for column in range(raw.cols))
tuple(
format_canonical_integer(canonical[row, column])
for column in range(raw.cols)
)
for row in range(raw.rows)
)
),
rank=rank,
invariant_factors=tuple(str(value) for value in invariant_factors),
invariant_factors=tuple(
format_canonical_integer(value) for value in invariant_factors
),
)


def compute_inverse(request: SquareIntegerMatrixRequest) -> MatrixInverseResult:
import sympy

source = sympy.Matrix(
[[int(value) for value in row] for row in request.matrix.entries]
[
[parse_canonical_integer(value) for value in row]
for row in request.matrix.entries
]
)
inverse = native_matrices.inverse(source)
return MatrixInverseResult(
Expand All @@ -165,7 +176,9 @@ def compute_trace(request: SquareIntegerMatrixRequest) -> MatrixTraceResult:
source = sympy.Matrix(
[[int(value) for value in row] for row in request.matrix.entries]
)
return MatrixTraceResult(trace=str(int(native_matrices.trace(source))))
return MatrixTraceResult(
trace=format_canonical_integer(native_matrices.trace(source))
)


def compute_product(request: RationalMatrixProductRequest) -> MatrixProductResult:
Expand Down Expand Up @@ -204,7 +217,10 @@ def compute_adjugate(request: SquareIntegerMatrixRequest) -> MatrixAdjugateResul
import sympy

source = sympy.Matrix(
[[int(value) for value in row] for row in request.matrix.entries]
[
[parse_canonical_integer(value) for value in row]
for row in request.matrix.entries
]
)
if source.rows != source.cols:
raise ValueError("adjugate requires a square matrix")
Expand All @@ -213,7 +229,8 @@ def compute_adjugate(request: SquareIntegerMatrixRequest) -> MatrixAdjugateResul
adjugate=IntegerOutputMatrix(
entries=tuple(
tuple(
str(int(adjugate[row, column])) for column in range(adjugate.cols)
format_canonical_integer(adjugate[row, column])
for column in range(adjugate.cols)
)
for row in range(adjugate.rows)
)
Expand Down
28 changes: 19 additions & 9 deletions src/jacobian/domains/polynomial/elementary_operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from functools import cache
from typing import Any

from jacobian.canonical import format_canonical_integer, parse_canonical_integer
from jacobian.contracts.polynomial_operations import (
IntegerPolynomial,
IntegerPolynomialCompositionRequest,
Expand Down Expand Up @@ -45,7 +46,10 @@ def _integer_poly(polynomial: IntegerPolynomial) -> Any:
from sympy import Poly

return Poly.from_list(
[int(coefficient) for coefficient in polynomial.coefficients],
[
parse_canonical_integer(coefficient)
for coefficient in polynomial.coefficients
],
_x(),
domain="ZZ",
)
Expand All @@ -54,7 +58,8 @@ def _integer_poly(polynomial: IntegerPolynomial) -> Any:
def _integer_wire(polynomial: Any) -> IntegerPolynomial:
return IntegerPolynomial(
coefficients=tuple(
str(int(coefficient)) for coefficient in polynomial.all_coeffs()
format_canonical_integer(int(coefficient))
for coefficient in polynomial.all_coeffs()
)
)

Expand All @@ -67,17 +72,19 @@ def integer_polynomial_gcd(
gcd = left.gcd(right)
return IntegerPolynomialGcdResult(
gcd=_integer_wire(gcd),
left_content=str(int(left.content())),
right_content=str(int(right.content())),
gcd_content=str(int(gcd.content())),
left_content=format_canonical_integer(int(left.content())),
right_content=format_canonical_integer(int(right.content())),
gcd_content=format_canonical_integer(int(gcd.content())),
)


def integer_polynomial_content(
request: IntegerPolynomialRequest,
) -> IntegerPolynomialContentResult:
return IntegerPolynomialContentResult(
content=str(int(_integer_poly(request.polynomial).content()))
content=format_canonical_integer(
int(_integer_poly(request.polynomial).content())
)
)


Expand All @@ -88,7 +95,7 @@ def integer_polynomial_primitive_part(
content, primitive = source.primitive()
reconstructed = primitive.mul_ground(content)
return IntegerPolynomialPrimitivePartResult(
content=str(int(content)),
content=format_canonical_integer(int(content)),
primitive_part=_integer_wire(primitive),
reconstruction=_integer_wire(reconstructed),
)
Expand All @@ -97,9 +104,12 @@ def integer_polynomial_primitive_part(
def integer_polynomial_evaluate(
request: IntegerPolynomialEvaluationRequest,
) -> IntegerPolynomialEvaluationResult:
point = int(request.point)
point = parse_canonical_integer(request.point)
value = _integer_poly(request.polynomial).eval(point)
return IntegerPolynomialEvaluationResult(point=request.point, value=str(int(value)))
return IntegerPolynomialEvaluationResult(
point=request.point,
value=format_canonical_integer(int(value)),
)


def integer_polynomial_compose(
Expand Down
Loading
Loading