-2

I have a problem where I need to solve a polynomial of a degree that increases with each iteration (loop). The expression is

(1/(1+x)^1) + (1/(1+x)^2) + (1/(1+x)^3) + (1/(1+x)^4) + (1/(1+x)^5)

As you can see, the exponential grows with each iteration and this expression will be used in the function "fsolve". I DO NOT WANT to evaluate the expression, but rather to build it in order to use it in the function "fsolve". (I am using scipy.optimize to import fsolve) Thanks in advance

Edit

def func(x):
    return["Here i will have my mathematical expression in the question"]
print(fsolve(func, [0.05]))

where 0.05 is a starting guess of the solution to the expression when put equal to zero.

EDIT2
The entire problem is :

C1 - (1-1/(1+x)^n - C2*sum[1/(1+x) + 1/(1+x)^2)+ ... + 1/(1+x)^n]

where C1 and C2 are two constants and n is the iterations. The boundaries of the sum-expression is from 1 to n.

StVnilor
  • 61
  • 1
  • 6
  • `fsolve` is not part of python. Are you using some library? – Sembei Norimaki Feb 20 '23 at 15:53
  • Yes I am using scipy.optimize – StVnilor Feb 20 '23 at 15:56
  • could you add this to the question tags? – Sembei Norimaki Feb 20 '23 at 15:57
  • Python is not a symbolic language and scipy are numerical recipes. What do you mean by solve a polynomial? There is no polynomial here but rather rational function of increasing orders. In order to use optimization techniques you need to be able to evaluate your objective function wrt it's variable. The goal is then being able to construct a parametric evaluable rational function to submit to the solver. Could you better describe the problem you are dealing with? Could you show us what a call to scipy will look like? Cheers – jlandercy Feb 20 '23 at 16:07
  • I added an edit with explanation to your comment @jlandercy – StVnilor Feb 20 '23 at 16:22
  • @StochasticVolatility, I have added an answer to at least construct the function. If you read the documentation, fsolve will take a function that returns numerical values of the same size if the input. There is no way to pass a string representing your function in a human readable input. – jlandercy Feb 20 '23 at 16:29

3 Answers3

2

I would use a comprehension:

exp = " + ".join([f"(1/(1+x)^{i})" for i in range(1, 6)])
print(exp)

Giving you:

(1/(1+x)^1) + (1/(1+x)^2) + (1/(1+x)^3) + (1/(1+x)^4) + (1/(1+x)^5)
JonSG
  • 10,542
  • 2
  • 25
  • 36
1

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.

enter image description here

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]
jlandercy
  • 7,183
  • 1
  • 39
  • 57
  • The expression in question has other constants and terms that I have not mentioned here since they are superfluos for my question. The only terms that increase are the 1/(1+x)^n for each iteration n. I did not understand your solution with factory. If you could break it down a little bit, i would appreciate it a lot. – StVnilor Feb 20 '23 at 20:49
  • In order to answer to your request I need the full description of the problem. Update your post with the complete function to adjust. – jlandercy Feb 21 '23 at 07:23
  • I have added the complete expression. I really appreciate your effort. – StVnilor Feb 21 '23 at 08:29
  • Could you also explain what @np.vectorize and factory does? Also why did you name the function as "wrapped"? Any particular reason? – StVnilor Feb 21 '23 at 09:11
  • @StochasticVolatility, I have updated my answer to get more insight on those topics, I added links to documentation as well. – jlandercy Feb 21 '23 at 11:18
  • Is there any alternative to using np.vectorize? I really don't understand how it works since I am new to Python and programming in general. @jlandercy – StVnilor Feb 22 '23 at 09:15
  • As long as you will feed single x values to the function yes, So for fsolve you can ignore it, but then you cannot easily plot functions. It's very simple, vectorize allow you to feed the function with an array and it will apply element-wise (element by element) the function and return an array of the same size. – jlandercy Feb 22 '23 at 11:52
  • I still don't understand the usage of np.vectorize. What is it that is being vectorized? I do NOT want to plot the function. I just want to find the roots. What is the role of np.vectorize? What are its inparameters and what is its output? Where do they come from? If you could write down an example where you describe where np.vectorize comes in and what it does, I would appreciate it a lot @jlandercy – StVnilor Feb 22 '23 at 15:57
  • I have no more word to describe it. I provided link of the official documentation in my post. I suggest you try it by your self with example in documentation or with the example of the plot in my answer. Just comment out the line and see the difference. – jlandercy Feb 23 '23 at 05:41
1

Are you sure that you only want five terms? Analytically, with infinite terms your entire expression evaluates to 1 + 1/x, which is trivially invertible.

Reinderien
  • 11,755
  • 5
  • 49
  • 77