Repository navigation
Model number-to-string conversions with a radix, fix toBinaryString divergence - #76
Closed
nils-loose wants to merge 6 commits into
Closed
nils-loose wants to merge 6 commits into
nils-loose wants to merge 6 commits into
Conversation
…d's values Same infrastructure as the object-array index search: a branch the executor records itself, flipped by the explorer like any other, with no counterpart in the static pre-analysis graph. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The digits of a constant radix up to 10 are encoded as str.from_int of the integer with the same decimal digits, which Z3 decides directly; the per-digit concatenation cut to a symbolic length timed out or gave models that violate the constraint (Integer.toBinaryString diverged). toBinaryString and toOctalString now use the same encoding. A symbolic radix is pinned to its concrete value by choice branches (binary search over 2..36 on the radix after the JDK's fallback to 10), so the conversion is exact on each path with a constant radix. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LongValue/ShortValue.asStringValue used str.from_int of the signed value, which is "" for a negative number; StringBuilder.append(long) and string concatenation of a long or short now get the '-' and the digits of the magnitude. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
String.valueOf(float/double), StringBuilder.append(F/D) and string concatenation threw NotImplementedException, which ended the run even for a concrete value. The string is now the concrete one; a symbolic value records context loss, as no exact encoding of the shortest decimal representation is practical. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A choice branch enumerates another value of an operand (e.g. another radix) and restarts from there; the program's own branches on the path come first, in DFS order otherwise. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every radix 2..36 and radixes outside that range on edge values for Integer/Long toString and toUnsignedString, the one-argument variants, the string of numbers in concatenation, and a forked-agent test of the radix choice branches. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nils-loose
added a commit
that referenced
this pull request
Oct 19, 2026
Combining #72, #74, #78, #82 and #88: the driver tests create #88's budget and verdict lock, and #88's tests get the stack-overflow (#78) and copy-free (#82) Database reads and the choice flag (#76). A run reports one verdict (#88), so the incomplete-search subtests reset the flag. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nils-loose
added a commit
that referenced
this pull request
Oct 19, 2026
Tree.py: #84's loop-state positions are registered at the start of #72's iterative Tree.add; the recursive add_recursive is not brought back. Node/Branch/TraceParser/DataTransferObjects.py, BranchDTO/DTOBuilder.java: both the choice flag (#69/#76) and the loop states (#84). SymbolicTraceHandler.java: addChoiceBranch, checkAndSetLibraryBranch and the loop-state collection side by side. StrategyService.py: the walk on the live tree (#82) reports #84's count of pruned subtrees. SVCompDriver.py: #84's --no-loop-state-matching next to the other state. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nils-loose
added a commit
that referenced
this pull request
Oct 19, 2026
…tates to every branch Combining #72, #82 and #84: - #84 pruned a subtree below a repeated loop-head state by returning from the recursive dfs. In #72's iterative walk that return ended the whole walk, so the open branches of every later subtree were never offered, and an exhausted search could report SAFE. It now skips that subtree only (regression test added). - The pruned-subtree count is reset for each walk, as #82 walks the persistent tree instead of a copy. - The choice branches (#69/#76) and library-call branches (#75/#90) take the pending loop-head states too, so a state always goes with the branch right after it, which its position assumes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Stacked on #70 (
feat/bitop-models); base is that branch.What changes
Integer/LongtoString(x, radix)andtoUnsignedString(x, radix)are modelled exactly (newNumberStrings).str.from_intof the integer that has the same decimal digits. Power-of-two digits are bit fields; the other radixes use a chain of divisions by the constant radix.iteover the radixes). It is exact too, but no Z3 query on it finished within 90 s, not even for radix 10.toBinaryString/toOctalStringdivergence (Exact Integer/Long/Double bit-op models and radix-2^k string conversions #70): these now use the samestr.from_intencoding.str.from_codestrings, cut to a symbolic length. On it Z3 4.14 returns models that violate the constraint, or needs 13 to 64 s.from_intencoding, the same query (toBinaryString(x) == "11000111010111") takes 0.03 s.Long.toBinaryStringno longer times out.invokedynamicmakeConcatWithConstants(DynamicInvocation) andStringBuilder.append(I/J/...)both go throughValue.asStringValue().IntValuewas already exact.LongValueandShortValueusedstr.from_intof the signed value, which is""for a negative number; they now useNumberStrings.decimal.StringBuilderValue,StringValueorStringInvocation.asStringValuereturns the concreteString.valueOfstring. Before, it threwNotImplementedException, which ended the run even for concrete values (e.g. the"pi2_hi = " + pi2_hiin theMathSinconstructor). A symbolic value records context loss. I found no exact encoding of the shortest decimal representation that would cover a useful range, soFloat/Double.toStringstay unmodelled (context loss as before).BranchElement/BranchDTO/SymbolicTraceHandler.addChoiceBranch, the explorer'schoiceflag and the SA-walk masking), so the two PRs merge cleanly.SVCompDriver.solve_candidatesnow solves choice branches after the program's own branches (a stable sort, so DFS order otherwise). Without this, the explorer enumerated all radixes before it tried theequalsbranch, and the radix tasks timed out.Measured
Config:
--sv-comp, 300 s, 15 GB, no SA. 333 runs:context-loss-number-to-string,fp-to-string-not-implemented);feat/bitop-models(#70)Flips over #70 (all UNKNOWN or timeout to correct):
The other 6 runs of the +17 come from #70 itself (toHexString, toOctalString, toUnsignedString(x)).
Tests
NumberStringsSpec:Integer/LongtoString/toUnsignedStringagainst the JDK on edge values (MIN, MAX, 0, -1, ...), for every radix 2..36 and for -1, 0, 1, 37, MIN and MAX;Short/Byte.toString;RadixChoiceAgentSpec(forked agent) checks the choice branches for radixes 36, 0 and 2.test_solver_rlimithas a new case for the branch order.test: 8819 tests, 0 failures.Known limits
🤖 Generated with Claude Code