New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

solve() is a giant mess #6659

Open
rlamy opened this Issue Dec 15, 2012 · 23 comments

Comments

Projects
None yet
7 participants
@rlamy
Copy link
Member

rlamy commented Dec 15, 2012

Let's just kill it and start over!

The replacement needs to have a simple interface (no complicated transformations of the input, no flags whatsoever) and a consistent return type.

Original issue for #6659: http://code.google.com/p/sympy/issues/detail?id=3560
Original author: https://code.google.com/u/101272611947379421629/

@smichr

This comment has been minimized.

Copy link
Member

smichr commented Jan 5, 2013

One thing that might be done is to move solving to the object level, so Add, Mul, etc... would have their own _eval_solve routines.

Original comment: http://code.google.com/p/sympy/issues/detail?id=3560#c1
Original author: https://code.google.com/u/117933771799683895267/

@asmeurer

This comment has been minimized.

Copy link
Member

asmeurer commented Jan 5, 2013

I'm not sure if that will simplify things much, though I still think we should do it (or is it really just better eval_inverse support that we need?). Mul._eval_solve would be trivial, but Add._eval_solve would be basically the whole solving code. The variation of different kinds of expressions isn't really expressed in the core classes. 

On the other hand, I like how dsolve is structured (there are some things that could be changed, but the basic idea is pretty extensible).

Original comment: http://code.google.com/p/sympy/issues/detail?id=3560#c2
Original author: https://code.google.com/u/asmeurer@gmail.com/

@wxgeo

This comment has been minimized.

Copy link
Contributor

wxgeo commented Jan 9, 2013

"and a consistent return type"

Indeed.

I didn't work on sympy for a while, so some questions :
* is there some consensus now about output format ?
* is there a corresponding  issue opened for it ?
* is there a place (in sympy wiki ?) which sum up pros and cons for different output formats ? 
(I couldn't find any in https://github.com/sympy/sympy/wiki )

I remember some discussions on this subject in google groups, notably this one : https://groups.google.com/forum/?fromgroups=#!topicsearchin/sympy/solver$20AND$20output/sympy/lUzKkeV3QJI IMO, the right direction is using some kind of sympy.Set flavor, at least by default, as Ronan pointed out in this discussion.

As fr as I'm concerned, I would drop dict output support.

However, if dict output is really needed, it could be supported for some equations types by setting a flag.
Anyway, it shouldn't be used by default, as dict can't handle every case, and so can't be used for output format unification.
(Adding a flag for this would not ease the flag mess, but better this that taking no decision and keeping current situation).

I think this output issue must be addressed before any redesign attempt.

Original comment: http://code.google.com/p/sympy/issues/detail?id=3560#c3
Original author: https://code.google.com/u/117997262464115802198/

@asmeurer

This comment has been minimized.

Copy link
Member

asmeurer commented Jan 9, 2013

There have been many discussions about creating a Solve or Solution object, which would be the output of solve. I think we could make it match both current interfaces (list and dict). We should try to think ahead for things like infinite solutions to avoid having to change the API again. At least underlying it, it should use the Set classes. Or we could just return one directly. 

What's wring with the dict output? This format very clearly matches the solution to the variable.

Original comment: http://code.google.com/p/sympy/issues/detail?id=3560#c4
Original author: https://code.google.com/u/asmeurer@gmail.com/

@wxgeo

This comment has been minimized.

Copy link
Contributor

wxgeo commented Jan 9, 2013

"What's wrong with the dict output?"

Most arguments again dict output are detailed in the discussion linked above.


Personally, I would sum up my own arguments as follows :

* correct mathematical type is set, and unless major drawback, it's always better to follow closely mathematical conventions.
  Using dict may lead to surprising results, like solve(x**2 = 1, x) != solve(y**2 = 1, y). Mathematically, it doesn't make sense IMO.

* this is probably mostly personal, but most of the time, when I solve equations, I use only one variable, so, in practice, using sets leads to more natural and readable code, like `for sol in solutions`, or `if 0 in solutions` (and not `for sol in solutions.values()`...).

* if one expands sympy solving abilities, I'm afraid some fundamental restrictions of dict may appear.
For example, solve(x**2 +y**2 < 1, (x, y)) should be the disk of radius 1 and center (0,0) ; I don't see how to represent it using a dict.

Original comment: http://code.google.com/p/sympy/issues/detail?id=3560#c5
Original author: https://code.google.com/u/117997262464115802198/

@asmeurer

This comment has been minimized.

Copy link
Member

asmeurer commented Jan 9, 2013

So I think a Solution class could solve all these problems.  We could allow solution[x] syntax to get the solution for a specific variable, but still allow "for sol in solutions" and even solve(x**2 - 1, x) == solve(y**2 - 1, y) (though I'm not yet convinced that this second one is a good idea).  It would also be straightforward to allow parametric solutions (which I think encompasses both overdetermined systems as well as things like solve(sin(x), x), in the first case the parameter is real/complex, in the second, it is integral).  

I'm not sure what you would expect from solve(x**2 + y**2 < 1, (x, y)). I don't see how to represent the unit open disk other than by the very equation you gave to solve.  

By the way, the practical reason for returning dicts is that solve guesses the variables from the equations, so that you can just type solve([x - y, x + y]) (instead of solve([x - y, x + y], (x, y))).

Original comment: http://code.google.com/p/sympy/issues/detail?id=3560#c6
Original author: https://code.google.com/u/asmeurer@gmail.com/

@wxgeo

This comment has been minimized.

Copy link
Contributor

wxgeo commented Jan 10, 2013

"So I think a Solution class could solve all these problems."

It would be nice to have solve(x+1<0,x) simply return Interval(-oo, -1) for example. 

IMO, there should be a generic Set class from which inherits things as different as solutions of solve(x+1<0,x) or solve(sin(x), x). A class which represent a mathematical set.
And probably a Solutions class for equations for which we can't find all solutions (something like RootOf).


I think something like sets of named tuples may be considered as well, when there are many variables. 
This enables access by name (`sol.x for sol in solutions`), but mimics better mathematical structure.

(I don't know if it has already been discussed.)

"I'm not sure what you would expect from solve(x**2 + y**2 < 1, (x, y))"
Neither I am. Maybe a Disk class (this is not such an exotic object when working with complexes).
Yet, for now, I admit it is mostly theory, but I expect such problems to appear in the future if solve() output differs too much from mathematical sets.

"
By the way, the practical reason for returning dicts is that solve guesses the variables from the equations, so that you can just type solve([x - y, x + y]) (instead of solve([x - y, x + y], (x, y)))."

Guessing is just syntactic sugar, so for me alphabetical order is enough.

If you need more, sets of named tuples may do the trick, and preserve the {(x1,y1), (x2,y2)... } mathematical structure.

Original comment: http://code.google.com/p/sympy/issues/detail?id=3560#c7
Original author: https://code.google.com/u/117997262464115802198/

@asmeurer

This comment has been minimized.

Copy link
Member

asmeurer commented Jan 10, 2013

Some comments:

- We *have* a Set class. And it already had things like Integers, Interval, and TransformationSet (allows set builder notation). 

- In general you can't use attribute access to get at solutions. One, Symbols can have names that aren't valid Python identifiers. Two, symbols are identified by their name and assumption, not just their name, so you can have two different symbols with the same name. This can also happen with different classes, like Wild or Dummy. Third, we have support for solving for non-Symbols, like functions. So you have to use dictionary style syntax to get this. 

- Maybe you want solve(x**2 + y**2 < 1) to return the Set object corresponding to a Disk (for now it would have to just be TransformationSet((x, y), x**2 + y**2 < 1)). This seems to be a rather loose definition of solving. 

- A use for guessing (that well need to consider) is that you can reduce an under-determined system. In this case, you need to know what variables it picked. 

But I get your point, though, which is that there's little more that we might want from Sets than the variable solved for, except I guess for syntactic sugar to maintain old APIs.

Original comment: http://code.google.com/p/sympy/issues/detail?id=3560#c8
Original author: https://code.google.com/u/asmeurer@gmail.com/

@rlamy

This comment has been minimized.

Copy link
Member

rlamy commented Jan 10, 2013

@asmeurer: With a multivariate system of equations, there's no such thing as "the solution for a specific variable". Also, in general, there is no way to iterate over the solution set (since it can be infinite and even uncountable). So I don't understand what you're suggesting.

Anyway, my idea here is actually to quit trying to improve solve() and to create a robust and reliable way of finding the set of values satisfying a boolean expression (I'll call it newsolve()), that can be used in library code without complicated pre- and post-checks. Compatibility with solve() and use in interactive sessions should explicitly not be a concern at first.

I think that what we need most is a way to represent symbolic solution sets. newsolve(f(x), x) should return something like FilteredSet(Reals, Lambda([x], Eq(f(x), 0)), and newsolve(x**2 + y**2 < 1, [x, y]) something like 
FilteredSet(CartesianPower(Reals, 2), Lambda([x, y], x**2 + y**2 < 1)).
So we need more expressivity in building symbolic sets and also boolean Lambdas.

Original comment: http://code.google.com/p/sympy/issues/detail?id=3560#c9
Original author: https://code.google.com/u/101272611947379421629/

@rlamy

This comment has been minimized.

Copy link
Member

rlamy commented Jan 10, 2013

@asmeurer (I didn't see #c8 before):

- We *have* a Set class.
Yes, but we need more Set subclasses

- In general you can't use attribute access to get at solutions.
+1


- Maybe you want solve(x**2 + y**2 < 1) to return the Set object corresponding to a Disk (for now it would have to just be TransformationSet((x, y), x**2 + y**2 < 1)). This seems to be a rather loose definition of solving. 

Saying that the unit disk is the solution of x**2 + y**2 < 1 is just like saying (-oo, 0) is the solution of x < 0. I don't see why you call it loose. And what other object is there that might be called the solution of the inequation??
Also, I'm not sure what you mean with the TransformationSet, but I don't think that the disk can be expressed that way. It has to be something else (a FilteredSet, as I just called it): mathematically, it's {(x,y) in R²| x² + y² < 1}, which cannot be put into the form {f(x, y)| (x, y) in S}.

Original comment: http://code.google.com/p/sympy/issues/detail?id=3560#c10
Original author: https://code.google.com/u/101272611947379421629/

@asmeurer

This comment has been minimized.

Copy link
Member

asmeurer commented Jan 10, 2013

Ok, we're thinking of different things here. I'm taking about user API and you're talking about library API. I agree that the internals are a mess.

Original comment: http://code.google.com/p/sympy/issues/detail?id=3560#c11
Original author: https://code.google.com/u/asmeurer@gmail.com/

@wxgeo

This comment has been minimized.

Copy link
Contributor

wxgeo commented Jan 14, 2013

"- In general you can't use attribute access to get at solutions."

Then, something similar to a set of ordered dict (which are much like named tuples, except for access API) would do the trick.

Original comment: http://code.google.com/p/sympy/issues/detail?id=3560#c12
Original author: https://code.google.com/u/117997262464115802198/

@alessandro-bernardini

This comment has been minimized.

Copy link

alessandro-bernardini commented Feb 17, 2013

Here some (redundant) code picking up (again) some of the mess. The code is interpreted with Python 3.2.3 and Python 2.7.3. The version of sympy is given below. First I post the code and then the output within IDLE

import sympy
print(sympy.__version__)
a = sympy.var('a')
x = sympy.Symbol('x')
ss = sympy.solve(x**5 - 5*x + 12)
print(ss)
print([ss[i].n() for i in range(5)])
ss = sympy.solve([x**5 - 5*x + 12], x)
print(ss)
ss = sympy.solve(a**5 - 5*a + 12, a)
print(ss)
print([ss[i].n() for i in range(5)])
ss = sympy.solve([a**5 - 5*a + 12])
print(ss)
ss = sympy.solve([a**4 - 5*a + 12])
print(ss)
ss = sympy.solve([x**4 - 5*x + 12], x)
print(ss)

OUTPUT Python3:
Python 3.2.3 (default, Oct 19 2012, 19:53:57) 
[GCC 4.7.2] on linux2
Type "copyright", "credits" or "license()" for more information.
>>> ================================ RESTART ================================
>>> 
0.7.2
[RootOf(x**5 - 5*x + 12, 0), RootOf(x**5 - 5*x + 12, 1), RootOf(x**5 - 5*x + 12, 2), RootOf(x**5 - 5*x + 12, 3), RootOf(x**5 - 5*x + 12, 4)]
[-1.84208596619025, -1.84208596619025, -1.84208596619025, 1.2728972239225 - 0.719798681483861*I, 1.2728972239225 + 0.719798681483861*I]
[]
[RootOf(a**5 - 5*a + 12, 0), RootOf(a**5 - 5*a + 12, 1), RootOf(a**5 - 5*a + 12, 2), RootOf(a**5 - 5*a + 12, 3), RootOf(a**5 - 5*a + 12, 4)]
[-1.84208596619025, -1.84208596619025, -1.84208596619025, 1.2728972239225 - 0.719798681483861*I, 1.2728972239225 + 0.719798681483861*I]
[]
[{a: -sqrt(8/(25/16 + 3*sqrt(1751)*I/16)**(1/3) + 2*(25/16 + 3*sqrt(1751)*I/16)**(1/3))/2 + sqrt(-2*(25/16 + 3*sqrt(1751)*I/16)**(1/3) - 10/sqrt(8/(25/16 + 3*sqrt(1751)*I/16)**(1/3) + 2*(25/16 + 3*sqrt(1751)*I/16)**(1/3)) - 8/(25/16 + 3*sqrt(1751)*I/16)**(1/3))/2}, {a: sqrt(8/(25/16 + 3*sqrt(1751)*I/16)**(1/3) + 2*(25/16 + 3*sqrt(1751)*I/16)**(1/3))/2 - sqrt(-2*(25/16 + 3*sqrt(1751)*I/16)**(1/3) + 10/sqrt(8/(25/16 + 3*sqrt(1751)*I/16)**(1/3) + 2*(25/16 + 3*sqrt(1751)*I/16)**(1/3)) - 8/(25/16 + 3*sqrt(1751)*I/16)**(1/3))/2}, {a: -sqrt(-2*(25/16 + 3*sqrt(1751)*I/16)**(1/3) - 10/sqrt(8/(25/16 + 3*sqrt(1751)*I/16)**(1/3) + 2*(25/16 + 3*sqrt(1751)*I/16)**(1/3)) - 8/(25/16 + 3*sqrt(1751)*I/16)**(1/3))/2 - sqrt(8/(25/16 + 3*sqrt(1751)*I/16)**(1/3) + 2*(25/16 + 3*sqrt(1751)*I/16)**(1/3))/2}, {a: sqrt(-2*(25/16 + 3*sqrt(1751)*I/16)**(1/3) + 10/sqrt(8/(25/16 + 3*sqrt(1751)*I/16)**(1/3) + 2*(25/16 + 3*sqrt(1751)*I/16)**(1/3)) - 8/(25/16 + 3*sqrt(1751)*I/16)**(1/3))/2 + sqrt(8/(25/16 + 3*sqrt(1751)*I/16)**(1/3) + 2*(25/16 + 3*sqrt(1751)*I/16)**(1/3))/2}]
[(-sqrt(8/(25/16 + 3*sqrt(1751)*I/16)**(1/3) + 2*(25/16 + 3*sqrt(1751)*I/16)**(1/3))/2 + sqrt(-2*(25/16 + 3*sqrt(1751)*I/16)**(1/3) - 10/sqrt(8/(25/16 + 3*sqrt(1751)*I/16)**(1/3) + 2*(25/16 + 3*sqrt(1751)*I/16)**(1/3)) - 8/(25/16 + 3*sqrt(1751)*I/16)**(1/3))/2,), (sqrt(8/(25/16 + 3*sqrt(1751)*I/16)**(1/3) + 2*(25/16 + 3*sqrt(1751)*I/16)**(1/3))/2 - sqrt(-2*(25/16 + 3*sqrt(1751)*I/16)**(1/3) + 10/sqrt(8/(25/16 + 3*sqrt(1751)*I/16)**(1/3) + 2*(25/16 + 3*sqrt(1751)*I/16)**(1/3)) - 8/(25/16 + 3*sqrt(1751)*I/16)**(1/3))/2,), (-sqrt(-2*(25/16 + 3*sqrt(1751)*I/16)**(1/3) - 10/sqrt(8/(25/16 + 3*sqrt(1751)*I/16)**(1/3) + 2*(25/16 + 3*sqrt(1751)*I/16)**(1/3)) - 8/(25/16 + 3*sqrt(1751)*I/16)**(1/3))/2 - sqrt(8/(25/16 + 3*sqrt(1751)*I/16)**(1/3) + 2*(25/16 + 3*sqrt(1751)*I/16)**(1/3))/2,), (sqrt(-2*(25/16 + 3*sqrt(1751)*I/16)**(1/3) + 10/sqrt(8/(25/16 + 3*sqrt(1751)*I/16)**(1/3) + 2*(25/16 + 3*sqrt(1751)*I/16)**(1/3)) - 8/(25/16 + 3*sqrt(1751)*I/16)**(1/3))/2 + sqrt(8/(25/16 + 3*sqrt(1751)*I/16)**(1/3) + 2*(25/16 + 3*sqrt(1751)*I/16)**(1/3))/2,)]
>>> 


NOW Python2 
The (different) code:
import sympy
print(sympy.__version__)
a = sympy.var('a')
x = sympy.Symbol('x')
ss = sympy.solve(x**5 - 5*x + 12)
print(ss)
ss = sympy.solve([x**5 - 5*x + 12], x)
print(ss)
ss = sympy.solve(a**5 - 5*a + 12, a)
print(ss)
ss = sympy.solve([a**5 - 5*a + 12])
print(ss)
ss = sympy.solve([a**4 - 5*a + 12])
print(ss)
ss = sympy.solve([x**4 - 5*x + 12], x)
print(ss)

The output
Python 2.7.3 (default, Sep 26 2012, 21:53:58) 
[GCC 4.7.2] on linux2
Type "copyright", "credits" or "license()" for more information.
>>> ================================ RESTART ================================
>>> 
0.7.1.rc1
[]
[]
[]
[]
[((-2*(25/16 + 3*1751**(1/2)*I/16)**(1/3) + 10/(8/(25/16 + 3*1751**(1/2)*I/16)**(1/3) + 2*(25/16 + 3*1751**(1/2)*I/16)**(1/3))**(1/2) - 8/(25/16 + 3*1751**(1/2)*I/16)**(1/3))**(1/2)/2 + (8/(25/16 + 3*1751**(1/2)*I/16)**(1/3) + 2*(25/16 + 3*1751**(1/2)*I/16)**(1/3))**(1/2)/2,), (-(8/(25/16 + 3*1751**(1/2)*I/16)**(1/3) + 2*(25/16 + 3*1751**(1/2)*I/16)**(1/3))**(1/2)/2 + (-2*(25/16 + 3*1751**(1/2)*I/16)**(1/3) - 10/(8/(25/16 + 3*1751**(1/2)*I/16)**(1/3) + 2*(25/16 + 3*1751**(1/2)*I/16)**(1/3))**(1/2) - 8/(25/16 + 3*1751**(1/2)*I/16)**(1/3))**(1/2)/2,), (-(-2*(25/16 + 3*1751**(1/2)*I/16)**(1/3) - 10/(8/(25/16 + 3*1751**(1/2)*I/16)**(1/3) + 2*(25/16 + 3*1751**(1/2)*I/16)**(1/3))**(1/2) - 8/(25/16 + 3*1751**(1/2)*I/16)**(1/3))**(1/2)/2 - (8/(25/16 + 3*1751**(1/2)*I/16)**(1/3) + 2*(25/16 + 3*1751**(1/2)*I/16)**(1/3))**(1/2)/2,), ((8/(25/16 + 3*1751**(1/2)*I/16)**(1/3) + 2*(25/16 + 3*1751**(1/2)*I/16)**(1/3))**(1/2)/2 - (-2*(25/16 + 3*1751**(1/2)*I/16)**(1/3) + 10/(8/(25/16 + 3*1751**(1/2)*I/16)**(1/3) + 2*(25/16 + 3*1751**(1/2)*I/16)**(1/3))**(1/2) - 8/(25/16 + 3*1751**(1/2)*I/16)**(1/3))**(1/2)/2,)]
[((-2*(25/16 + 3*1751**(1/2)*I/16)**(1/3) + 10/(8/(25/16 + 3*1751**(1/2)*I/16)**(1/3) + 2*(25/16 + 3*1751**(1/2)*I/16)**(1/3))**(1/2) - 8/(25/16 + 3*1751**(1/2)*I/16)**(1/3))**(1/2)/2 + (8/(25/16 + 3*1751**(1/2)*I/16)**(1/3) + 2*(25/16 + 3*1751**(1/2)*I/16)**(1/3))**(1/2)/2,), (-(8/(25/16 + 3*1751**(1/2)*I/16)**(1/3) + 2*(25/16 + 3*1751**(1/2)*I/16)**(1/3))**(1/2)/2 + (-2*(25/16 + 3*1751**(1/2)*I/16)**(1/3) - 10/(8/(25/16 + 3*1751**(1/2)*I/16)**(1/3) + 2*(25/16 + 3*1751**(1/2)*I/16)**(1/3))**(1/2) - 8/(25/16 + 3*1751**(1/2)*I/16)**(1/3))**(1/2)/2,), (-(-2*(25/16 + 3*1751**(1/2)*I/16)**(1/3) - 10/(8/(25/16 + 3*1751**(1/2)*I/16)**(1/3) + 2*(25/16 + 3*1751**(1/2)*I/16)**(1/3))**(1/2) - 8/(25/16 + 3*1751**(1/2)*I/16)**(1/3))**(1/2)/2 - (8/(25/16 + 3*1751**(1/2)*I/16)**(1/3) + 2*(25/16 + 3*1751**(1/2)*I/16)**(1/3))**(1/2)/2,), ((8/(25/16 + 3*1751**(1/2)*I/16)**(1/3) + 2*(25/16 + 3*1751**(1/2)*I/16)**(1/3))**(1/2)/2 - (-2*(25/16 + 3*1751**(1/2)*I/16)**(1/3) + 10/(8/(25/16 + 3*1751**(1/2)*I/16)**(1/3) + 2*(25/16 + 3*1751**(1/2)*I/16)**(1/3))**(1/2) - 8/(25/16 + 3*1751**(1/2)*I/16)**(1/3))**(1/2)/2,)]
>>> 

You can of course reproduce this with your python(3) (idle(3)) installation.

Original comment: http://code.google.com/p/sympy/issues/detail?id=3560#c13
Original author: https://code.google.com/u/116921275998115216639/

@alessandro-bernardini

This comment has been minimized.

Copy link

alessandro-bernardini commented Feb 17, 2013

Please observe that in my example just above, the MULTIPLICITY of the roots is wrong too !

It seems they just fixed this now.
See the Issue 3610 :    Solve fails to find all roots.

Original comment: http://code.google.com/p/sympy/issues/detail?id=3560#c14
Original author: https://code.google.com/u/116921275998115216639/

@asmeurer

This comment has been minimized.

Copy link
Member

asmeurer commented Feb 18, 2013

Here's the result in the latest git master:

0.7.2-git
[RootOf(x**5 - 5*x + 12, 0), RootOf(x**5 - 5*x + 12, 1), RootOf(x**5 - 5*x + 12, 2), RootOf(x**5 - 5*x + 12, 3), RootOf(x**5 - 5*x + 12, 4)]
[-1.84208596619025, -0.351854240827372 - 1.70956104337033*I, -0.351854240827372 + 1.70956104337033*I, 1.2728972239225 - 0.719798681483861*I, 1.2728972239225 + 0.719798681483861*I]
[]
[RootOf(a**5 - 5*a + 12, 0), RootOf(a**5 - 5*a + 12, 1), RootOf(a**5 - 5*a + 12, 2), RootOf(a**5 - 5*a + 12, 3), RootOf(a**5 - 5*a + 12, 4)]
[-1.84208596619025, -0.351854240827372 - 1.70956104337033*I, -0.351854240827372 + 1.70956104337033*I, 1.2728972239225 - 0.719798681483861*I, 1.2728972239225 + 0.719798681483861*I]
[]
[{a: -sqrt(8/(25/16 + 3*sqrt(1751)*I/16)**(1/3) + 2*(25/16 + 3*sqrt(1751)*I/16)**(1/3))/2 + sqrt(-2*(25/16 + 3*sqrt(1751)*I/16)**(1/3) - 10/sqrt(8/(25/16 + 3*sqrt(1751)*I/16)**(1/3) + 2*(25/16 + 3*sqrt(1751)*I/16)**(1/3)) - 8/(25/16 + 3*sqrt(1751)*I/16)**(1/3))/2}, {a: sqrt(8/(25/16 + 3*sqrt(1751)*I/16)**(1/3) + 2*(25/16 + 3*sqrt(1751)*I/16)**(1/3))/2 - sqrt(-2*(25/16 + 3*sqrt(1751)*I/16)**(1/3) + 10/sqrt(8/(25/16 + 3*sqrt(1751)*I/16)**(1/3) + 2*(25/16 + 3*sqrt(1751)*I/16)**(1/3)) - 8/(25/16 + 3*sqrt(1751)*I/16)**(1/3))/2}, {a: -sqrt(-2*(25/16 + 3*sqrt(1751)*I/16)**(1/3) - 10/sqrt(8/(25/16 + 3*sqrt(1751)*I/16)**(1/3) + 2*(25/16 + 3*sqrt(1751)*I/16)**(1/3)) - 8/(25/16 + 3*sqrt(1751)*I/16)**(1/3))/2 - sqrt(8/(25/16 + 3*sqrt(1751)*I/16)**(1/3) + 2*(25/16 + 3*sqrt(1751)*I/16)**(1/3))/2}, {a: sqrt(-2*(25/16 + 3*sqrt(1751)*I/16)**(1/3) + 10/sqrt(8/(25/16 + 3*sqrt(1751)*I/16)**(1/3) + 2*(25/16 + 3*sqrt(1751)*I/16)**(1/3)) - 8/(25/16 + 3*sqrt(1751)*I/16)**(1/3))/2 + sqrt(8/(25/16 + 3*sqrt(1751)*I/16)**(1/3) + 2*(25/16 + 3*sqrt(1751)*I/16)**(1/3))/2}]
[(-sqrt(8/(25/16 + 3*sqrt(1751)*I/16)**(1/3) + 2*(25/16 + 3*sqrt(1751)*I/16)**(1/3))/2 + sqrt(-2*(25/16 + 3*sqrt(1751)*I/16)**(1/3) - 10/sqrt(8/(25/16 + 3*sqrt(1751)*I/16)**(1/3) + 2*(25/16 + 3*sqrt(1751)*I/16)**(1/3)) - 8/(25/16 + 3*sqrt(1751)*I/16)**(1/3))/2,), (sqrt(8/(25/16 + 3*sqrt(1751)*I/16)**(1/3) + 2*(25/16 + 3*sqrt(1751)*I/16)**(1/3))/2 - sqrt(-2*(25/16 + 3*sqrt(1751)*I/16)**(1/3) + 10/sqrt(8/(25/16 + 3*sqrt(1751)*I/16)**(1/3) + 2*(25/16 + 3*sqrt(1751)*I/16)**(1/3)) - 8/(25/16 + 3*sqrt(1751)*I/16)**(1/3))/2,), (-sqrt(-2*(25/16 + 3*sqrt(1751)*I/16)**(1/3) - 10/sqrt(8/(25/16 + 3*sqrt(1751)*I/16)**(1/3) + 2*(25/16 + 3*sqrt(1751)*I/16)**(1/3)) - 8/(25/16 + 3*sqrt(1751)*I/16)**(1/3))/2 - sqrt(8/(25/16 + 3*sqrt(1751)*I/16)**(1/3) + 2*(25/16 + 3*sqrt(1751)*I/16)**(1/3))/2,), (sqrt(-2*(25/16 + 3*sqrt(1751)*I/16)**(1/3) + 10/sqrt(8/(25/16 + 3*sqrt(1751)*I/16)**(1/3) + 2*(25/16 + 3*sqrt(1751)*I/16)**(1/3)) - 8/(25/16 + 3*sqrt(1751)*I/16)**(1/3))/2 + sqrt(8/(25/16 + 3*sqrt(1751)*I/16)**(1/3) + 2*(25/16 + 3*sqrt(1751)*I/16)**(1/3))/2,)]

The output is the same in Python 2 and 3 (I think it only differed for you because you have different versions installed).

Original comment: http://code.google.com/p/sympy/issues/detail?id=3560#c15
Original author: https://code.google.com/u/asmeurer@gmail.com/

@asmeurer

This comment has been minimized.

Copy link
Member

asmeurer commented Feb 25, 2013

**Blockedon:** 6766  

Referenced issues: #6766
Original comment: http://code.google.com/p/sympy/issues/detail?id=3560#c16
Original author: https://code.google.com/u/asmeurer@gmail.com/

@smichr

This comment has been minimized.

Copy link
Member

smichr commented Feb 27, 2013

To get consistent output (until everything is rewritten some day) the `dict=True` option will give you a list of dictionaries (see issue 6766 )

Referenced issues: #6766
Original comment: http://code.google.com/p/sympy/issues/detail?id=3560#c17
Original author: https://code.google.com/u/117933771799683895267/

@asmeurer

This comment has been minimized.

Copy link
Member

asmeurer commented Mar 17, 2013

**Blocking:** 6798  

Referenced issues: #6798
Original comment: http://code.google.com/p/sympy/issues/detail?id=3560#c18
Original author: https://code.google.com/u/asmeurer@gmail.com/

@asmeurer

This comment has been minimized.

Copy link
Member

asmeurer commented Mar 17, 2013

Another thing to think about: issue 6798 .

Referenced issues: #6798
Original comment: http://code.google.com/p/sympy/issues/detail?id=3560#c19
Original author: https://code.google.com/u/asmeurer@gmail.com/

@gwax

This comment has been minimized.

Copy link
Contributor

gwax commented Apr 1, 2013

Between this and issue 4333 I think that we might want both a Solution object and a SolutionSet object (or some sort of unified object that handles both cases).

I would expect a Solution to be a dict-like object.

I would expect a SolutionSet to have iterable and/or list-like behavior over Solution objects. This is necessary to handle infinite solutions (e.g solve(sin(x), x) ) and could work just as well for simpler cases like polynomials.

Expected behavior might be:

>>> sols = solve(x**4-1,x)
>>> sols
SolutionSet(Solution({x: 1}), Solution({x: -1}), Solution({x: I}), Solution({x: -I}))
>>> len(sols)
4
>>> [s[x] for s in sols]
[1, -1, I, -I]

or:

>>> sols = solve(sin(x),x)
>>> sols
SolutionSet(Solution({x: 0}), ...)
>>> len(sols)
oo
>>> sols.next()
Solution({x: 0})
>>> sols.next()
Solution({x: Pi})
>>> sols.next()
Solution({x: -Pi})

Referenced issues: #4333
Original comment: http://code.google.com/p/sympy/issues/detail?id=3560#c20
Original author: https://code.google.com/u/waksman@gwax.com/

@asmeurer

This comment has been minimized.

Copy link
Member

asmeurer commented Apr 2, 2013

I think that's a good idea. We often confuse properties of the individual solutions and the properties of the whole set of solutions that we might want to put on this object. For example

Solution:
 - Is it a real solution (sometimes solve() returns partially solved results, like solve(f(x) - x, x) => [f(x)])?

SolutionSet:
 - Are all solutions guaranteed to be included?

(I know there are more, but I can't think of them off the top of my head)

Two questions:

- How should it work if solutions are parameterized? One example is solve(sin(x), x). The solution set is {n*pi | n integer}. Another example is solving a linear system that has free variables (or more generally, a positive dimensional system). Is this formalism part of Solution or SolutionSet? I don't like your example, because it doesn't give the general form of the solution, n*pi. Just iterating is not enough. If you have a scheme like

 * solve f(x) = 0
 * plug the solutions into g(x)
 * do something

You should be able to easily plug the general solution into g(x) (e.g., n*pi, where n = Dummy('n', integer=True)).

- What should happen for inequalities? In this case, solve() returns a boolean expression.

Original comment: http://code.google.com/p/sympy/issues/detail?id=3560#c21
Original author: https://code.google.com/u/asmeurer@gmail.com/

@asmeurer

This comment has been minimized.

Copy link
Member

asmeurer commented May 20, 2013

So here are my ideas

SolutionSet should just be a subclass of Set.  I think we need to use a custom subclass instead of just a Set because data like whether or not it has found all solutions ( issue 6798 ) is part of the whole solution set, not individual solutions.  There are also other potential attributes we might add in the future (for example, expanding or restricting the solution set based on assumptions).

Set objects already can handle all the things we want.  A finite set of solutions is FiniteSet.  No solutions is EmptySet. Infinite solutions is TransformationSet.  Solutions to inequalities, might be some kind of product set (if the set objects we need don't exist, we can just create them).  

The Solution object represents a single solution.  It won't exist for solutions to inequalities, only for discrete SolutionSet objects.  It should unify the two different kinds of solutions returned now, a dictionary, like {x: 1, y: 2}, and a tuple (1, 2).  In other words, if s is the Solution from solve([x - 1, y - 2], [x, y]), then s[0] and s[x] should give 1, and s[1] and s[y] should give 2. If [x, y] was not given and they were determined automatically, then numerical indexing would not be allowed.  

I'm still unclear what to do with parameterized solutions.  

Another issue there that might come up is that sometimes the parameter can be one of the original solve variables (like with an underdetermined linear system), and sometimes it must be a new one.

Referenced issues: #6798
Original comment: http://code.google.com/p/sympy/issues/detail?id=3560#c22
Original author: https://code.google.com/u/asmeurer@gmail.com/

@rlamy

This comment has been minimized.

Copy link
Member

rlamy commented May 20, 2013

I now think that the solution object should rather be a Boolean. The problem with a Set in the multivariate case is that it forces the variables to be ordered, causing solve(some_system, x, y) != solve(some-system, y, x).

Also, a Boolean could be directly used as an assumption, and be passed to further calls to solve(), or to refine(), etc.

Original comment: http://code.google.com/p/sympy/issues/detail?id=3560#c23
Original author: https://code.google.com/u/101272611947379421629/

@rlamy rlamy added this to the Release0.8.0 milestone Mar 7, 2014

@rlamy rlamy added imported labels Mar 7, 2014

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment