sagemath / sage

Main repository of SageMath
https://www.sagemath.org
Other
1.33k stars 453 forks source link

Solve does not give consistent results when a dummy variable is involved #12809

Open 93c36c93-d939-4ecc-9b2f-4f78060138e2 opened 12 years ago

93c36c93-d939-4ecc-9b2f-4f78060138e2 commented 12 years ago

When working on another ticket (#11201) that involves documentation for the solve function, doing doctests proved to be frustrating. I added to the examples section of the docstring, and then multiple tests failed. I did some manual testing in sage and noticed a problem:

sage: var('x y')
(x, y)
sage: solve(cos(x)*sin(y)==1/2, x+y==0,x,y)
[[x == 1/4*pi + pi*z6, y == -1/4*pi - pi*z6]]

This is a perfectly valid answer, except that when I tried to solve the same equations again, the answer is different:

sage: solve(cos(x)*sin(y)==1/2, x+y==0,x,y)
[[x == 1/4*pi + pi*z14, y == -1/4*pi - pi*z14]]

This is still mathematically correct. If I solve them many more times, z's coefficient keeps going up by 8:

sage: solve(cos(x)*sin(y)==1/2, x+y==0,x,y)
[[x == 1/4*pi + pi*z22, y == -1/4*pi - pi*z22]]
sage: solve([cos(x)*sin(x) == 1/2, x+y == 0],x,y)
[[x == 1/4*pi + pi*z30, y == -1/4*pi - pi*z30]]
sage: solve([cos(x)*sin(x) == 1/2, x+y == 0],x,y)
[[x == 1/4*pi + pi*z38, y == -1/4*pi - pi*z38]]
sage: solve([cos(x)*sin(x) == 1/2, x+y == 0],x,y)
[[x == 1/4*pi + pi*z46, y == -1/4*pi - pi*z46]]
sage: solve([cos(x)*sin(x) == 1/2, x+y == 0],x,y)
[[x == 1/4*pi + pi*z54, y == -1/4*pi - pi*z54]]
sage: solve([cos(x)*sin(x) == 1/2, x+y == 0],x,y)
[[x == 1/4*pi + pi*z62, y == -1/4*pi - pi*z62]]
sage: solve([cos(x)*sin(x) == 1/2, x+y == 0],x,y)
[[x == 1/4*pi + pi*z70, y == -1/4*pi - pi*z70]]
sage: solve([cos(x)*sin(x) == 1/2, x+y == 0],x,y)
[[x == 1/4*pi + pi*z78, y == -1/4*pi - pi*z78]]
sage: solve([cos(x)*sin(x) == 1/2, x+y == 0],x,y)
[[x == 1/4*pi + pi*z86, y == -1/4*pi - pi*z86]]
sage: solve([cos(x)*sin(x) == 1/2, x+y == 0],x,y)
[[x == 1/4*pi + pi*z94, y == -1/4*pi - pi*z94]]

All still mathematically correct.

This is a problem when you are trying to add examples to the documentation for solve and many doctests fail because the coefficients for the dummy variable are shifted up by some integer amount. I have included the file expressions_pyx_doctests.txt to show more of what I am talking about. To cause this, I added two examples, and the examples that I added were not complained about in the doctest. Also, z's coefficient went up by 6 this time, different than 8 in the first example.

CC: @kcrisman @orlitzky @eviatarbach

Component: symbolics

Keywords: solve

Issue created by migration from https://trac.sagemath.org/ticket/12809

93c36c93-d939-4ecc-9b2f-4f78060138e2 commented 12 years ago

Attachment: expression_pyx_doctest.txt

kcrisman commented 12 years ago
comment:1

This is a known issue - I won't call it a problem, though some people (like the reporter) certainly have strong feelings on this. People have tried to weasel out of it in changing doctests by putting ellipses in for the variable names, but in the end it's better to show the end user reading the documentation what a likely output actually looks like. Concur that it's annoying for those writing doctests, but at least in one opinion this is not really a bug.

That said, if we can be smart enough with Maxima and how it's naming these dummy variables for this not to happen, or to return something different, that would be great. But it's probably not worth the effort, given how many actual enhancements or bugs we can fix instead :-)

williamstein commented 12 years ago
comment:2

Replying to @kcrisman:

This is a known issue - I won't call it a problem, though some people (like the reporter) certainly have strong feelings on this. People have tried to weasel out of it in changing doctests by putting ellipses in for the variable names, but in the end it's better to show the end user reading the documentation what a likely output actually looks like.

I completely disagree with you. I think it is perfectly reasonable to replace an example like

sage: solve([cos(x)*sin(x) == 1/2, x+y == 0],x,y)
[[x == 1/4*pi + pi*z94, y == -1/4*pi - pi*z94]]

with

sage: solve([cos(x)*sin(x) == 1/2, x+y == 0],x,y)
[[x == 1/4*pi + pi*z..., y == -1/4*pi - pi*z...]]

Alternatively, if this is NOT intended for somebody to actually read, then simply make it a "unit test", e.g., put all the tests into a function (e.g., "test_solve"), then do whatever you want to verify correctness of output using asserts, and finally put one single doctest in test_solve, which is:

sage: test_solve()
williamstein commented 12 years ago
comment:3

This does suggest we may want to consider reseting Maxima's counter for dummy variables before calling solve.

nbruin commented 12 years ago
comment:4

Replying to @williamstein:

This does suggest we may want to consider reseting Maxima's counter for dummy variables before calling solve.

It would require care of the user to remember whether different occurrences of the same variable name refer to independent parameters or not, but to_poly has a convenience function to relabel the temporaries:

(%i1) load(to_poly_solver);
(%o1) ...
(%i2) display2d: false;
(%o2) false
(%i3) nicedummies(to_poly_solve([sin(x)=cos(x)],[x]));
(%o3) %union([x = (4*%pi*%z0+%pi)/4])
(%i4) nicedummies(to_poly_solve([sin(x)=cos(x)],[x]));
(%o4) %union([x = (4*%pi*%z0+%pi)/4])

I think the slight inconvenience of getting new names every time is preferable to the ambiguity that is caused by reusing names among several equation solves. Another issue is that the maxima-to-sage conversion maps %z1 to z1 without checking. This can lead to further unintended name collisions.

(but then again, if someone is willing to use the results returned by a CAS solve routine without checking, they're asking for trouble anyway)

orlitzky commented 12 years ago
comment:6

I implemented MaximaLibElement.nicedummies(), but it seems that once you make the round-trip through sage, the information that zXX is a dummy is lost:

from sage.interfaces.maxima_lib import maxima_lib
sage: y = var('y')                                      
sage: soln = solve([cos(x)*sin(x) == 1/2, x+y == 0],x,y)
sage: maxima_lib(soln[0][0])
x=%pi*z36+%pi/4
sage: maxima_lib(soln[0][0]).nicedummies()
x=%pi*z36+%pi/4

(The function does actually work before you leave Maxima.)

So, we'd either have to add a nicedummies parameter to each of the routines that return them, or write our own implementation that works entirely within sage.

nbruin commented 12 years ago
comment:7

Replying to @orlitzky:

I implemented MaximaLibElement.nicedummies(), but it seems that once you make the round-trip through sage, the information that zXX is a dummy is lost

This is mainly for documentation and sufficient cross linking. In this sage-devel thread the cause is described: maxima's %z2 gets translated to z2 in sage and then to z2 in maxima, so the variables get renamed.

See nicedummies in maxima's share/contrib/topoly.lisp to see how "dummies" get recognized (it's a property hanging off the symbol, not the name of the symbol)

pelegm commented 7 years ago
comment:13

Should we update the milestone?