0

I've checked multiple other answers for this error, but can't figure out why this is occuring.

from sympy.solvers import nsolve
from sympy import Symbol, log
x = Symbol('x')
u = nsolve(log(x/0.06) / (x-0.06) - 8,x)
print(u)
Traceback (most recent call last):
  File "C:\Users\cyjac\PycharmProjects\pharmacome\scrap.py", line 5, in <module>
    u = nsolve(log(x/0.06) / (x-0.06) - 8,x)
  File "C:\Users\cyjac\PycharmProjects\pharmacome\venv\lib\site-packages\sympy\utilities\decorator.py", line 88, in func_wrapper
    return func(*args, **kwargs)
  File "C:\Users\cyjac\PycharmProjects\pharmacome\venv\lib\site-packages\sympy\solvers\solvers.py", line 2979, in nsolve
    x = sympify(findroot(f, x0, **kwargs))
  File "C:\Users\cyjac\PycharmProjects\pharmacome\venv\lib\site-packages\mpmath\calculus\optimization.py", line 920, in findroot
    x0 = [ctx.convert(x0)]
  File "C:\Users\cyjac\PycharmProjects\pharmacome\venv\lib\site-packages\mpmath\ctx_mp_python.py", line 671, in convert
    return ctx._convert_fallback(x, strings)
  File "C:\Users\cyjac\PycharmProjects\pharmacome\venv\lib\site-packages\mpmath\ctx_mp.py", line 634, in _convert_fallback
    raise TypeError("cannot create mpf from " + repr(x))
TypeError: cannot create mpf from x
hpaulj
  • 221,503
  • 14
  • 230
  • 353
Jacob Wheeler
  • 53
  • 1
  • 8
  • I am not sure but: 1) `mpf` could refer to the `modules=['mpmath']` default parameter of `nsolve` 2) you are using a numerical solver so a initial value is required, `nsolve(expr, x, x0)` – cards Apr 05 '23 at 17:55
  • the initial value solve the problem... but it opens a new one... how do I find the right initial value(s)? Here a more simple example: `nsolve(x**2-1, x, -3)` --> `-1.0000000` – cards Apr 05 '23 at 17:59
  • 1
    I get a simple numeric result from 'plain' `solve`. – hpaulj Apr 05 '23 at 18:12
  • `nsolve` of `expr = log(x/0.06) - 8*(x-0.06)` is easier, working with more initial values. There are some hints in the docs and code that a variable in the denominator can give problems. – hpaulj Apr 05 '23 at 18:18
  • Using the flag `verify=False`, `sp.nsolve(expr, x, x0, verify=False)`, you may avoid the accuracy error (and then check if the solution makes sense by yourself). This can be useful when you are guessing the initial value PS use as initial value _x0< 0.4_ – cards Apr 05 '23 at 18:27
  • @hpaulj Yes, this works in this example of 0.06 in the log, but in the full example I'm testing, the real decimal is longer. Even rounding it to 4 decimal places leaves it to hang forever. SymPy is terribly inefficient at this for whatever reason. nsolve requiring a starting value kind of defeats its purpose for me now that I know this. – Jacob Wheeler Apr 07 '23 at 00:10

1 Answers1

1

When you use nsolve you must provide an appropriate initial guess. For example:

expr = log(x/0.06) / (x-0.06) - 8
nsolve(expr, x, 1)
# ValueError: Could not find root within given tolerance. (64.0 > 2.16840434497100886801e-19)
# Try another starting point or tweak arguments.

This error happens quite often. Hence, when we are dealing with functions of 1 or 2 variables it is a good idea to plot the functions in order to get a proper initial guess:

enter image description here

Then:

nsolve(expr, x, 0.25)
# 0.225493155418687

Since you are solving a single equation, it is worth to point out the following:

  • nsolve(expr, x, 0.25) by default is going to use the secant method. As seen above, it is really sensitive on the choice of the initial guess.
  • nsolve([expr], [x], 10) (note that I wrapped the expression and symbol into lists) is going to use the multidimensional Newton method, which uses the Jacobian. It tends to be less sensitive to the choice of the initial guess.
Davide_sd
  • 10,578
  • 3
  • 18
  • 30
  • 1
    One can make the equation much less sensitive to such troubles by using the numerator of the normalized expression: `i` in the following gives an answer when in range [1,1000]. `nsolve(expr.as_numer_denom()[0], x, i).` – smichr Apr 05 '23 at 19:45
  • You've been lucky: if you plot the expression you clearly see that the numerator has two zeros. For some weird reason, `nsolve` always find the bigger... But if you wrap the expression into a list in order to use the multidimensional newton method, it will also find the first. – Davide_sd Apr 05 '23 at 20:15
  • That small root is a zero in the denominator of the original expr. It lies to the left of the max of the numerator (which is at `solve(expr.as_numer_denom()[0].diff()) -> [0.125]`. – smichr Apr 05 '23 at 20:50
  • Is there a faster way to do this than nsolve then? Graphing it defeats the point of trying to automate equation solving for me, and solve is very slow when the precision of the decimal inside the log is anything more than 2. – Jacob Wheeler Apr 07 '23 at 00:11
  • You can try `solve(expr, x, rational=False)`: note that it is not guaranteed that `solve` will be able to find all roots. – Davide_sd Apr 07 '23 at 07:15