I am trying to find the roots of a function that has a lot of arguments using findroot
in mpmath
, but in this question, I am going to use a simple function.
from mpmath import mp
def func(x, **parameters):
return parameters["a"]*x*x + 1
solution = mp.findroot(f = func, x0 = 0.955j, solver = 'muller', **kwargs = (a = 1))
Output:
SyntaxError: invalid syntax
The func
function in the code returns the value of $f(x) = ax^2 + 1$ at an $x$-value, given the value of $a$. For example, the value of $f(1)$ for $a = 1$ is 2. To find the roots of $f$, I used findroot
. Since my function has arguments, I will need to use the **kwargs in findroot
. However, I am struggling to use it. I keep on getting a syntax error.
The syntax error is in assigning **kwargs =
. The variable kwargs
is a dictionary, and **
unpacks that dictionary. The expression **kwargs = (a = 1)
is essentially trying to unpack a dictionary while also assigning that dictionary to another variable assignment.
The **kwargs
parameter of a function allows you to pass any number of additional keyword arguments. The parameter a=1
is a keyword argument. The kwargs
itself is a collection of keyword arguments that you want to "package together".
A quick demonstration:
In [1]: def print_kwargs(**kwargs):
...: for key, value in kwargs.items():
...: print(key, value)
...:
In [2]: print_kwargs(a=1, b=2, c=3)
a 1
b 2
c 3
In [3]: params = dict(a=1, b=2, c=3)
In [4]: print_kwargs(**params)
a 1
b 2
c 3
Looking at the documentation, it looks like the solver is allowed to accept whatever kwargs
you pass. In other words, the kwargs
aren't passed to your function, func
, they're passed to the solver, 'muller'
in your case.
The docs suck and the kwargs
that the mpmath.calculus.optimization.Muller
takes aren't explained at all as far as I can see so I have no idea what it does with its keyword arguments, but the point is that the kwargs
aren't being passed to your function as you expected.
Unfortunately you'll need to do a bit more work on your function func
if you want it to be a dynamic polynomial which can take arguments for its parameters.
Here's what you can do:
def function_builder(**params):
def func(x):
return params["a"] * x**2 + 1 # or whatever function
return func
And then you'd do:
>>> params = dict(a=1)
>>> func = function_builder(**params)
>>> mp.findroot(f=func, x0=0.955j, solver='muller')
mpc(real='0.0', imag='1.0')
You can implement function_builder()
in such a way as to return a new function object which is only a function of x
. Then when you want to find the roots of a particular polynomial, you can call function_builder()
with whatever parameters you want and it'll return the function you want to which mp.findroot
can then use.
Here's an example of how you might implement a general quadratic equation builder:
def quadratic_builder(**params):
a, b, c = params["a"], params["b"], params["c"]
def f(x):
return a * x**2 + b * x + c
return f
Of course, for something as specific as a quadratic equation, it'd be better to explicitly call out the keyword arguments:
def quadratic_builder(*, a, b, c):
def f(x):
return a * x**2 + b * x + c
return f
And then you can package together "sets" of coefficients:
coefs_1 = dict(a=1, b=3, c=-5)
coefs_2 = dict(a=-2, b=1, c=0)
coefs_3 = dict(a=-6, b=8, c=3)
f1 = quadratic_builder(**coefs_1)
f2 = quardatic_builder(**coefs_2)
f3 = quardatic_builder(**coefs_3)
Etc.