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
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 :)
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
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
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