Skip to content
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' function lost an answer? #21026

Open
loopxia opened this issue Mar 3, 2021 · 15 comments
Open

'solve' function lost an answer? #21026

loopxia opened this issue Mar 3, 2021 · 15 comments

Comments

@loopxia
Copy link

loopxia commented Mar 3, 2021

In [1]: eq=(2-x)**(1/S(3))+sqrt(x-1)-1

In [2]: eq
Out[2]:
3 _______     _______
╲╱ 2 - x  + ╲╱ x - 1  - 1

In [3]: solve(eq)
Out[3]: [1, 2]

why another answer x=10 is lost?
is this a bug or I lost something?

'solveset' is similar lost answer 10.

@oscarbenjamin
Copy link
Contributor

SymPy uses the principle root so the cube root of -8 is not -2:

In [8]: cbrt(-8)
Out[8]: 
  3 ____
2⋅╲╱ -1 

In [9]: cbrt(-8).n()
Out[9]: 1.0 + 1.73205080756888

You should be able to solve this using real_root but that crashes out with recursion error:

In [16]: x = Symbol('x', real=True)

In [17]: eq = real_root(2-x, 3) + sqrt(x-1)-1

In [18]: eq
Out[18]: 
  _______   3 _________                
╲╱ x - 1  + ╲╱ │x - 2│ ⋅sign(2 - x) - 1

In [19]: solve(eq, x)
...
RecursionError: maximum recursion depth exceeded

An explicit Piecewise rewrite works:

In [20]: eq.rewrite(Piecewise)
Out[20]: 
            ⎛⎧3 _______               ⎞ ⎛⎧1   for x - 2 < 0_______   ⎜⎪╲╱ x - 2   for x - 20⎟ ⎜⎪                 ⎟    
╲╱ x - 1  + ⎜⎨                        ⎟⋅⎜⎨-1  for x - 2 > 0- 1
            ⎜⎪3 _______               ⎟ ⎜⎪                 ⎟    
            ⎝⎩╲╱ 2 - x     otherwise  ⎠ ⎝⎩0     otherwiseIn [21]: piecewise_fold(eq.rewrite(Piecewise))
Out[21]: 
⎧  3 _______     _______- ╲╱ x - 2  + ╲╱ x - 1  - 1    for x > 2  
⎪                                          
⎪         _______                          
⎨       ╲╱ x - 1  - 1         for x - 20
⎪                                          
⎪ 3 _______     _______                    
⎪ ╲╱ 2 - x  + ╲╱ x - 1  - 1     otherwiseIn [22]: solve(piecewise_fold(eq.rewrite(Piecewise)), x)
Out[22]: [1, 2, 10]

The recursion error should be fixed.

@loopxia
Copy link
Author

loopxia commented Mar 3, 2021

Hi @oscarbenjamin , thanks for your reply.
I got a different print of 'eq' and error (Sympy:1.7.1 ):

In [219]: x = Symbol('x', real=True)

In [220]: eq = real_root(2-x, 3) + sqrt(x-1)-1

In [221]: eq
Out[221]:
            ⎛⎧3 ________________   ⎜⎪╲╱ │x - 2│ ⋅sign(2 - x)  for im(x) = 0⎟
╲╱ x - 1  + ⎜⎨                                      ⎟ - 1
            ⎜⎪       3 _______                      ⎟
            ⎝⎩       ╲╱ 2 - x            otherwiseIn [222]: solve(eq)
---------------------------------------------------------------------------
NotImplementedError                       Traceback (most recent call last)
<ipython-input-222-4c33efa621d3> in <module>
----> 1 solve(eq)

/usr/local/anaconda3/lib/python3.8/site-packages/sympy/solvers/solvers.py in solve(f, *symbols, **flags)
    954         for e in fi.find(Abs):
    955             if e.has(*symbols):
--> 956                 raise NotImplementedError('solving %s when the argument '
    957                     'is not real or imaginary.' % e)
    958

NotImplementedError: solving Abs(x - 2) when the argument is not real or imaginary.

In [224]: eq.rewrite(Piecewise)
Out[224]:
            ⎛⎧3 ________________   ⎜⎪╲╱ │x - 2│ ⋅sign(2 - x)  for im(x) = 0⎟
╲╱ x - 1  + ⎜⎨                                      ⎟ - 1
            ⎜⎪       3 _______                      ⎟
            ⎝⎩       ╲╱ 2 - x            otherwiseIn [225]: piecewise_fold(_)
Out[225]:
⎧  _______   3 _________
⎪╲╱ x - 1  + ╲╱ │x - 2│ ⋅sign(2 - x) - 1  for im(x) = 0
⎨
⎪       3 _______     _______
⎩       ╲╱ 2 - x  + ╲╱ x - 1  - 1           otherwise

In [226]: solve(_)
---------------------------------------------------------------------------
NotImplementedError                       Traceback (most recent call last)
<ipython-input-226-2ba847fb376d> in <module>
----> 1 solve(_)

/usr/local/anaconda3/lib/python3.8/site-packages/sympy/solvers/solvers.py in solve(f, *symbols, **flags)
    954         for e in fi.find(Abs):
    955             if e.has(*symbols):
--> 956                 raise NotImplementedError('solving %s when the argument '
    957                     'is not real or imaginary.' % e)
    958

NotImplementedError: solving Abs(x - 2) when the argument is not real or imaginary.

@oscarbenjamin
Copy link
Contributor

Oh, sorry I missed this part (edited now):

x = Symbol('x', real=True)

@loopxia
Copy link
Author

loopxia commented Mar 3, 2021

oops, that's my fault. I missed it.
Thank you @oscarbenjamin

@loopxia
Copy link
Author

loopxia commented Mar 3, 2021

@oscarbenjamin don't forget function solveset cannot solve it too. Thank you.

@ghost
Copy link

ghost commented Mar 3, 2021

I think it should iterate over all the roots, and then select the ones which satisfy the relation, instead of only taking the principal roots of the number. This seems necessary since solveset/solve should in principle give all the possible solutions.

@oscarbenjamin
Copy link
Contributor

The definition in sympy of (-1)**(1/3) is that it means the principal root. It is not for solve to apply a different interpretation of the meaning of rational powers.

@ghost
Copy link

ghost commented Mar 4, 2021

Which leads me to this:
Case 1:

>>> x = symbols('x')
>>> f = x**4 - 1
>>> solveset(f,x)
FiniteSet(-1, 1, I, -I)

Case 2:

>>> f = x -root(1,4)
>>> solveset(f,x)
FiniteSet(1)

I would expect solveset to give all the possible answers since, I haven't explicitly demanded real or positive roots

@oscarbenjamin
Copy link
Contributor

I would expect solveset to give all the possible answers since, I haven't explicitly demanded real or positive roots

Look at what you are passing in to solveset:

In [1]: root(1, 4)                                                              
Out[1]: 1

In [2]: solveset(x - 1, x)                                                      
Out[2]: {1}

https://docs.sympy.org/latest/modules/functions/elementary.html#sympy.functions.elementary.miscellaneous.root

@ghost
Copy link

ghost commented Mar 4, 2021

@oscarbenjamin Thanks a lot for clarifying! , I should definitely refer to the docs more.

@ghost
Copy link

ghost commented Mar 9, 2021

if isinstance(result, Complement) or isinstance(result,ConditionSet):
solution_set = result
else:
f_set = [] # solutions for FiniteSet
c_set = [] # solutions for ConditionSet
for s in result:
if checksol(f, symbol, s):
f_set.append(s)
else:
c_set.append(s)
solution_set = FiniteSet(*f_set) + ConditionSet(symbol, Eq(f, 0), FiniteSet(*c_set))

I think i found, where exactly the problem lies. Actually sympy is able to find all the possible solutions, but checksol rejects the value 10 because (-8)**Rational(1,3) by default evaluates to the value 1 + 1.7320i . I believe that something has to be done on evalf so that it allows evaluation to other possible roots as well, maybe a flag that forces evalf to not just use the principal value?
@oscarbenjamin what are your thoughts?

@oscarbenjamin
Copy link
Contributor

I've already explained above:

SymPy uses the principal root so the cube root of -8 is not -2:

That means that 10 is not a solution of the equation.

@ghost
Copy link

ghost commented Mar 10, 2021

I've already explained above:

SymPy uses the principal root so the cube root of -8 is not -2:

That means that 10 is not a solution of the equation.

the example given below is contrary to what we would expect , if we consider sympy to evaluate only principal roots

>>> x = symbols('x')
>>> eq = (x-2)**Rational(1,3) + 2
>>> solveset(eq,x,S.Reals)
FiniteSet(-6)

@oscarbenjamin
Copy link
Contributor

the example given below is contrary to what we would expect , if we consider sympy to evaluate only principal roots

Okat that result is incorrect.

@smichr
Copy link
Member

smichr commented Mar 25, 2021

Okat that result is incorrect.

real_root can be used to get the real, rather than principle, root. So when the domain is Real then that is used there, I believe:

>>> solveset(eq,x)
EmptySet
>>> solveset(eq,x,Reals)
FiniteSet(-6)
>>> ((-8)**Rational(1,3))
2*(-1)**(1/3)
>>> real_root(_)
-2

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

No branches or pull requests

3 participants