Repository navigation
Emit SymPy code that runs (#985) - #1001
Merged
Merged
Conversation
ToSympyCode is documented as producing code you can run in SymPy. For every
set, every lambda, every piecewise and every non-vector matrix, it did not.
Three separate faults, none of which the tests could see:
Unqualified names. The preamble is `import sympy` and nothing else, so
FiniteSet, Interval, Union, Intersection, Complement, ConditionSet and S
were all NameError.
Parts interpolated rather than exported. Interval, Piecewise and the
non-vector Matrix wrote their children with {Left} instead of
{Left.ToSymPy()}. A bare variable spells the same in both languages, so it
only shows once the part is a function -- sin(a), which Python has not got.
A lambda emitted no body at all: sympy.Lambda(x, ), a TypeError.
And a set builder threw AngouriBugException out of the exporter, because
its Codomain is Domain.Any and SpecialSet.Create has no member for it.
SymPy names that set -- S.UniversalSet is the one it prints ConditionSet
without a third argument for, which is what "no restriction beyond the
predicate" means here too.
`x in RR` changes shape rather than only qualification. Python's `in`
coerces its result to a bool, and a membership that is not decided is not
one: `x in sympy.S.Reals` raises "did not evaluate to a bool". `.contains`
answers with the condition, and still answers True or False where it can.
Why the tests passed: they asserted substring containment, which holds
whether a part was exported or interpolated, and holds on a program that
does not run at all. The new cases pin the whole emitted expression, and
one asserts every SymPy name carries its qualifier.
Measured with work/sympycheck, which executes the generated program rather
than reading it, and whose corpus this extends from 24 cases to 45: 43 run,
0 inexact. The two that do not are the set builders, whose preamble still
declares the %1 placeholder of #989; their expr line is correct here.
Suite 7385 passed, 0 failed. Corpus unchanged at 116/119 with 0 wrong.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KbKcbJP266A3EGyQ5kq7Ru
Member
Author
|
Composition with #1000 is measured, not assumed. Built a local integration branch of the two and ran both gates: The two set builders that still fail on this branch alone are fixed by that combination and nothing else — their # this PR alone
import sympy
%1 = sympy.Symbol('%1') # <- #989's leak, a SyntaxError
expr = sympy.ConditionSet(x, x > 0, sympy.S.UniversalSet) # <- correct
# with #1000
import sympy
x = sympy.Symbol('x')
expr = sympy.ConditionSet(x, x > 0, sympy.S.UniversalSet) # runsThe only merge conflict between them is |
This was referenced Aug 22, 2026
…ns-985 # Conflicts: # BREAKING-CHANGES.md
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.
Closes #985 — and the two defects it names turned out to be four, across every set export.
The defect
MathS.ToSympyCodeis documented as producing code you can run in SymPy. For every set, every lambda, everypiecewiseand every non-vector matrix, it did not. Run against SymPy 1.14:Three separate faults, plus the exception #985 reported:
Unqualified names. The preamble is
import sympyand nothing else, soFiniteSet,Interval,Union,Intersection,Complement,ConditionSetandSwere every one of them aNameError. #985 named the lambda and the set builder; this is the rest of the file.Parts interpolated rather than exported.
Interval,Piecewiseand the non-vectorMatrixwrote their children as{Left}instead of{Left.ToSymPy()}. A bare variable spells the same in both languages, which is why it went unnoticed — it only shows once the part is a function:A lambda emitted no body at all —
sympy.Lambda(x, ).A set builder threw.
ConditionalSet.CodomainisDomain.Any, whichSpecialSet.Createhas no member for, so the exporter's cast raisedAngouriBugException. SymPy names that set:S.UniversalSetis the one it printsConditionSetwithout a third argument for, which is what "no restriction beyond the predicate" means here too.x in RRchanges shape, not just qualificationPython's
incoerces its result to abool, and a membership that is not decided is not one:.containsanswers with the condition and still answersTrue/Falsewhere it can, so that is what is emitted now.Why the tests passed
They assert substring containment.
Assert.Contains("Piecewise((a, b), (c, d))")holds whether the parts were exported or merely interpolated — and holds on a program that does not run at all. Two of the existing cases were recording the broken output outright ("a in B"→"a in B", andConditionSet(x, x > 0, S.Reals)unqualified); both are updated.The new cases pin the whole emitted expression with
Assert.Equal, and one asserts every SymPy name carries its qualifier.Measured by running it
work/sympycheckin the analysis workspace executes the generated program rather than reading it — it exists because #909 and #911 were both invisible to string tests. Its corpus stopped at numbers; I extended it from 24 cases to 45 to reach sets, binders, piecewise and matrices.masterThe two outstanding are the set builders, and they are not this PR's half: their
exprline is correct here, but the preamble still declares%1 = sympy.Symbol('%1')— the placeholder leak of #989, fixed in #1000, and aSyntaxErrorwhatever the body says. I built a local integration branch of both and ran it: 45 of 45, 0 inexact, suite 7395 passed 0 failed.Also: suite 7385 passed, 0 failed on this branch alone; corpus 116/119, 0 wrong, with no case's verdict or answer changed.
Against the other open PRs
Derived with
git merge-tree --write-tree:BREAKING-CHANGES.mdonlyNo source conflicts with any of them — this touches
ToSympy.Omni.Classes.cs, which none of the others do. Whichever merges last takes the mechanical round and I will do it.🤖 Generated with Claude Code
https://claude.ai/code/session_01KbKcbJP266A3EGyQ5kq7Ru