Problem solving a minimization problem in cvxpy - python

I have a linear optimization problem, which can be expressed in a cost function code like this:
value_to_minimize = 0.0;
for i in range(0, len(v_1)):
value_to_minimize += np.abs(v_1[i] - (v_2[i] * c1 + v_3[i] * c2 + v_4[i] * c3));
The task of the solver should be to find values for the variables c1, c2, c3 which minimize the value. As boundary conditions, c1, c2, c3 together should result in 1.0 and not be negative.
v_1, v_2, v_3 and v_4 are vectors with 10000 float values.
Here is the outline to solve this minimization problem in cvxpy, but without the parameter pass in cp.Minimize(...):
V1 = np.array(v_1).reshape(10000, 1)
V2 = np.array(v_2 + v_3 + v_4).reshape(10000, 3)
c = cp.Variable((3,1),nonneg=True)
prob = cp.Problem(cp.Minimize(..., # ???
[sum(c) == 1]))
prob.solve(verbose=True)
How would the minimize function for cvxpy look in that case?

If you don't mind using another library, I would recommend scipy for this one:
from scipy.optimize import minimize
import numpy as np
def OF(x0, v_1, v_2, v_3, v_4):
value_to_minimize = 0.0
for i in range(0, len(v_1)):
value_to_minimize += np.abs(v_1[i] - (v_2[i] * x0[0] + v_3[i] * x0[1] + v_4[i] * x0[2]))
return value_to_minimize
if __name__ == '__main__':
x0 = np.array([0, 0, 0])
v_1 = np.random.randint(10, size = 10000)
v_2 = np.random.randint(10, size = 10000)
v_3 = np.random.randint(10, size = 10000)
v_4 = np.random.randint(10, size = 10000)
minx0 = np.repeat(0, [len(x0)] , axis = 0)
maxx0 = np.repeat(np.inf, [len(x0)] , axis = 0)
bounds = tuple(zip(minx0, maxx0))
cons = {'type':'eq',
'fun':lambda x0: 1 - sum(x0)}
res_cons = minimize(OF, x0, (v_1, v_2, v_3, v_4), bounds = bounds, constraints=cons, method='SLSQP')
print(res_cons)
print('Current value of objective function: ' + str(res_cons['fun']))
print('Current value of controls:')
print(res_cons['x'])
Output is:
fun: 27919.666908810435
jac: array([5092. , 5672. , 5108.39868164])
message: 'Optimization terminated successfully.'
nfev: 126
nit: 21
njev: 21
status: 0
success: True
x: array([0.33333287, 0.33333368, 0.33333345])
Current value of objective function: 27919.666908810435
Current value of controls:
[0.33333287 0.33333368 0.33333345]
But obviously the actual values here do not mean much since I just used random integers for the v_ values... just a demo that this model would meet your constraint of c values adding to 1 and boundary of not less than zero (negative).
edit update: did not look at the OF/constraints closely enough to realize this was a linear problem... should be using a linear solver algorithm (though, you can use a nonlinear, it's overkill though).
scipy's linear solvers are not great for complex optimization problems like this one, reverting back to cvxpy :
import numpy as np
import cvxpy as cp
# Create two scalar optimization variables.
x = cp.Variable()
y = cp.Variable()
z = cp.Variable()
v_1 = np.random.randint(10, size = 10000)
v_2 = np.random.randint(10, size = 10000)
v_3 = np.random.randint(10, size = 10000)
v_4 = np.random.randint(10, size = 10000)
constraints = [x + y + z == 1, x >= 0, y >= 0, z >= 0]
objective = cp.Minimize(cp.sum(cp.abs(v_1 - (v_2 * x + v_3 * y + v_4 * z))))
prob = cp.Problem(objective, constraints)
print("Value of OF:", prob.solve())
print('Current value of controls:')
print(x.value, y.value, z.value)
output:
Value of OF: 27621.999978414093
Current value of controls:
0.3333333333016109 0.33333333406414983 0.3333333326298208

I strongly recommend removing one of the parameters and a constraint. If you know that c1 + c2 + c3 = 1., then use c3 = 1. - c1 - c2! This makes the task of minimizer much easier. Also if v_1 etc. are numpy arrays, then use them as arrays, e.g.,
c3 = 1. - c1 - c2
value_to_minimize = np.sum(np.abs(v_1 - (v_2 * c1 + v_3 * c2 + v_4 * c3)))

Related

How to fit a rotated and translated hyperbola to a set of x,y points in Python

I want to fit a set of data points in the xy plane to the general case of a rotated and translated hyperbola to back out the coefficients of the general equation of a conic.
I've tried the methodology proposed in here but so far I cannot make it work.
When fitting to a set of points known to be a hyperbola I get quite different outputs.
What I'm doing wrong in the code below?
Or is there any other way to solve this problem?
import numpy as np
from sympy import plot_implicit, Eq
from sympy.abc import x, y
def fit_hyperbola(x, y):
D1 = np.vstack([x**2, x*y, y**2]).T
D2 = np.vstack([x, y, np.ones(len(x))]).T
S1 = D1.T # D1
S2 = D1.T # D2
S3 = D2.T # D2
# define the constraint matrix and its inverse
C = np.array(((0, 0, -2), (0, 1, 0), (-2, 0, 0)), dtype=float)
Ci = np.linalg.inv(C)
# Setup and solve the generalized eigenvector problem
T = np.linalg.inv(S3) # S2.T
S = Ci#(S1 - S2#T)
eigval, eigvec = np.linalg.eig(S)
# evaluate and sort resulting constraint values
cond = eigvec[1]**2 - 4*eigvec[0]*eigvec[2]
# [condVals index] = sort(cond)
idx = np.argsort(cond)
condVals = cond[idx]
possibleHs = condVals[1:] + condVals[0]
minDiffAt = np.argmin(abs(possibleHs))
# minDiffVal = possibleHs[minDiffAt]
alpha1 = eigvec[:, idx[minDiffAt + 1]]
alpha2 = T#alpha1
return np.concatenate((alpha1, alpha2)).ravel()
if __name__ == '__main__':
# known hyperbola coefficients
coeffs = [1., 6., -2., 3., 0., 0.]
# hyperbola points
x_ = [1.56011303e+00, 1.38439984e+00, 1.22595618e+00, 1.08313085e+00,
9.54435408e-01, 8.38528681e-01, 7.34202759e-01, 6.40370424e-01,
5.56053814e-01, 4.80374235e-01, 4.12543002e-01, 3.51853222e-01,
2.97672424e-01, 2.49435970e-01, 2.06641170e-01, 1.68842044e-01,
1.35644673e-01, 1.06703097e-01, 8.17157025e-02, 6.04220884e-02,
4.26003457e-02, 2.80647476e-02, 1.66638132e-02, 8.27872926e-03,
2.82211172e-03, 2.37095181e-04, 4.96740239e-04, 3.60375275e-03,
9.59051203e-03, 1.85194083e-02, 3.04834928e-02, 4.56074477e-02,
6.40488853e-02, 8.59999904e-02, 1.11689524e-01, 1.41385205e-01,
1.75396504e-01, 2.14077865e-01, 2.57832401e-01, 3.07116093e-01,
3.62442545e-01, 4.24388335e-01, 4.93599021e-01, 5.70795874e-01,
6.56783391e-01, 7.52457678e-01, 8.58815793e-01, 9.76966133e-01,
1.10813998e+00, 1.25370436e+00]
y_ = [-0.66541515, -0.6339625 , -0.60485332, -0.57778425, -0.5524732 ,
-0.52865638, -0.50608561, -0.48452564, -0.46375182, -0.44354763,
-0.42370253, -0.4040097 , -0.38426392, -0.3642594 , -0.34378769,
-0.32263542, -0.30058217, -0.27739811, -0.25284163, -0.22665682,
-0.19857079, -0.16829086, -0.13550147, -0.0998609 , -0.06099773,
-0.01850695, 0.02805425, 0.07917109, 0.13537629, 0.19725559,
0.26545384, 0.34068177, 0.42372336, 0.51544401, 0.61679957,
0.72884632, 0.85275192, 0.98980766, 1.14144182, 1.30923466,
1.49493479, 1.70047747, 1.92800474, 2.17988774, 2.45875143,
2.76750196, 3.10935692, 3.48787892, 3.90701266, 4.3711261 ]
plot_implicit (Eq(coeffs[0]*x**2 + coeffs[1]*x*y + coeffs[2]*y**2 + coeffs[3]*x + coeffs[4]*y, -coeffs[5]))
coeffs_fit = fit_hyperbola(x_, y_)
plot_implicit (Eq(coeffs_fit[0]*x**2 + coeffs_fit[1]*x*y + coeffs_fit[2]*y**2 + coeffs_fit[3]*x + coeffs_fit[4]*y, -coeffs_fit[5]))
The general equation of hyperbola is defined with 5 independent coefficients (not 6). If the model equation includes dependant coefficients (which is the case with 6 coefficients) trouble might occur in the numerical regression calculus.
That is why the equation A * x * x + B * x * y + C * y * y + D * x + F * y = 1 is considered in the calculus below. The fitting is very good.
Then one can goback to the standard equation a * x * x + 2 * b * x * y + c * y * y + 2 * d * x + 2 * f * y + g = 0 in setting a value for g (for example g=-1).
The formulas to find the coordinates of the center, the equations of asymptotes, the equations of axis, are given in addition.
https://mathworld.wolfram.com/ConicSection.html
https://en.wikipedia.org/wiki/Conic_section
https://en.wikipedia.org/wiki/Hyperbola

Solve non-linear non homogeneous differential equation with python (Duffing oscillator)

I try to solve the Duffing equation using odeint:
def func(z, t):
q, p = z
return [p, F*np.cos(OMEGA * t) + Fm*np.cos(3*OMEGA * t) + 2*gamma*omega*p - omega**2 * q - beta * q**3]
OMEGA = 1.4
T = 1 / OMEGA
F = 0.2
gamma = 0.1
omega = -1.0
beta = 0.0
Fm = 0
z0 = [1, 0]
psi = 1 / ((omega**2 - OMEGA**2)**2 + 4*gamma**2*OMEGA**2)
wf = np.sqrt(omega**2 - gamma**2)
t = np.linspace(0, 100, 1000)
sol1 = odeintw(func, z0, t, atol=1e-13, rtol=1e-13, mxstep=1000)
When F = gamma = beta = 0 we have a system of two linear homogeneous equations. It's simple!
But when F not equal 0 the system becomes non homogeneous. The problem is that the numerical solution does not coincide with the analytical one:
Figure 1
the numerical solution does not take into account the inhomogeneous solution of the equation.
I have not been able to figure out if it is possible to use here solve_bvp function. Could you help me?
Inserting the constants, the equation becomes
x'' + 2*c*x' + x = F*cos(W*t)
The general solution form is
x(t)=A*cos(W*t)+B*sin(W*t)+exp(-c*t)*(C*cos(w*t)+D*sin(w*t))
w^2=1-c^2
for the particular solution one gets
-W^2*(A*cos(W*t)+B*sin(W*t))
+2*c*W*(B*cos(W*t)-A*sin(W*t))
+ (A*cos(W*t)+B*sin(W*t))
=F*cos(W*t)
(1-W^2)*A + 2*c*W*B = F
-2*c*W*A + (1-W^2)*B = 0
For the initial conditions it is needed that
A+C = 1
W*B-c*C+w*D=0
In python code thus
F=0.2; c=0.1; W=1.4
w=(1-c*c)**0.5
A,B = np.linalg.solve([[1-W*W, 2*c*W],[-2*c*W,1-W*W]], [F,0])
C = 1-A; D = (c*C-W*B)/w
print(f"w={w}, A={A}, B={B}, C={C}, D={D}")
with the output
w=0.99498743710662, A=-0.192, B=0.056, C=1.192, D=0.04100554286257586
continuing
t = np.linspace(0, 100, 1000)
u=odeint(lambda u,t:[u[1], F*np.cos(W*t)-2*c*u[1]-u[0]], [1,0],t, atol=1e-13, rtol=1e-13)
plt.plot(t,u[:,0],label="odeint", lw=3);
plt.plot(t,A*np.cos(W*t)+B*np.sin(W*t)+np.exp(-c*t)*(C*np.cos(w*t)+D*np.sin(w*t)), label="exact");
plt.legend(); plt.grid(); plt.show();
results in an exact fit

How to implement 'solve_ivp' with vectorized='True' in python

I've been trying to solve a set of differential equations using solve_ivp. The Jacobian matrix of the system is the A as you can see below. I wanted to enable the option vectorized='True' but unfortunately i do not know how to modify the present code to vectorize the Jacobian matrix A. Does anyone know how this can be done?
# imports
import numpy as np
import scipy.sparse as sp
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt
# grid sizing
R=0.05 #sphere radius
N=1000#number of points
D=0.00002 #diffusion coefficient
k=10 # Arrhenius
Cs=1.0 # Boundary concentration
C0=0.0 # Initial concentration
time_constant=R**2.0/D
dr=R/(N-1)
# Algebra simplification
a=D/dr**2
Init_conc=np.linspace(0,0,N)
B=np.zeros(N)
B[N-1]=Cs*(a+a/(N-1))
#
e1 = np.ones(N)
e2 = np.ones(N)
e3 = np.ones(N)
#
#
#
e1[0]=-k-6*a
e1[1:]=-k-2*a
#
#
e2[1]=6*a
for i in range(2,N) :
e2[i]=a+a/(i-1)
#
#
#
for i in range (0,N-1) :
e3[i]=a-a/(i+1)
A = sp.spdiags([e3,e1,e2],[-1,0,1],N,N,format="csc")
def dc_dt(t,C) :
dc=A.dot(C)+B
return dc
# Solving the system, I want to implement the same thing with vectorized='True'
OutputTimes=np.linspace(0,0.2*time_constant,100)
ans=solve_ivp(dc_dt,(0,0.2*time_constant),Init_conc,method='RK45',t_eval=OutputTimes,vectorized='False')
print (ans)
Please have a look at this answer, the explanation is thorough. For your code in particular, please see below for updated snippet and figure. It is not obvious that vectorize is providing any speed-up. However, providing A for the keyword jac makes a difference. But I guess it is only valid if A is constant?
# imports
import numpy as np
import scipy.sparse as sp
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt # noqa
def dc_dt(t, C):
print(C.shape)
if len(C.shape) == 1:
return np.squeeze(A.dot(C)) + B
else:
return A.dot(C) + np.transpose(np.tile(B, (C.shape[1], 1)))
# return np.squeeze(A.dot(C)) + B
# grid sizing
R = 0.05 # sphere radius
N = 1000 # number of points
D = 0.00002 # diffusion coefficient
k = 10 # Arrhenius
Cs = 1.0 # Boundary concentration
C0 = 0.0 # Initial concentration
time_constant = R**2.0 / D
dr = R / (N - 1)
# Algebra simplification
a = D / dr**2
Init_conc = np.repeat(0, N)
B = np.zeros(N)
B[-1] = Cs * (a + a / (N - 1))
e1 = np.ones(N)
e2 = np.ones(N)
e3 = np.ones(N)
e1[0] = -k - 6 * a
e1[1:] = -k - 2 * a
e2[1] = 6 * a
for i in range(2, N):
e2[i] = a + a / (i - 1)
for i in range(0, N - 1):
e3[i] = a - a / (i + 1)
A = sp.spdiags([e3, e1, e2], [-1, 0, 1], N, N, format="csc")
# Solving the system, I want to implement the same thing with vectorized='True'
OutputTimes = np.linspace(0, 0.2 * time_constant, 10000)
ans = solve_ivp(dc_dt, (0, 0.2 * time_constant), Init_conc,
method='BDF', t_eval=OutputTimes, jac=A, vectorized=True)
plt.plot(np.arange(N), ans.y[:, 0])
plt.plot(np.arange(N), ans.y[:, 1])
plt.plot(np.arange(N), ans.y[:, 10])
plt.plot(np.arange(N), ans.y[:, 20])
plt.plot(np.arange(N), ans.y[:, 50])
plt.plot(np.arange(N), ans.y[:, -1])
plt.show()

Solve underdetermined linear equation Ax + By = C where y is constrained by x

I am new to optimization and have been struggling to solve for variable x and y in linear equation Ax +By = C, while y is constrained by the solution of x.
An example of the problem can be:
A = np.random.rand(20,100)
B = np.random.rand(20,200)
C = np.random.rand(20)
Solve for x and y so that Ax +By = C, with constraints that x is non-negative and -0.7*x0 < y0,y1 <0.7*x0, -0.7*x1 < y2,y3 <0.7*x1... ( -0.7x[i] < y[2i],y[2i+1]<0.7x[i] )
I would really appreciate if someone can recommend me a python package that solve this problem, or some way to transform my problem into a more conventional format that can be solved directly with libraries like Scipy.optimize
I solved the problem using CVXPY:
import cvxpy as cp
import numpy as np
m = 20
n = 100
A = np.random.rand(m,n)
B = np.random.rand(m,n)
C = np.random.rand(m,n)
d = np.random.rand(m)
# construct the problem.
x = cp.Variable(n)
y = cp.Variable(n)
z = cp.Variable(n)
objective = cp.Minimize(cp.sum_squares(A*x + B*y + C*z -d))
constaints = [0 <= x, x <= 1, y <= 0.7*x, y >= -0.7*x, z <= 0.7*x, z >= -0.7*x]
prob = cp.Problem(objective, constraints)
result = prob.solve()
It depends on what do you mean by solve, what you have described have multiple solutions and it is the interior of a polyhedral.
It is a linear programming problem if you are willing to convert the problem to say
-0.7x_0 <=y_0 <= 0.7x_0
Suppose not, consider introducing a small positive number m to make a linear program.
-0.7x_0 + m <=y_0 <= 0.7x_0 - m
You can then use scipy.optimize.linprog to solve a linear program.
If you are just interested in a particular solution, you can just set c in the objective function to be zero.
Edit:
import numpy as np
from scipy.optimize import linprog
m_size = 20
bound = 0.7
x_size = 100
y_size = 2 * x_size
small_m = 0
A = np.random.rand(m_size, x_size)
B = np.random.rand(m_size, y_size)
C = np.random.rand(m_size)
A_eq = np.hstack((A, B))
b_eq = C
sub_block = np.array([-bound, -bound ])
sub_block.shape = (2,1)
block = np.kron(np.eye(x_size), sub_block)
upper_block = np.hstack((block, - np.eye(y_size)))
lower_block = np.hstack((block, np.eye(y_size)))
A_ub = np.vstack((upper_block, lower_block))
b_ub = -small_m * np.ones( 2 * y_size)
bounds = tuple([tuple([0, None]) for i in range(x_size)] + [tuple([None, None]) for i in range(y_size)])
c = [0 for i in range(x_size+y_size)]
res = linprog(c, A_ub = A_ub, b_ub = b_ub, A_eq = A_eq, b_eq = b_eq, bounds = bounds)
x_part = res.x[:x_size]
y_part = res.x[x_size:]

Error in Optimization with Lagrange Multiplier

I'm trying to maximize/minimize a function with two variables using Lagrange Multiplier method, below is my code
import numpy as np
from scipy.optimize import fsolve
Sa = 200
Sm = 100
n = 90
mu1 = 500
mu2 = 150
sigma1 = 25
sigma2 = 10
f = 0.9
def func(X):
u1 = X[0]
u2 = X[1]
L = X[2] # this is the multiplier. lambda is a reserved keyword in python
'function --> f(u1,u2) = u1**2 + u2**2'
'constraint --> g(u1,u2) = (Snf/a)**1/b - n = 0'
Snf = Sa/(1-Sm/(sigma1*u1 + mu1))
a = (f*(sigma1*u1 + mu1)**2)/(sigma2*u2 + mu2)
b = -1/3*(f*(sigma1*u1 + mu1))/(sigma2*u2 + mu2)
return (u1**2+u2**2 - L * ((Snf/a)**1/b) - n)
def dfunc(X):
dLambda = np.zeros(len(X))
h = 1e-3 # this is the step size used in the finite difference.
for i in range(len(X)):
dX = np.zeros(len(X))
dX[i] = h
dLambda[i] = (func(X+dX)-func(X-dX))/(2*h);
return dLambda
# this is the max
X1 = fsolve(dfunc, [1, 1, 0])
print (X1, func(X1))
# this is the min
X2 = fsolve(dfunc, [-1, -1, 0])
print (X2, func(X2))
When I try to do a simple function as the constraint such as u1+u2=4 or u1^2+u2^2 = 20, it works just fine , but when I try my actual constraint function it always gives this error, is there a reason why?? THanks for the help
C:\Python34\lib\site-packages\scipy\optimize\minpack.py:161:
RuntimeWarning: The iteration is not making good progress, as measured by the
improvement from the last five Jacobian evaluations.
warnings.warn(msg, RuntimeWarning)

Categories

Resources