Closed hflemmen closed 11 months ago
Yeah, the behavior should roughly match the sympy equivalent (we should document this better). However, when SymForce is using the SymEngine symbolic API (the default), it calls symengine.solve
instead of sympy.solve
, and symengine.solve
does not accept a dict
argument.
If you want something in sympy.solve
that's not in symengine.solve
, you can either symforce.set_symbolic_api('sympy')
, or do something like:
import sympy as sm
import symforce.symbolic as sf
x, y, z = sf.symbols("x y z")
solutions = sm.solve([x**2 + y - 2*z, y + 4*z], x, y, dict=True)
print(solutions)
sf_solutions = [{sf.S(k): sf.S(v) for k, v in solution.items()} for solution in solutions]
to call sympy.solve
while SymForce is using the SymEngine symbolic API
Thank you, the explanation makes sense. The workaround works when I use simple symbols that already exists in sympy, but this workaround means that I can't use more complex symforce types?
E.g.
import symforce.symbolic as sf
import sympy as sm
R = sf.Rot2.symbolic("R")
solutions = sm.solve(R.to_tangent(epsilon=sf.epsilon())[0] - 0, R, dict=True)
print(solutions)
fails with the (shortened) error message:
SymPyDeprecationWarning:
The string fallback in sympify() is deprecated.
To explicitly convert the string form of an object, use
sympify(str(obj)). To add define sympify behavior on custom
objects, use sympy.core.sympify.converter or define obj._sympy_
(see the sympify() docstring).
sympify() performed the string fallback resulting in the following string:
'<Rot2 <C real=R_re, imag=R_im>>'
See https://docs.sympy.org/latest/explanation/active-deprecations.html#deprecated-sympify-string-fallback
for details.
This has been deprecated since SymPy version 1.6. It
will be removed in a future version of SymPy.
return list(map(sympify, w if iterable(w) else [w]))
ValueError: Error from parse_expr with transformed code: "<Symbol ('Rot2' )<Symbol ('C' )Symbol ('real' )=Symbol ('R_re' ),imag =Symbol ('R_im' )>>"
Is there a way to use sympys solve
with an expression including symforce types, or do I have to choose one of them? I.e. if I understand it correctly, I have two options: 1: I either can use symengine backend that allows complex types, but without the option to solve a set of coupled equations, or 2: I can use the sympy backend that does not support complex types but can solve a set of coupled equations. Is there a way to get both symforce types and a solve function that accepts the dict
argument?
You can at least do something like this, although it's a little cumbersome:
In [3]: solutions = sm.solve(R.to_tangent(epsilon=sf.epsilon())[0] - 0, R.to_storage(), dict=True)
In [4]: solutions
Out[4]: [{R_im: 0}]
Noting that I don't think the symengine API supports solve
with complex types either, e.g. I get this:
In [6]: solutions = sf.solve(R.to_tangent(epsilon=sf.epsilon())[0] - 0, R)
---------------------------------------------------------------------------
SympifyError Traceback (most recent call last)
Cell In[6], line 1
----> 1 solutions = sf.solve(R.to_tangent(epsilon=sf.epsilon())[0] - 0, R)
File ~/symforce/symforce/internal/symbolic.py:529, in solve(*args, **kwargs)
528 def solve(*args: T.Any, **kwargs: T.Any) -> T.List[Scalar]:
--> 529 solution = sympy.solve(*args, **kwargs)
530 from symengine.lib.symengine_wrapper import EmptySet as _EmptySet
532 if isinstance(solution, FiniteSet):
File symengine_wrapper.pyx:5402, in symengine.lib.symengine_wrapper.solve()
File symengine_wrapper.pyx:547, in symengine.lib.symengine_wrapper.sympify()
File symengine_wrapper.pyx:590, in symengine.lib.symengine_wrapper._sympify()
File symengine_wrapper.pyx:513, in symengine.lib.symengine_wrapper.sympy2symengine()
SympifyError: sympy2symengine: Cannot convert '<Rot2 <C real=R_re, imag=R_im>>' (of type <class 'symforce.geo.rot2.Rot2'>) to a symengine type.
That works, but you are right that it is a little cumbersome. Thanks for the explanation.
The symbolic solve function appears to be different than sympys equivalent. In addition, it appears to be sparsely documented, so I am not entirely sure it should be the same.
I tried to run the example from sympys documentation:
, but got the error:
Removing the
dict
parameter gave me the error:From the code it seems like symforce is just calling the sympy.solve, which leads me to expect the behaviour in the sympy documentation.
Environment (please complete the following information):