Repository navigation
Exact Math models, per-call UFs for transcendental functions, bounded queries over them - #80
Closed
nils-loose wants to merge 5 commits into
Closed
nils-loose wants to merge 5 commits into
nils-loose wants to merge 5 commits into
Conversation
Math.round rounded ties to even (round(2.5) = 2), but the JDK rounds them toward positive infinity. It is now floor(x) plus one if x - floor(x) >= 0.5, which is exact. max/min of floats and doubles used SMT-LIB's fp.max/fp.min, which return the other argument for one NaN and either zero for +0.0/-0.0; they now follow the JDK's definition. signum is new, as are floorDiv and floorMod of ints and longs for a concrete non-zero divisor (a symbolic one would need a branch for the ArithmeticException, so it stays unmodelled). MathInvocationSpec checks every model against the JDK on edge values (NaN, zeros, ties, infinities, saturation, MIN_VALUE). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each call of a sampled Math function (now including sin and cos) is an application of its own function math_<name>_<n>, defined by a trace constraint as the sample table: exact at the samples, the concrete result elsewhere. Before, the table was inlined and every call recorded precision loss; now precision loss is recorded where it matters, when a branch constraint contains such a function (DTOBuilder's UF check), so a Math result that never reaches a branch no longer rules out SAFE. The function names also let the explorer tell which queries depend on a model. Every call has its own function, so the definitions never constrain each other or the inputs: each can be satisfied by its function value. SinCosUF is removed. It restricted the argument of every symbolic sin/cos call to its sample inputs through a trace-global constraint, which also held for branches before the call; with the result reaching no branch, no precision loss was recorded, and a feasible branch could be reported unreachable: x in (0.7, 0.8) followed by an unused Math.sin(x) gave SAFE. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A query now carries only the UF definitions whose functions it (or a definition it needs) contains, plus constraints without functions. The left-out ones only constrain functions that occur nowhere else in the query; as every Math model call has its own function, each of them can be satisfied whatever the inputs, so satisfiability does not change. A query over a sampled Math function can only give a guess, and its trace is precision loss, so it cannot end in SAFE anyway. In sv-comp mode such a query gets at most --approximate-rlimit (default 4e8, one to two minutes); running out of it gives up on the branch (UNKNOWN) instead of retrying. This bounds the runs that used to end in a quick UNKNOWN with the Math calls concretised and now spent the whole CPU limit in one query. A solution counts as a guess (an expected divergence) if its query depends on a Math model or its trace used an approximate model, as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A model of a query over a Math model function also interprets the function (math_tan_3); reading it as an input value failed with an unsupported prefix and stopped the exploration. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nils-loose
added a commit
that referenced
this pull request
Oct 19, 2026
SinCosUF.java: deleted as in #80 (its trace-global sample constraint gave a false SAFE); #71's eager precision loss there goes with it. StrategyService.py: solve_branch takes both #74's extra_exprs and #80's approximate_rlimit, on #80's sliced query. SVCompDriver.py: retries (#74) and first queries both get the approximate-query cap (#80); a solution keeps #74's record and #80's query-based guess flag. SymbolicExplorer.py: both sets of options. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nils-loose
added a commit
that referenced
this pull request
Oct 19, 2026
Combining #71 and #80: each per-call math_<f>_<n> function is declared APPROXIMATE_MODEL_FUNCTION in the SymbolRegistry, so a branch over one is a precision loss by kind, not only as an undeclared symbol. The registry test builds its function through SampledMath, as SinCosUF is gone, and the divergence tests pass #80's approximate_rlimit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nils-loose
added a commit
that referenced
this pull request
Oct 19, 2026
_extract_variables: one copy (#82 cherry-picks #72's patch). StrategyService.py, Database.py: #82 walks the Database's tree under the lock; visit_tree passes the solved and #74's retired branches, in #72's 'with lock' style. SVCompDriver.py: #82's shallow snapshot of the target node for check_divergence, with #74's solution record and #80's guess flag; the ingest-failure (#72) and stack-overflow (#78) checks read the Database without copying the tree. DTOBuilder.java: #82's FormulaDumper with #71's SymbolRegistry check. DFS.py: #82 logs the per-node line only with an SA graph. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merged
Contributor
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changes and why
Exact Math models (
MathInvocation, checked against the JDK inMathInvocationSpec):Math.roundrounded ties to even (round(2.5) = 2), but the JDK rounds them toward positive infinity. It is nowfloor(x)plus one ifx - floor(x) >= 0.5, which is exact.max/minof floats and doubles used SMT-LIBfp.max/fp.min. Those return the other argument when one argument is NaN, and either zero for +0.0/-0.0. Both now follow the JDK definition.signum, andfloorDiv/floorModfor int/long with a concrete non-zero divisor. A symbolic divisor would need a branch for the ArithmeticException, so it stays unmodelled (context loss).Transcendental functions are per-call UFs (
SampledMath, now including sin/cos):math_<name>_<n>(x).SinCosUFis removed. Its trace-global constraint "the argument is one of the samples" also applied to branches before the call, and gave a false SAFE. Reproducer:if (x > 0.7 && x < 0.8) assert false; Math.sin(x);gives SAFE on the integration branch and VIOLATION here.Explorer:
math_function can only give a guess, and its trace is already precision loss. In sv-comp mode it gets at most--approximate-rlimit(default 2.5e8, about 1-1.5 min). If it runs out, the explorer gives up on that branch (UNKNOWN) and does not escalate the limit.Range axioms were not added. The table definition fixes the value at every argument, so axioms add no information. A free UF limited only by range axioms would give the solver guesses that diverge almost every time.
Measured
Competition config (nosa, 300 s, sv-comp.cfg, 15 GB), compared with
baseline-results.jsonl. 437 runs:Tests
MathInvocationSpec: round, max/min, signum, floorDiv/floorMod against the JDK on edge values. It fails on the old models.tests/test_math_models.py: slicing, approximate-query cap, model extraction.gradlew testand all explorer unit tests pass.Known limits
--approximate-rlimitis a trade-off: a higher cap recovers violations behind hard queries (coral21) but brings back long runs.🤖 Generated with Claude Code