I'm using scipy.optimize.linprog library to calculate the minimization using the simplex method. I'm working on this problem in my textbook and I'm hoping someone can point me in the right direction because I'm not getting the output I expect. The problem is:
Minimize w = 10*y1 + 15*y2 + 25*y3
Subject to: y1 + y2 + y3 >= 1000
y1 - 2*y2 >= 0
y3 >= 340
with y1 >= 0, y2 >= 0
The code I wrote for this is:
import numpy as np
import pandas as pd
from scipy.optimize import linprog
A = np.array([
[1, 1, 1],
[1,-2, 0],
[0, 0, 1]])
b = np.array([1000,0,340])
c = np.array([-10,-15,-25])
res = linprog(c, A_ub=A, b_ub=b,
bounds=(0, None))
print('Optimal value:', res.fun, '\nX:', res.x)
Which gives the output:
Optimal value: -18400.0
X: [ 0. 660. 340.]
I expect it to be:
Optimal value: -15100.0
X: [ 660. 0. 340.]
I can't seem to find consistency with this function but maybe it's the way I'm using it.
You've set up the inputs slightly wrong; see the manual. Specifically, you have a number of sign errors.
Your vector c has the wrong sign; linprog minimizes c x so c should just be the coefficients in w = c x
Your vector b and matrix A have the wrong sign. Their signs should be inverted to switch from your form of constraint f(x) >= const to the desired form for the linprog method, which is a less-than-or-equal, i.e. -f(x) <= - const
You are missing the final two constraints.
Your proposed minimum is < 0, which is obviously impossible as
w = 10*x1 + 15*x2 + 25*x3 is always positive with your constraints as x1,x2,x3>=0.
The correct code reads:
import numpy as np
from scipy.optimize import linprog
A = np.array([[-1, -1, -1], [-1,2, 0], [0, 0, -1], [-1, 0, 0], [0, -1, 0]])
b = np.array([-1000, 0, -340, 0, 0])
c = np.array([10,15,25])
res = linprog(c, A_ub=A, b_ub=b,bounds=(0, None))
print('Optimal value:', res.fun, '\nX:', res.x)
# python2
# ('Optimal value:', 15100.0, '\nX:', array([ 660., 0., 340.]))
# python3
# Optimal value: 15099.999961403426
# X: [6.59999996e+02 1.00009440e-07 3.40000000e+02]
As the positiveness of y1 and y2 can be guaranteed under bounds=(0, None), a simplified version of code is shown as below:
import numpy as np
from scipy.optimize import linprog
A = np.array([[-1, -1, -1], [-1,2, 0], [0, 0, -1]])
b = np.array([-1000, 0, -340])
c = np.array([10,15,25])
res = linprog(c, A_ub=A, b_ub=b,bounds=(0, None))
print('Optimal value:', res.fun, '\nX:', res.x)
Output:
Optimal value: 15099.999961403195
X: [6.59999996e+02 1.00009440e-07 3.40000000e+02]
Related
I am new to the world of CVXPY and have run in to an issue with an optimization problem. In it I need to derive an expression based on the optimized variable and dividing the variable with the expression - simplified but like this:
import cvxpy as cvx
x = cvx.Variable(1,nonneg=True)
y = cvx.sqrt(x)
print("y is DCP:" + str(y.is_dcp()))
z = x/y # y is x dependent so not dcp
print("z is DCP:" + str(z.is_dcp()))
objective = cvx.Maximize(cvx.sum(z))
probl = cvx.Problem(objective, [x<=10])
probl.solve(verbose=True)
Looking at the rules for DCP optimization I realize that variable/variable division is not DCP. My question if therefore if someone has a solution or workI am noew to the world ov cvxpy and have d for this issue?
Inputing a constant in place for y in z obviously fixes the issue. However, I need to optimize on an expression based on the variable. Is there a way to do this?
I added the example above for simplicity, but my problem is more in line with the following:
import numpy as np
import cvxpy as cvx
import warnings
warnings.simplefilter(action='ignore')
ratio = np.array([-1.95844359, -7.14519994, 0.08811825, 2.92089828, 2.87685278,
-3.13022284, -1.12513724, 3.72449473, -2.68733876, 2.31347068,
4.06927235, -5.38002868, 2.18026303, -2.95228569, -7.00564848,
-3.19870931, -2.1249305 ])
category = np.array([[0,0, 1, 0],
[0,0, 0, 1],
[0,1, 0, 0],
[1,0, 0, 0],
[0,1, 0, 0],
[0,0, 1, 0],
[0,0, 1, 0],
[0,1, 0, 0],
[0,0, 1, 0],
[1,0, 0, 0],
[0,1, 0, 0],
[0,0, 1, 0],
[1,0, 0, 0],
[0,0, 1, 0],
[0,0, 0, 1],
[0,0, 1, 0],
[0,0, 1, 0]])
x = cvx.Variable(17,nonneg=True)
constraints = [cvx.sum(x) == 1]
constraints += [cvx.max(x.T*category) <= 0.34]
x2 =x.copy()
category_weight = x2.T*category # Category weights
category_weight.is_dcp()
category_weight_x = category_weight*category.T
category_weight_x.is_dcp() # Category weight for each x
category_weight_x = cvx.sum(category_weight_x,axis = 1)
# sum over rows to get (len(x),)
category_weight_x_inv = cvx.inv_pos(category_weight_x)
category_weight_x_inv.is_dcp() #1/n
# PROBLEM:
x_category_weight = x2/category_weight_x # category weight_x is not constant - not allowed!
x_category_weight.is_dcp()
#
ratio_weighted_opt = ratio*x_category_weight.T #Get Ratio value for x in category
ratio_category_opt = ratio_weighted_opt.T*category #split ratio to category columns
ratio_category_opt_cap = cvx.pos(ratio_category_opt) #set negativ to 0
ratio_category_opt_cap.is_dcp()
ratio_category_opt_cap = cvx.pos(1-ratio_category_opt) #set bigger than 1 to 1
ratio_category_opt_cap +=1
ratio_category_opt_cap.is_dcp()
ratio_category_opt_cap_category = ratio_category_opt_cap*category_weight #multiply with category weight to total
objective = cvx.Maximize(cvx.sum(ratio_weighted_opt))
probl = cvx.Problem(objective, constraints)
probl.solve(verbose=True)
I have a dictionary with a tuple as key and a result as value.
I'm looking for a way to "solve" as many keys as possible, even if it's not possible to "solve" all of then.
input : {(A, C):1,(A, B, C): 1}
output : {(A, C):1, (A, B, C): 1, B:0}
in other word :
modified input :
1*A + 0*B + 1*C = 1
1*A + 1*B + 1*C = 1
output :
A = ?
B = 0
C = ?
I can only use numpy and scipy.
I tried this, but it must be square matrix :
import numpy as np
a = np.array([[1, 0, 1], [1, 1, 1]])
b = np.array([1, 1])
from scipy import linalg
x = linalg.solve(a, b)
print(x)
Do you have ideas where I should look at ?
this code does the trick, but it's not very 'clean'
import numpy as np
A=np.array([[1, 0, 1], [1, 1, 1]])
B=np.array([1,1])
s = solutionNonSquare = np.linalg.lstsq(A, B)[0]
for i,val in enumerate(s):
if val < 0.0001:
print('x[',i,'] = 0')
else:
print('x[',i,'] = ?')
print(s)
Thanks a lot for your smartness
Matrix a is Non square Matrix, so it does not have full Rank. However, we can compute pseudo inverse, using np.linalg.pinv and then we can compute x = np.matmul(a_psudo_inv, b)
import numpy as np
a = np.array([[1, 0, 1], [1, 1, 1]])
b = np.array([1, 1])
from numpy import linalg
a_psudo_inv = np.linalg.pinv(a)
print(a_psudo_inv)
print(a_psudo_inv.shape)
x = np.matmul(a_psudo_inv, b)
print(x)
Solution >> [5.00000000e-01 5.55111512e-16 5.00000000e-01]
I'm trying to use the optimization module in SciPy to solve constrained optimization problem. I need to implement the 'hess' argument. In scipy's documentation and tutorial, their hessian are simply [[2, 0], [0, 0]] and [[2, 0], [0, 0]]. However, my hessian is something like [[(-24)*x[0]**2 + 48*x[0]-16, 0], [0, 0]] and [[(-48)*x[0]**2 + 192*x[0]-176, 0], [0, 0]] so that I cannot simply use numpy.array to do multiplication. It seems that I should send a LinearOperator object to the 'hess' arguement. Examples of using LinearOperator is unclear in both scipy.optimize tutorial and LinearOperator documentation since they only show examples of lower dimension. I'm wondering how to correctly use it?
The problem formulation is
my code is:
import numpy as np
from scipy.optimize import Bounds
from scipy.optimize import NonlinearConstraint
from scipy.optimize import minimize
def f(x):
return (-x[0]-x[1])
def grad(x):
return np.array([-1, -1])
def hess(x):
return np.array([[0, 0], [0, 0]])
def cons_f(x):
return [(-2)*x[0]**4 + 8*x[0]**3 + (-8)*x[0]**2 + x[1] -2, (-4)*x[0]**4 + 32*x[0]**3 + (-88)*x[0]**2 + 96*x[0] + x[1] -36]
def cons_Jacobian(x):
return [[(-8)*x[0]**3 + 24*x[0]**2 - 16*x[0], 1], [(-16)*x[0]**3 + 96*x[0]**2 - 176*x[0] +96, 1]]
def cons_Hessian(x,v):
# TODO
return v[0]*[[(-24)*x[0]**2 + 48*x[0]-16, 0], [0, 0]] + v[1]*[[(-48)*x[0]**2 + 192*x[0]-176, 0], [0, 0]]
nonlinear_constraint = NonlinearConstraint(cons_f, -np.inf, 0, jac=cons_Jacobian, hess=cons_Hessian)
bounds = Bounds([0, 0], [3.0, 4.0])
x0 = np.array([0.5, 1])
res = minimize(f, x0, method='trust-constr', jac=grad, hess=hess,
constraints=[nonlinear_constraint],bounds=bounds)
The cons_Hessian(x,v)is absolutely wrong in my code.
In their example, although hessians are simply[[2, 0], [0, 0]] and [[2, 0], [0, 0]], the usage is confusing. I don't understand where p comes in.
from scipy.sparse.linalg import LinearOperator
def cons_H_linear_operator(x, v):
def matvec(p):
return np.array([p[0]*2*(v[0]+v[1]), 0])
return LinearOperator((2, 2), matvec=matvec)
nonlinear_constraint = NonlinearConstraint(cons_f, -np.inf, 1,
jac=cons_J, hess=cons_H_linear_operator)
There's no need to use a LinearOperator. You only need to ensure that cons_f, cons_Jacobian and cons_Hessian return np.ndarrays. That's the reason why you can't evaluate your cons_Hessian. Additionally, it's highly recommended to use double literals instead of integers, i.e. -2.0 instead of 2 to prevent that the function returns np.ndarrays with a integer dtype.
Your example works for me by writing these functions as follows:
def cons_f(x):
con1 = (-2.0)*x[0]**4 + 8*x[0]**3 + (-8)*x[0]**2 + x[1] - 2
con2 = (-4)*x[0]**4 + 32*x[0]**3 + (-88)*x[0]**2 + 96*x[0] + x[1] -36
return np.array([con1, con2])
def cons_Jacobian(x):
con1_grad = [(-8.0)*x[0]**3 + 24*x[0]**2 - 16*x[0], 1]
con2_grad = [(-16)*x[0]**3 + 96*x[0]**2 - 176*x[0] +96, 1]
return np.array([con1_grad, con2_grad])
def cons_Hessian(x,v):
con1_hess = np.array([[(-24.0)*x[0]**2 + 48*x[0]-16, 0], [0, 0]])
con2_hess = np.array([[(-48)*x[0]**2 + 192*x[0]-176, 0], [0, 0]])
return v[0]*con1_hess + v[1]*con2_hess
import numpy as np
import sympy as sp
from sympy import *
init_printing()
uVars = list(symbols(', '.join([f'u{n}' for n in range(1, 3 + 1)])))
aVars = list(symbols(', '.join([f'a{n}' for n in range(1, 3 + 1)])))
lambda1, mu = symbols('lambda, mu')
U = np.array([ [0, -uVars[2], uVars[1]], [uVars[2], 0, -uVars[0]], [-uVars[1], uVars[0], 0] ])
a = np.array([ [aVars[0], 0, 0], [0, aVars[1], 0], [0, 0, aVars[2]] ])
I = np.eye(3)
L = a*lambda1 + U
preCharPoly = L - mu*I
preCharPoly_sym = sp.Matrix(preCharPoly)
factor(preCharPoly_sym.det())
The above code outputs the following polynomial:
However, I require the polynomial to be factored with respect to the variables lambda and mu as shown here:
I have been examining the documentation at https://docs.sympy.org/latest/modules/simplify/simplify.html but cannot figure out how to do what is desired. How do I specify factor() or simplify() to perform their tasks with respect to lambda and mu?
What is the easiest/fastest way to take a weighted sum of values in a numpy array?
Example: Solving the heat equation with the Euler method
length_l=10
time_l=10
u=zeros((length_l,length_l))# (x,y)
u[:, 0]=1
u[:,-1]=1
print(u)
def dStep(ALPHA=0.1):
for position,value in ndenumerate(u):
D2u= (u[position+(1,0)]-2*value+u[position+(-1, 0)])/(1**2) \
+(u[position+(0,1)]-2*value+u[position+( 0,-1)])/(1**2)
value+=ALPHA*D2u()
while True:
dStep()
print(u)
D2u should be the second central difference in two dimensions. This would work if I could add indexes like (1,4)+(1,3)=(2,7). Unfortunately, python adds them as (1,4)+(1,3)=(1,4,1,3).
Note that computing D2u is equivalent to taking a dot product with this kernel centered around the current position:
0, 1, 0
1,-4, 1
0, 1, 0
Can this be vectorised as a dot product?
I think you want something like:
import numpy as np
from scipy.ndimage import convolve
length_l = 10
time_l = 10
u = np.zeros((length_l, length_l))# (x,y)
u[:, 0] = 1
u[:, -1] = 1
alpha = .1
weights = np.array([[ 0, 1, 0],
[ 1, -4, 1],
[ 0, 1, 0]])
for i in range(5):
u += alpha * convolve(u, weights)
print(u)
You could reduce down a bit by doing:
weights = alpha * weights
weights[1, 1] = weights[1, 1] + 1
for i in range(5):
u = convolve(u, weights)
print(u)