syntax for results of solve() and dsolve()

181 views
Skip to first unread message

Ondrej Certik

unread,
May 18, 2009, 1:49:32 AM5/18/09
to sy...@googlegroups.com
Hi,

we were just discussing with Aaron on #sympy IRC what should the
solve() and dsolve() return. Here is what Mathematica is doing and I
like that a lot:

solve(whatever) return a list of dicts. You can find lots of examples here:

http://reference.wolfram.com/mathematica/ref/Solve.html

to translate to sympy syntax, use this table:

Mathematica: {a, b, c, d}
sympy: [a, b, c, d]

Mathematica: {a -> b, c -> d}
sympy: {a: b, c: d}

So here are some particular examples using sympy syntax:

>>> solve(Eq(x**2, 1), x)
[{x: 1}, {x: -1}]
>>> solve(Eq(x**2, 0), x)
[{x: 0}, {x: 0}]

More tricky is if solve() can't do it, either because sympy can't do
it, or because it's algebraically not possible. Mathematica returns an
instance of a Solve() class (talking in sympy language) Like:

>>> solve(Eq(sin(x)+x, 0), x)
Solve(Eq(sin(x)+x), x)

and it also prints bunch of warnings (that can be suppressed), like:

Solve::tdep: The equations appear to involve the variables to be solved for in
an essentially non-algebraic way.

But both me and Aaron like what Maple is doing:

>>> solve(Eq(sin(x)+x, 0), x)
[RootOf(Eq(sin(x)+x), x, 1)]


At least sometimes you know the number of roots (one in the case
above), so that's why the "1" above. I am not sure currently if even
the number of roots is unknown. Then I think it should return:

>>> solve(Eq(sin(x)+x, 0), x)
RootsOf(Eq(sin(x)+x), x)

which could behave like a dictionary. These RootOf and RootsOf classes
can have some features, e.g. sometimes you know how to do some
operation on the root (like squaring it), even without having an
explicit formula for it.

As to dsolve(), in mathematica it returns a list of dicts, examples:

http://reference.wolfram.com/mathematica/ref/DSolve.html

so in sympy syntax:

>>> dsolve(Eq(y(x).diff(x, 2) + y(x), 0), [y(x)])
[{y(x): sin(x)}, {y(x): cos(x)}]

sometimes it returns things using integration constants:

[{y(x): C(1)*sin(x) + C(2)*cos(x)}]

You can see from the docs above that Mathematica is returning both
ways, I am not sure at the moment when it returns what. In any case,
it's a list of dicts.

Sometimes the solution to the ODE can only be returned in an implicit
form. Mathematica just calls solve() for the equation and returns
whatever it returns (see above). So I think we should do the same in
sympy.

This means changing the behavior of solve() as described above, e.g.
not returning NotImplementedError() exceptions, but rather RootsOf, or
a list of RootOf.


Before Aaron changes the dsolve() and solve() behavior, do you all
agree the above is the way to go? If not, let's discuss it. Not that
it's a backward incompatible change, both in solve() and dsolve(), but
I think we should do it.

Ondrej

Aaron S. Meurer

unread,
May 18, 2009, 1:53:02 AM5/18/09
to sy...@googlegroups.com
I agree with all of this, as Ondrej noted. Only, I think your Maple
example should be something like:
solve(cos(x)=x,x);
RootOf(_Z - cos(_Z))

Of course, sinx=-x when x=0.

Aaron Meurer

Fabian Pedregosa

unread,
May 18, 2009, 8:26:57 AM5/18/09
to sy...@googlegroups.com
Ondrej Certik wrote:
> Hi,
>
> we were just discussing with Aaron on #sympy IRC what should the
> solve() and dsolve() return. Here is what Mathematica is doing and I
> like that a lot:
>
> solve(whatever) return a list of dicts.

What are the benefits of this instead of simply return a list with
solutions ?

You can find lots of examples here:
>
> http://reference.wolfram.com/mathematica/ref/Solve.html
>
> to translate to sympy syntax, use this table:
>
> Mathematica: {a, b, c, d}
> sympy: [a, b, c, d]
>
> Mathematica: {a -> b, c -> d}
> sympy: {a: b, c: d}
>
> So here are some particular examples using sympy syntax:
>
>>>> solve(Eq(x**2, 1), x)
> [{x: 1}, {x: -1}]

don't see the benefit over [1,-1]. It is clear that it is relative to x
since you passed that argument to solve. Plus, it would make it harder
to iterate over the solutions.

In my oppinion it adds an unnecessary layer of complexity, but I'd love
to see some examples where this results code shorted/more readable.

>>>> solve(Eq(x**2, 0), x)
> [{x: 0}, {x: 0}]
>
> More tricky is if solve() can't do it, either because sympy can't do
> it, or because it's algebraically not possible. Mathematica returns an
> instance of a Solve() class (talking in sympy language) Like:
>
>>>> solve(Eq(sin(x)+x, 0), x)
> Solve(Eq(sin(x)+x), x)
>

Seems to me a good idea

Priit Laes

unread,
May 18, 2009, 11:06:11 AM5/18/09
to sy...@googlegroups.com
Ühel kenal päeval, P, 2009-05-17 kell 22:49, kirjutas Ondrej Certik:
> Hi,

>
> solve(whatever) return a list of dicts. You can find lots of examples here:

It should also handle lists with infinite amount of solutions:

Currently we miss some of the solutions:

In [30]: solve(cos(pi*x), x)
Out[30]: [1/2]

But it should be x = n - 1/2, where n is the set of integers...

Päikest,
Priit :)

Vinzent Steinberg

unread,
May 18, 2009, 1:19:14 PM5/18/09
to sympy
On May 18, 2:26 pm, Fabian Pedregosa <fab...@fseoane.net> wrote:
> Ondrej Certik wrote:
[...]
> > So here are some particular examples using sympy syntax:
>
> >>>> solve(Eq(x**2, 1), x)
> > [{x: 1}, {x: -1}]
>
> don't see the benefit over [1,-1]. It is clear that it is relative to x
> since you passed that argument to solve. Plus, it would make it harder
> to iterate over the solutions.
>
> In my oppinion it adds an unnecessary layer of complexity, but I'd love
> to see some examples where this results code shorted/more readable.

I think it is only necessary for multidimensional equations involving
several variables.

>
> > More tricky is if solve() can't do it, either because sympy can't do
> > it, or because it's algebraically not possible. Mathematica returns an
> > instance of a Solve() class (talking in sympy language) Like:
>
> >>>> solve(Eq(sin(x)+x, 0), x)
> > Solve(Eq(sin(x)+x), x)
>
> Seems to me a good idea
>
> > and it also prints bunch of warnings (that can be suppressed), like:
>
> > Solve::tdep: The equations appear to involve the variables to be solved for in
> >     an essentially non-algebraic way.
>
> > But both me and Aaron like what Maple is doing:
>
> >>>> solve(Eq(sin(x)+x, 0), x)
> > [RootOf(Eq(sin(x)+x), x, 1)]
>
> > At least sometimes you know the number of roots (one in the case
> > above), so that's why the "1" above. I am not sure currently if even
> > the number of roots is unknown. Then I think it should return:
>
> >>>> solve(Eq(sin(x)+x, 0), x)
> > RootsOf(Eq(sin(x)+x), x)
>
> > which could behave like a dictionary. These RootOf and RootsOf classes
> > can have some features, e.g. sometimes you know how to do some
> > operation on the root (like squaring it), even without having an
> > explicit formula for it.

I really like RootOf(), it allows mixing algebraic and numerical
solutions, like

>>> solve((cos(x)+x)*x, x)
[RootOf(cos(x)+x,x), 0]
>>> _.evalf()
[-0.7390851332151542, 0]

It would be also more consistent with integrate() which returns an
Integral() instead of raising a NotImplementedError.

I'd be glad to have this in sympy! It would be much easier to fall
back to numerical methods.

Vinzent

Ondrej Certik

unread,
May 18, 2009, 1:37:02 PM5/18/09
to sy...@googlegroups.com
On Mon, May 18, 2009 at 10:19 AM, Vinzent Steinberg
<vinzent....@googlemail.com> wrote:
>
> On May 18, 2:26 pm, Fabian Pedregosa <fab...@fseoane.net> wrote:
>> Ondrej Certik wrote:
> [...]
>> > So here are some particular examples using sympy syntax:
>>
>> >>>> solve(Eq(x**2, 1), x)
>> > [{x: 1}, {x: -1}]
>>
>> don't see the benefit over [1,-1]. It is clear that it is relative to x
>> since you passed that argument to solve. Plus, it would make it harder
>> to iterate over the solutions.
>>
>> In my oppinion it adds an unnecessary layer of complexity, but I'd love
>> to see some examples where this results code shorted/more readable.
>
> I think it is only necessary for multidimensional equations involving
> several variables.

Also it's consistent with Sage and Mathematica. We may add an optional
parameter to solve() that would control the output.

Currently, it's inconsistent in sympy, e.g. compare:


In [2]: solve((x+5*y-2, -3*x+6*y-15), x, y)
Out[2]: {x: -3, y: 1}

In [3]: solve(x-2, x)
Out[3]: [2]


e.g. sometimes you get a dict, sometimes a list? And what if the
system of 2 equations is nonlinear and it has 2 or 3 solutions? So
it's a mess.

To convert from the list of dicts for one equation to a list:

In [1]: r = [{x: 1}, {x: -1}]

In [2]: r
Out[2]: [{x: 1}, {x: -1}]

In [3]: [i.values()[0] for i in r]
Out[3]: [1, -1]

And this may be automatic when one does for example:

>>> solve(x**2-1, x, return_list=True)
[1, -1]

But imho there is a value in being consistent by default.

I also prefer the RootOf, since most of the time it should be
possible. Only sometimes you don't even know the number of roots, then
I would use RootsOf (we have this class already), rather than
introducing a new class Solve().

Ondrej

Ondrej Certik

unread,
May 18, 2009, 1:39:32 PM5/18/09
to sy...@googlegroups.com

This is what Mathematica does:

In[6]:= Solve[Cos[Pi*x]==0, x]

Solve::ifun: Inverse functions are being used by Solve, so some solutions may
not be found; use Reduce for complete solution information.

1 1
Out[6]= {{x -> -(-)}, {x -> -}}
2 2

Or to make the output more readable:

In[7]:= InputForm[Solve[Cos[Pi*x]==0, x]]

Solve::ifun: Inverse functions are being used by Solve, so some solutions may
not be found; use Reduce for complete solution information.

Out[7]//InputForm= {{x -> -1/2}, {x -> 1/2}}


So it's not really clever either.

Ondrej

Ondrej Certik

unread,
May 18, 2009, 1:44:37 PM5/18/09
to sy...@googlegroups.com

And if one uses Reduce, as it suggests:

In[9]:= InputForm[Reduce[Cos[Pi*x]==0, x]]

Out[9]//InputForm=
Element[C[1], Integers] && (x == (-Pi/2 + 2*Pi*C[1])/Pi ||
x == (Pi/2 + 2*Pi*C[1])/Pi)

So we might do the same in sympy.

Ondrej

Reply all
Reply to author
Forward
0 new messages