1

I want to solve an optimization problem as proposed in this thread. Now, I not only want to solve for the x[1]...x[n], but also for the variable y. It looks like something is wrong with the indexing.

from sympy import Sum, symbols, Indexed, lambdify
from scipy.optimize import minimize
import numpy as np

def _eqn(y, variables, periods, sign=-1.0):
    x, i = symbols("x i")
    n = periods-1
    s = Sum(Indexed('x', i)/(1+0.06)**i, (i, 1, n))
    f = lambdify(x, s, modules=['sympy'])
    return float(sign*(y + f(variables)))

z = 3
results = minimize(lambda xy: _eqn(xy[0], xy[1:z], z),np.zeros(z))
print(results.x)
jakevdp
  • 77,104
  • 11
  • 125
  • 160
Peterhack
  • 941
  • 4
  • 15
  • 34

2 Answers2

0

If all you need is variable number of arguments for minimization then does the following code work for you?

from sympy import var
from scipy.optimize import minimize
import numpy as np

def _eqn(y, variables, periods, sign=-1.0):    
    f = 0
    for i,x in enumerate(variables):
        f += x/(1+0.06)**(i+1)
    return float(sign*(y + f))

z = 3
results = minimize(lambda xy: _eqn(xy[0], xy[1:z], z),np.zeros(z))
print(results.x)
0

From the error message it seems there is an issue in your indexing. The summation runs from 1 to n but by default indexing of list type objects in Python goes from 0 to n-1. If I change this in your code it seems to work. Check it out.

import sympy as sp
from scipy.optimize import minimize
import numpy as np

sp.init_printing()

def _eqn(y, variables, periods, sign=-1.0):
    x, i = sp.symbols("x i")
    n = periods-1
    s = sp.Sum(sp.Indexed('x', i)/(1+0.06)**(i+1), (i, 0, n-1)).doit()
    f = sp.lambdify(x, s, modules=['sympy'])
    return float(sign*(y + f(variables)))

z = 3
results = minimize(lambda xy: _eqn(xy[0], xy[1:z], z),np.zeros(z))
print(results.x)
  • Ok, I see your point. It should be (i+1) though. So in the some I now have x[0] and x[1] as variables. Is your indexing in the minimize function then really correct? Assuming that we change y and variables in the _eqn, wouldn't it be minimize(lambda xy: _eqn(xy[:z-1], xy[z-1], z),np.zeros(z))? Because that the indexing matches the sum variables?! Can I somehow check the result as it deviates in those cases. – Peterhack Nov 13 '17 at 08:28
  • The indexing seems to be correct. Inside the `_eqn` function, `x[0]` and `x[1]` refer to `variables[0]` and `variables[1]` which in turn refer to `xy[1]` and `xy[2]`. You have already separated `xy[0]` into the input argument `y` of the function `_eqn`. –  Nov 13 '17 at 13:35
  • Ok, I guess I can follow. But why are the result (with i+1) and your first answer different? Does this really have an impact? `x = [var('x'+ str(i)) for i in range(z)]` – Peterhack Nov 13 '17 at 14:26
  • Now I understood. You were talking about (i+1) in the exponent of the denominator. I have updated both my answers. The equation that you are solving has no minimum value for `sign=-1.0` because as we make `y` and `x` larger, the total function become more and more negative. If you set `sign=1.0`, then both the methods give same answer. –  Nov 13 '17 at 15:10
  • I am sorry but the function you are trying to minimize has a constant Jacobian. Minimization requires solving for Jacobian = 0. So, there is no unique minimum with either `sign=+1.0` or `sign=-1.0`. In that sense both my answers are useless. –  Nov 13 '17 at 15:52