Rework sympy to pymbolic mappers - #305
Conversation
|
Hmm.. this needs a bit more work. It needs to make a distinction between |
ebf1188 to
3d633e9
Compare
alexfikl
left a comment
There was a problem hiding this comment.
Worth noting: this is pretty breaking because PymbolicToSympy just does sympy now. From what I can tell, these are undocumented classes that are not used by pytential or anyone.. so not a big deal? 😁
| symbols: bool | ||
|
|
||
| def __init__(self, *, symbols: bool = False) -> None: | ||
| self.symbols = symbols |
There was a problem hiding this comment.
This also adds a little attribute to the mapper instead of having another mapper altogether. Good idea? Seemed cleaner..
There was a problem hiding this comment.
It's not well-explained. I have a vague sense of what it does, but that's based on Ctrl+F. Add a docstring?
There was a problem hiding this comment.
| if not self.symbols: | ||
| return super().map_ImaginaryUnit(expr) | ||
|
|
||
| return prim.Variable("I") |
There was a problem hiding this comment.
I don't think this is used anywhere (will be in #279), but it didn't roundtrip I correctly at all before.
There was a problem hiding this comment.
I'm not sure? This tries to roundtrip with SympyToPymbolic mostly + places like HelmholtzKernel use var("I")..
There was a problem hiding this comment.
🟡 Changes recommended
The SymEngine symbols=True conversion path does not currently map the imaginary unit I as expected, which can break the new roundtrip behavior covered by tests.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR decouples symbolic↔pymbolic conversion from the active backend (USE_SYMENGINE) by implementing explicit sympy and SymEngine mapper variants and introducing to_pymbolic / to_symbolic (and to_symbolic_expr) helpers that dispatch appropriately. This allows converting expressions for either backend even when the other backend is selected at runtime.
Changes:
- Add
to_pymbolic,to_symbolic, andto_symbolic_exprhelpers and refactor mapper implementations insumpy/symbolic.py. - Refactor call sites across the codebase to use the new helper functions instead of directly instantiating backend-specific mappers.
- Extend roundtrip tests to cover additional known symbols (notably the imaginary unit
I).
File summaries
| File | Description |
|---|---|
| sumpy/tools.py | Switch kernel scaling constant conversion to sym.to_pymbolic. |
| sumpy/test/test_misc.py | Update symbolic roundtrip test to use helpers and include I. |
| sumpy/test/test_codegen.py | Use to_pymbolic helper for sym→pymbolic conversion in codegen test. |
| sumpy/symbolic.py | Introduce helper dispatch + implement both sympy and SymEngine mapper variants. |
| sumpy/qbx.py | Use sym.to_symbolic_expr for strengths conversion. |
| sumpy/kernel.py | Use sym.to_symbolic_expr(..., symbols=True) for expression/scaling conversion. |
| sumpy/e2p.py | Use to_pymbolic helper for kernel scaling conversion. |
| sumpy/codegen.py | Remove local sym→pymbolic mapper and rely on sym.to_pymbolic. |
| .basedpyright/baseline.json | Update pyright baseline to reflect new/changed typing diagnostics. |
Review details
- Files reviewed: 9/9 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
3d633e9 to
f4c6f95
Compare
inducer
left a comment
There was a problem hiding this comment.
Thanks! A few comments/questions below.
| symbols: bool | ||
|
|
||
| def __init__(self, *, symbols: bool = False) -> None: | ||
| self.symbols = symbols |
There was a problem hiding this comment.
It's not well-explained. I have a vague sense of what it does, but that's based on Ctrl+F. Add a docstring?
| symbols: bool | ||
|
|
||
| def __init__(self, *, symbols: bool = False) -> None: | ||
| self.symbols = symbols |
There was a problem hiding this comment.
Shouldn't it be declared in the mixin instead?
There was a problem hiding this comment.
Sure. I didn't put it in there because in my head mixins don't carry state, but I guess it doesn't matter that much. pyright is also very upset by the mixin 😢
There was a problem hiding this comment.
Well, if methods in the mixin rely on a bit of data, the same class should declare what it's supposed to be. (IMO)
There was a problem hiding this comment.
| symbols: bool | ||
|
|
||
| def __init__(self, *, symbols: bool = False) -> None: | ||
| self.symbols = symbols |
There was a problem hiding this comment.
Is symbols=False ever needed really?
There was a problem hiding this comment.
I think at least some of the codegen needs it so that things like var("pi") from the kernels get transformed into numbers (which is what happens with symbols=False). Not sure this is the best place to do that..
| if not self.symbols: | ||
| return super().map_ImaginaryUnit(expr) | ||
|
|
||
| return prim.Variable("I") |
f4c6f95 to
eee22cd
Compare
eee22cd to
e787c4d
Compare
The mappers previously inherited from either the Sympy or SymEngine variants based on
USE_SYMENGINE. This meant that you couldn't use them at all for the other one, e.g. ifUSE_SYMENGINE=Truethere was no way to convertsympyexpressions.This implements both variants (with some code duplication) and adds
to_pymbolicandto_symbolichelper functions that do the actual dispatch based onUSE_SYMENGINE.