Scipy offers numerical solvers that expect numerical function as parameters. There is no point to write a function that return of string of the function representation, that will not work.
A simple way to implement what you are asking is making use of factory (here we will use a decorated function):
import numpy as np
from scipy import optimize
def factory(order=1):
@np.vectorize
def wrapped(x):
return np.sum([1/np.power(1 + x, i + 1) for i in range(order)])
return wrapped
The key idea is to pass order
parameter to the decorator (factory
) which will return the decorated function (wrapped
) with a compliant signature for fsolve
. For short:
wrapped
function is your objective function which returns the desired quantity wrt order
and x
values
factory
function is a commodity to parametrize the order
and avoid to rewrite the whole thing each time you want to investigate a specific order
, this function returns the wrapped
decorated function.
The @np.vectorize
decorator is a numpy
commodity to vectorize function. Then the function behaves on array element-wise and returns the results for each value. Handy for plotting the function:
x = np.linspace(-20, 20, 2000)
fig, axe = plt.subplots()
for order in range(1, 6):
y = factory(order)(x) # This call will return 1 value if not vectorized, returns 2000 values as expected when vectorized
axe.plot(x, y, label="Order %d" % order)
Then simply iterate through your orders to get the functions you want to solve:
for order in range(1, 11):
function = factory(order=order)
solution = optimize.fsolve(function, x0=-2.1)
print(order, solution)
# 1 [-1.93626047e+83]
# 2 [-2.]
# 3 [-1.64256077e+83]
# 4 [-2.]
# 5 [-1.47348643e+83]
# 6 [-2.]
# 7 [-1.42261052e+83]
# 8 [-2.]
# 9 [-1.43409773e+83]
# 10 [-2.]
Indeed as fsolve
is looking for roots and in your setup there are orders with no real root. Analyzing the function of order 5 will show that there is no real roots but rather asymptotic zero when x
goes to infinity (which is not a root) as we remains in the real domain. For even orders, the root is easily found by the above procedure.

So your are likely to have float overflow
error and sometimes high numbers that are meaningless w.r.t. a root finding problem for odd orders.
OP Update
Based on the new function you have detailed in your post, you can add extra parameters to the objective function as follow:
def factory(order=1):
@np.vectorize
def wrapped(x, C1, C2):
return C1 - (1 - 1/np.power(1 + x, order) - C2*np.sum([1/np.power(1 + x, i + 1) for i in range(order)]))
return wrapped
Then we solve it for several orders as usual, I picked (C1, C2) = (10,10)
:
for order in range(1, 11):
function = factory(order=order)
solution = optimize.fsolve(function, x0=-2.1, args=(10, 10))
print(order, solution)
# 1 [-2.22222222]
# 2 [-3.20176167]
# 3 [-2.10582151]
# 4 [-2.73423813]
# 5 [-2.06950043]
# 6 [-2.54187609]
# 7 [-2.0517491]
# 8 [-2.43340793]
# 9 [-2.04122279]
# 10 [-2.36505166]