I am practicing with SciPy and I encountered an error when trying to use fmin_slsqp. I set up a problem in which I want to maximize an objective function, U, given a set of constraints.
I have two control variables, x[0,t] and x[1,t] and, as you can see, they are indexed by t (time periods). The objective function is:
def obj_fct(x, alpha,beta,Al):
U = 0
x[1,0] = x0
for t in trange:
U = U - beta**t * ( (Al[t]*L)**(1-alpha) * x[1,t]**alpha - x[0,t])
return U
The constraints are defined over these two variables and one of them links the variables from one period (t) to another (t-1).
def constr(x,alpha,beta,Al):
return np.array([
x[0,t],
x[1,0] - x0,
x[1,t] - x[0,t] - (1-delta)*x[1,t-1]
])
Finally, here is the use of fmin_slsqp:
sol = fmin_slsqp(obj_fct, x_init, f_eqcons=constr, args=(alpha,beta,Al))
Leaving aside the fact that there are better ways to solve such dynamic problems, my question is about the syntax. When running this simple code, I get the following error:
Traceback (most recent call last):
File "xxx", line 34, in <module>
sol = fmin_slsqp(obj_fct, x_init, f_eqcons=constr, args=(alpha,beta,Al))
File "D:\Anaconda3\lib\site-packages\scipy\optimize\slsqp.py", line 207, in fmin_slsqp
constraints=cons, **opts)
File "D:\Anaconda3\lib\site-packages\scipy\optimize\slsqp.py", line 311, in _minimize_slsqp
meq = sum(map(len, [atleast_1d(c['fun'](x, *c['args'])) for c in cons['eq']]))
File "D:\Anaconda3\lib\site-packages\scipy\optimize\slsqp.py", line 311, in <listcomp>
meq = sum(map(len, [atleast_1d(c['fun'](x, *c['args'])) for c in cons['eq']]))
File "xxx", line 30, in constr
x[0,t],
IndexError: too many indices for array
[Finished in 0.3s with exit code 1]
What am I doing wrong?
The initial part of the code, assigning values to the parameters, is:
from scipy.optimize import fmin_slsqp
import numpy as np
T = 30
beta = 0.96
L = 1
x0 = 1
gl = 0.02
alpha = 0.3
delta = 0.05
x_init = np.array([1,0.1])
A_l0 = 1000
Al = np.zeros((T+1,1))
Al[1] = A_l0
trange = np.arange(1,T+1,1, dtype='Int8') # does not include period zero
for t in trange: Al[t] = A_l0*(1 + gl)**(t-1)
The array x passed to your objective and constraint functions will be a one-dimensional array (just like your x_init is). You can't index a one-dimensional array with two indices, so expressions such as x[1,0] and x[0,t] will generate an error.
Related
i'm currently working on a model of a distillation flask for a university project, the phisical problem is described by a DAE system, and i'm trying to solve it using GEKKO.
I'm facing a problem with list handling:
In this case i built a function that outputs the compressibility factor of a mixture, and it requires as inputs 3 gekko variables T1,x,y (x,y arrays)
zv1 = m.Param(value=ZCALC(n,comps,R0,p,T1.value,x,y))
m = GEKKO()
y = m.Array(m.Var,n,value=0.)
x = m.Array(m.Var,n,value=0.)
for i in range(n):
y[i].value = y0[i]
x[i].value = x0[i]
T1 = m.Var(value=3.31513478e+02, lb=300, ub=900)
If i leave the 3 values as they are i recieve some errors like:
File "F:\Codice_GEKKO\D86_GEKKO.py", line 113, in <module>
zv1 = m.Param(value=ZCALC(n,comps,R0,p,T1.value,x,y))
File "F:\Codice_GEKKO\compressibilityfactor.py", line 48, in ZCALC
zv=np.max(np.roots([1,-1,(Av-Bv-Bv**2),-Av*Bv]))
File "<__array_function__ internals>", line 6, in roots
File "C:\Users\verci\AppData\Local\Programs\Python\Python36\lib\site-packages\numpy\lib\polynomial.py", line 222, in roots
non_zero = NX.nonzero(NX.ravel(p))[0]
File "<__array_function__ internals>", line 6, in nonzero
File "C:\Users\verci\AppData\Local\Programs\Python\Python36\lib\site-packages\numpy\core\fromnumeric.py", line 1908, in nonzero
return _wrapfunc(a, 'nonzero')
File "C:\Users\verci\AppData\Local\Programs\Python\Python36\lib\site-packages\numpy\core\fromnumeric.py", line 67, in _wrapfunc
return _wrapit(obj, method, *args, **kwds)
File "C:\Users\verci\AppData\Local\Programs\Python\Python36\lib\site-packages\numpy\core\fromnumeric.py", line 44, in _wrapit
result = getattr(asarray(obj), method)(*args, **kwds)
File "C:\Users\verci\AppData\Local\Programs\Python\Python36\lib\site-packages\gekko\gk_operators.py", line 25, in __len__
return len(self.value)
File "C:\Users\verci\AppData\Local\Programs\Python\Python36\lib\site-packages\gekko\gk_operators.py", line 144, in __len__
return len(self.value)
TypeError: object of type 'int' has no len()```
Traceback (most recent call last):
File "F:\Codice_GEKKO\D86_GEKKO.py", line 113, in <module>
zv1 = m.Param(value=ZCALC(n,comps,R0,p,T1,x,y))
File "F:\Codice_GEKKO\compressibilityfactor.py", line 27, in ZCALC
(1-np.sqrt(t/tc[ii])))**2
TypeError: loop of ufunc does not support argument 0 of type GK_Operators which has no callable sqrt method
The first error is biven by the fact that x and y are not list, but they are GEKKO arrays and the second error is due to T1 not being a float (t=T1)
I found out that by using T1.value i can avoid the second error but still i have the first error
I have read the gekko documentation but i haven't been able to find a method to obtain a "standard" python list from a GEKKO array
Thank you in advance for your help
There are two different methods for obtained the value of zv.
Option 1: Initialization Calculation
The first method is to use floating point numbers to obtain a single calculation that can be used for initialization of a parameter. This first method allows any type of functions such as np.roots() or np.sqrt(). The function ZCALC() returns a floating point number. Even though Gekko variables are used as an input, the floating point number is accessed from a scalar variable with T1.value or from an array variable with x[i].value.
def ZCALC(n,comps,R0,p,T1,x,y):
# using the initialized values
t = T1.value
i = 0 # select values from x,y arrays
x1 = x[i].value
y1 = y[i].value
print('t,x[0],y[0] initialized values')
print(t,x1,y1)
# include equations for compressibility factor
w = (1-np.sqrt(t/300))**2
z = np.max(np.roots([1,-1,x1**2,x1*y1]))
# original equations from question
#(1-np.sqrt(t/tc[ii])))**2
#zv=np.max(np.roots([1,-1,(Av-Bv-Bv**2),-Av*Bv]))
return z
zv1 = m.Param(value=ZCALC(n,comps,R0,p,T1,x,y))
Option 2: Implicit Calculation
If the compressibility factor needs to change as T1 and x,y change then use Gekko variables so that the model is compiled with that dependency. The functions are only called during problem initialization. Gekko needs the equations with specific Gekko functions to enable automatic differentiation to provide gradients to the solvers.
def ZCALC2(n,comps,R0,p,T1,x,y):
# using gekko variables
t = T1
i = 0
x1 = x[i] # use index to x array
y1 = y[i] # use index to y array
# use Gekko equations, not Numpy
w = (x1/y1)*(1-m.sqrt(t/300))**2
# set lower bound to get the maximum root
zv = m.Var(value=ZCALC(n,comps,R0,p,T1,x,y),lb=10)
# solve for roots of eq with gekko, not with np.roots
eq = 1-zv+x1**2*zv+x1*y1*zv**3
m.Equation(eq==0)
return zv
zv2 = ZCALC2(n,comps,R0,p,T1,x,y)
Here is a script that shows the two methods:
import numpy as np
m=GEKKO(remote=False)
def ZCALC(n,comps,R0,p,T1,x,y):
# using the initialized values
t = T1.value
i = 0 # select values from x,y arrays
x1 = x[i].value
y1 = y[i].value
print('t,x[0],y[0] initialized values')
print(t,x1,y1)
# include equations for compressibility factor
w = (1-np.sqrt(t/300))**2
z = np.max(np.roots([1,-1,x1**2,x1*y1]))
# original equations from question
#(1-np.sqrt(t/tc[ii])))**2
#zv=np.max(np.roots([1,-1,(Av-Bv-Bv**2),-Av*Bv]))
return z
def ZCALC2(n,comps,R0,p,T1,x,y):
# using gekko variables
t = T1
i = 0
x1 = x[i] # use index to x array
y1 = y[i] # use index to y array
# use Gekko equations, not Numpy
w = (x1/y1)*(1-m.sqrt(t/300))**2
# set lower bound to get the maximum root
zv = m.Var(value=ZCALC(n,comps,R0,p,T1,x,y),lb=10)
# solve for roots of eq with gekko, not with np.roots
eq = 1-zv+x1**2*zv+x1*y1*zv**3
m.Equation(eq==0)
return zv
n = 3
y = m.Array(m.Var,n)
x = m.Array(m.Var,n)
x0 = [0.1,0.2,0.3]
y0 = [0.15,0.25,0.35]
for i in range(n):
y[i].value = y0[i]
x[i].value = x0[i]
T1 = m.Var(value=331, lb=300, ub=900)
comps = ['C2=','C3=','C2H8']; R0 = 8.314; p=10
# define Zv from initialized values (fixed parameter)
zv1 = m.Param(value=ZCALC(n,comps,R0,p,T1,x,y))
# define Zv from Gekko variables (updates with T1,x,y changes)
zv2 = ZCALC2(n,comps,R0,p,T1,x,y)
# initialized value of zv1 does not update with changes in T1,x,y
# initialized value of zv2 does update with changes in T1,x,y
print('initialized value of zv1, zv2')
print(zv1.value,zv2.value)
If the compressibility factor correlations can't be expressed as Gekko equations then try the cspline for 1D or bspline for 2D functions to create an approximation. You may be able to use the bspline function if compressibility can depend on just 2 variables T and x (replace y with an explicit calculation of x).
I'm trying to find the max height of a rocket launched using the equation for the height. I have the derivative already set, so now I need to solve for zero using the derivative. The equation I'm trying to solve for 0 is
-0.0052t^3 + 4.26t + 0.000161534t^3.751
The related code is as follows
def velocity(equation):
time = Symbol('t')
derivative = equation.diff(time)
return derivative
def max_height():
time = Symbol('t')
equ = 2.13 * (time ** 2) - 0.0013 * (time ** 4) + 0.000034 * (time ** 4.751)
return solve(Eq(velocity(equ), 0))
if __name__ == '__main__':
t = Symbol('t')
print(max_height())
I tried inserting the direct equation into the Eq, like so...
return solve(Eq(-0.0052t^3 + 4.26t + 0.000161534t^3.751, 0))
thinking the problem might be with the return type of velocity, but that didn't work. I also tried playing around with making them class functions, but that didn't seem to help either.
The result I'm getting is that it runs indefinitely until I stop it. When I do stop it, I get the following errors
Traceback (most recent call last):
File "C:\Users\...\main.py", line 40, in <module>
print(max_height())
File "C:\Users\...\main.py", line 29, in max_height
return solve(Eq(velocity(equ), 0))
File "C:\Users\...\venv\lib\site-packages\sympy\solvers\solvers.py", line 1095, in solve
solution = _solve(f[0], *symbols, **flags)
File "C:\Users\...\venv\lib\site-packages\sympy\solvers\solvers.py", line 1675, in _solve
u = unrad(f_num, symbol)
File "C:\Users\...\venv\lib\site-packages\sympy\solvers\solvers.py", line 3517, in unrad
neq = unrad(eq, *syms, **flags)
File "C:\Users\...\venv\lib\site-packages\sympy\solvers\solvers.py", line 3300, in unrad
eq = _mexpand(eq, recursive=True)
File "C:\Users\...\venv\lib\site-packages\sympy\core\function.py", line 2837, in _mexpand
was, expr = expr, expand_mul(expand_multinomial(expr))
File "C:\Users\...\venv\lib\site-packages\sympy\core\function.py", line 2860, in expand_mul
return sympify(expr).expand(deep=deep, mul=True, power_exp=False,
File "C:\Users\...\venv\lib\site-packages\sympy\core\cache.py", line 72, in wrapper
retval = cfunc(*args, **kwargs)
File "C:\Users\...\venv\lib\site-packages\sympy\core\expr.py", line 3630, in expand
expr, _ = Expr._expand_hint(
File "C:\Users\...\venv\lib\site-packages\sympy\core\expr.py", line 3555, in _expand_hint
arg, arghit = Expr._expand_hint(arg, hint, **hints)
File "C:\Users\...\venv\lib\site-packages\sympy\core\expr.py", line 3563, in _expand_hint
newexpr = getattr(expr, hint)(**hints)
File "C:\...\venv\lib\site-packages\sympy\core\mul.py", line 936, in _eval_expand_mul
n, d = fraction(expr)
File "C:\...\venv\lib\site-packages\sympy\simplify\radsimp.py", line 1113, in fraction
return Mul(*numer, evaluate=not exact), Mul(*denom, evaluate=not exact)
File "C:\...\venv\lib\site-packages\sympy\core\cache.py", line 72, in wrapper
retval = cfunc(*args, **kwargs)
File "C:\...\venv\lib\site-packages\sympy\core\operations.py", line 85, in __new__
c_part, nc_part, order_symbols = cls.flatten(args)
File "C:\...\venv\lib\site-packages\sympy\core\mul.py", line 523, in flatten
c_part.append(p)
Any help would be greatly appreciated.
There are several problems:
You can't use ^ for exponentiation. In Python you need **. See sympy - gotchas.
Another problem is that you have 3 different variables t. This should be just one variable. If there is only one variable in an equation, equation.diff() automatically uses that one. If there are multiple, you also need to pass the correct variable to your velocity function.
As your equations uses floats, sympy gets very confused as it tries to find exact symbolic solutions, which doesn't work well in floats which are by definition only approximations. Especially the float in the exponent is hard for sympy. To cope, sympy uses a numerical solver nsolve, but which needs a seed value to start number crunching. Depending on the seed, either 0, 40.50 or 87.55 is obtained.
Here is how the updated code could look like:
from sympy import Symbol, Eq, nsolve
def velocity(equation):
derivative = equation.diff()
return derivative
def max_height():
time = Symbol('t')
equ = 2.13 * (time ** 2) - 0.0013 * (time ** 4) + 0.000034 * (time ** 4.751)
return nsolve(Eq(velocity(equ), 0), 30)
print(max_height())
It could help to draw a plot (using lambdify() to make the functions accessible in matplotlib):
from sympy import Symbol, Eq, nsolve, lambdify
def velocity(equation, time):
derivative = equation.diff(time)
return derivative
def get_equation(time):
return 2.13 * (time ** 2) - 0.0013 * (time ** 4) + 0.000034 * (time ** 4.751)
def max_height(equ, time):
return [nsolve(Eq(velocity(equ, time), 0), initial_guess) for initial_guess in [0, 30, 500]]
time = Symbol('t')
equ = get_equation(time)
max_heigths = max_height(equ, time)
equ_np = lambdify(time, equ)
vel_np = lambdify(time, velocity(equ, time))
import matplotlib.pyplot as plt
import numpy as np
xs = np.linspace(0, 105, 2000)
ys = equ_np(xs)
max_heigths = np.array(max_heigths)
plt.plot(xs, equ_np(xs), label='equation')
plt.plot(xs, vel_np(xs), label='velocity')
plt.axhline(0, ls='--', color='black')
plt.scatter(max_heigths, equ_np(max_heigths), s=100, color='red', alpha=0.5, label='extremes')
plt.legend()
plt.show()
I am trying to minimize the portfolio variance using Python's cvxopt. However, after lots of trying, it doesn't seem to work. The function and my code and the error are pasted below. Thanks for helping!
the minimize problem
objective function: min x.dot(sigma_mv).dot(x.T)
the constraint condition is all x>=0, sum(X) = 1
sigma_mv is the covariance matrix of 800*800, dim = 800
code
dim = sigma_mv.shape[0]
P = 2*sigma_mv
q = np.matrix([0.0])
G = -1*np.identity(dim)
h = np.matrix(np.zeros((dim,1)))
sol = solvers.qp(P,q,G,h)
Traceback (most recent call last):
File "<ipython-input-47-a077fa141ad2>", line 6, in <module>
sol = solvers.qp(P,q)
File "D:\spyder\lib\site-packages\cvxopt\coneprog.py", line 4470, in qp
return coneqp(P, q, G, h, None, A, b, initvals, kktsolver = kktsolver, options = options)
File "D:\spyder\lib\site-packages\cvxopt\coneprog.py", line 1822, in coneqp
raise ValueError("use of function valued P, G, A requires a "\
ValueError: use of function valued P, G, A requires a user-provided kktsolver
You have both equality and inequality constraints so you need to provide all the arguments to the built-in qp solver
Gx <=h
Ax=b
Here x>=0 can be written as -x<=0 So, G matrix will look like -1*(Identity matrix)
and h will a 0 vector
Similarly, your A will be an Identity matrix and b will be a unity vector(all elements =1)
Finally, solve expression should look like :
sol=solvers.qp(P, q, G, h, A, b)
I am using python ODE to solve Van der Pol equation. What I want to do is to find the number of times the ODE's derivatives is called during one invocation of integrate(t) method. The solver I am using is isoda. I am trying to find the number of times the derivatives is called during one invocation without the jacbian function and the number of times the jacobian function is called when I include the jacobian function.
def modified_vanderpol_integrator_demo(dt_min=1e-3, dt_max=10., mu=100.):
# define a class to store the number of calls
class F:
def __init__(self):
self.callsydot = 0
self.callsjac = 0
def ydot(self,t, y, mu):
self.callsydot += 1
x, v = y[0], y[1]
return array([v, mu*(1- x*x)*v - x])
def jac(self, t, y, mu):
self.callsjac += 1
return array([[0, 1], [2*mu*v*x - 1, mu*(1-x*x)]])
Dt = linspace(dt_min, dt_max, 1000)
Num_of_times1 = []
Num_of_times2 = []
for dt in Dt:
xinitial = array([1.0, 2.0]) # initial value
f1 = F()
r1 = ode(f1.ydot)
r1.set_integrator('lsoda')
r1.set_initial_value(xinitial)
r1.set_f_params(mu)
r1.integrate(dt)
Num_of_times1.append(f1.callsydot)
f2 = F()
r2 = ode(f2.ydot, f2.jac)
r2.set_integrator('lsoda', with_jacobian=True)
r2.set_initial_value(xinitial)
r2.set_f_params(mu)
r2.integrate(dt)
Num_of_times2.append(f2.callsjac)
plt.plot(Dt, Num_of_times1)
plt.plot(Dt, Num_of_times2)
plt.show()
When I run this script, I got the message
create_cb_arglist: Failed to build argument list (siz) with enough arguments (tot-opt) required by user-supplied function (siz,tot,opt=2,3,0).
Traceback (most recent call last):
File "stiffequations.py", line 118, in <module>
modified_vanderpol_integrator_demo(dt_min=1e-3, dt_max=10., mu=100.)
File "stiffequations.py", line 111, in modified_vanderpol_integrator_demo
r2.integrate(dt)
File "/usr/local/lib/python2.7/dist-packages/scipy/integrate/_ode.py", line 394, in integrate
self.f_params, self.jac_params)
File "/usr/local/lib/python2.7/dist-packages/scipy/integrate/_ode.py", line 1193, in run
y1, t, istate = self.runner(*args)
lsoda.error: failed in processing argument list for call-back jac.
Why this happens and how to fix it?
Thanks.
I have found out what is wrong with my script. I forget to set the parameters in the jacobian equation.
r2.set_jac_params(mu)
I'm trying to use Gaussian quadrature to approximate the integral of a function. (More info here: http://austingwalters.com/gaussian-quadrature/). The first function is on the interval [-1,1]. The second function is generalized to [a,b] by change of variable. The problem is that I keep getting the error "'numpy.ndarray' object is not callable". I assume (please correct me if I'm wrong) this means I've tried to call the arrays w and x as functions, but I'm not sure how to fix this.
This is the code
from __future__ import division
from pylab import *
from scipy.special.orthogonal import p_roots
def gauss1(f,n):
[x,w] = p_roots(n+1)
f = (1-x**2)**0.5
for i in range(n+1):
G = sum(w[i]*f(x[i]))
return G
def gauss(f,a,b,n):
[x,w] = p_roots(n+1)
f = (1-x**2)**0.5
for i in range(n+1):
G = 0.5*(b-a)*sum(w[i]*f(0.5*(b-a)*x[i]+ 0.5*(b+a)))
return G
These are the respective error messages
gauss1(f,4)
Traceback (most recent call last):
File "<ipython-input-82-43c8ecf7334a>", line 1, in <module>
gauss1(f,4)
File "C:/Users/Me/Desktop/hw8.py", line 16, in gauss1
G = sum(w[i]*f(x[i]))
TypeError: 'numpy.ndarray' object is not callable
gauss(f,0,1,4)
Traceback (most recent call last):
File "<ipython-input-83-5603d51e9206>", line 1, in <module>
gauss(f,0,1,4)
File "C:/Users/Me/Desktop/hw8.py", line 23, in gauss
G = 0.5*(b-a)*sum(w[i]*f(0.5*(b-a)*x[i]+ 0.5*(b+a)))
TypeError: 'numpy.ndarray' object is not callable
As Will says you're getting confused between arrays and functions.
You need to define the function you want to integrate separately and pass it into gauss.
E.g.
def my_f(x):
return 2*x**2 - 3*x +15
gauss(m_f,2,1,-1)
You also don't need to loop as numpy arrays are vectorized objects.
def gauss1(f,n):
[x,w] = p_roots(n+1)
G=sum(w*f(x))
return G
def gauss(f,n,a,b):
[x,w] = p_roots(n+1)
G=0.5*(b-a)*sum(w*f(0.5*(b-a)*x+0.5*(b+a)))
return G
quadpy, a small project of mine, might help:
import numpy
import quadpy
def f(x):
return numpy.exp(x)
scheme = quadpy.line_segment.gauss_legendre(10)
val = scheme.integrate(f, [0.0, 1.0])
print(val)
1.7182818284590464
There are many other quadrature schemes for 1D.
In gauss1(f,n), you are treating f as if it's a function when it is an array, since you are reassigning it;
def gauss1(f,n):
[x,w] = p_roots(n+1)
f = (1-x**2)**0.5 # This line is your problem.
for i in range(n+1):
G = sum(w[i]*f(x[i]))
return G
You are doing something similar in the second function.
Example: solving using gaussian integral with n = 2 for integral 2+sinX with b = pi/2 and a = 0
import numpy as np
E = np.array([-0.774597, 0.000000, 0.774597])
A = np.array([0.555556, 0.888889, 0.555556])
def gauss(f, a, b, E, A):
x = np.zeros(3)
for i in range(3):
x[i] = (b+a)/2 + (b-a)/2 *E[i]
return (b-a)/2 * (A[0]*f(x[0]) + A[1]*f(x[1]) + A[2]*f(x[2]))
f = lambda x: 2 + np.sin(x)
a = 0.0; b = np.pi/2
areaGau = gauss(f, a, b, E, A)
print("Gaussian integral: ", areaGau)