Opened 8 years ago

Last modified 3 years ago

#12809 new defect

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

Reported by: afleckenstein Owned by: burcin
Priority: minor Milestone: sage-6.4
Component: symbolics Keywords: solve
Cc: kcrisman, mjo, eviatarbach Merged in:
Authors: Reviewers:
Report Upstream: N/A Work issues:
Branch: Commit:
Dependencies: Stopgaps:

Description

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.

Attachments (1)

expression_pyx_doctest.txt (2.2 KB) - added by afleckenstein 8 years ago.

Download all attachments as: .zip

Change History (14)

Changed 8 years ago by afleckenstein

comment:1 follow-up: Changed 8 years ago by 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. 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 :-)

comment:2 in reply to: ↑ 1 Changed 8 years ago by was

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

comment:3 follow-up: Changed 8 years ago by was

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

comment:4 in reply to: ↑ 3 Changed 8 years ago by nbruin

Replying to was:

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)

comment:5 Changed 8 years ago by afleckenstein

  • Priority changed from major to minor

comment:6 follow-up: Changed 8 years ago by mjo

  • Cc mjo added

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.

comment:7 in reply to: ↑ 6 Changed 8 years ago by nbruin

Replying to mjo:

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)

comment:8 Changed 7 years ago by eviatarbach

  • Cc eviatarbach added

comment:9 Changed 6 years ago by jdemeyer

  • Milestone changed from sage-5.11 to sage-5.12

comment:10 Changed 6 years ago by vbraun_spam

  • Milestone changed from sage-6.1 to sage-6.2

comment:11 Changed 6 years ago by vbraun_spam

  • Milestone changed from sage-6.2 to sage-6.3

comment:12 Changed 5 years ago by vbraun_spam

  • Milestone changed from sage-6.3 to sage-6.4

comment:13 Changed 3 years ago by pelegm

Should we update the milestone?

Note: See TracTickets for help on using tickets.