I am still very new to Python, after years and years of Matlab. I am trying to use Pulp to set up an integer linear program.
Given an array of numbers:
{P[i]:i=1...N}
I want to maximize:
sum( x_i P_i )
subject to the constraints
A x <= b
A_eq x = b_eq
and with bounds (vector based bounds)
LB <= x <= UB
In pulp however, I don't see how to do vector declarations properly. I was using:
RANGE = range(numpy.size(P))
x = pulp.LpVariable.dicts("x", LB_ind, UB_ind, "Integer")
where I can only enter individual bounds (so only 1 number).
prob = pulp.LpProblem("Test", pulp.LpMaximize)
prob += pulp.lpSum([Prices[i]*Dispatch[i] for i in RANGE])
and for the constraints, do I really have to do this line per line? It seems that I am missing something. I would appreciate some help. The documentation discusses a short example. The number of variables in my case is a few thousand.
You can set the lowBound and upBound on variables after the initialization.
You can create an array of variables with
LB[i] <= x[i] <= UB[i]
with the following code.
x = pulp.LpVariable.dicts("x", RANGE, cat="Integer")
for i in x.viewkeys():
x[i].lowBound = LB_ind[i]
x[i].upBound = UB_ind[i]
The second parameter to LpVariable.dict is the index set of the decision variables, not their lower bounds.
For the first question, you can do it like this in some other problem.
students = range(96)
group = range(24)
var = lp.LpVariable.dicts("if_i_in_group_j", ((i, j) for i in students for j in group),cat='binary')
Related
I have a mathematical model of differential equations that begins as linear and then uses correctional coefficients after reaching a certain value (1).
Currently, I solve the linear function independently, find out where the array goes from less than 1 to greater than 1, and then use that value from the array as the new initial condition. I also correct the time scale.
def vttmodel_linear(m,t,tm,tv,M_max):
n = 1/(7*tm)
dMdt = n
return dMdt
M_0 = 0
M_max = 1 + 7*((RH_crit-RH)/(RH_crit-100)) - 2*np.square((RH_crit-RH)/(RH_crit-100))
print(M_max)
# tm = days
# M = weeks so 7*tm
t = np.arange(0,104+1)
tm = np.exp(-0.68*np.log(T) - 13.9*np.log(RH) + 0.14*W - 0.33*SQ + 66.02)
tv = np.exp(-0.74*np.log(T) - 12.72*np.log(RH) + 0.06*W + 61.50)
m = odient(vttmodel_linear, M_0, t, args=(tm,tv,M_max))
M_0 = m[(np.where(m>1)[0][0])-1]
t = np.where(m>1)[0]
Then I use the new initial condition, M_0 and the updated time scale to solve the non-linear portion of the model.
def vttmodel(M,t,tm,tv,M_max):
n = 1/(7*tm)
k1 = 2/((tv/tm)-1)
k2 = np.max([1-np.exp(2.3*(M-M_max)), 0])
dMdt = n*k1*k2
return dMdt
M = odient(vttmodel, M_0, t, args=(tm,tv,M_max))
I then splice the arrays m and M at the location I found earlier and graph the result.
I would like to find a simplified way to do this. I have tried using If statements within the odient function and also a While loop when calling the two functions, but have not had any luck interrupting the odient function. Suggestions would be helpful. Thank you.
I have this kind of data :
import random
data=random.sample(range(1, 100), 5)
x= [-1,1,1,-1,1]
def f(x,data):
prod=[a * b for a, b in zip(data, x)]
result=abs(sum(prod))
return result
I Would like to find the best x composed of -1 or 1 to minimize the value of f(x)
Maybe we can use scipy.minimise() but how can we add the -1 or 1 as a constrain on the value inside of x ?
Does somebody have an idea ?
You want to solve a mixed-integer linear programming problem (MILP), which aren't supported yet by scipy.optimize.
However, you can use a modelling package like PuLP to formulate your MILP and pass it to a MILP solver. Note that your MIP can be formulated as
(P)
min |f(x)| = |d_0 * x_0 + ... + d_n * x_n|
s.t. x_i ∈ {-1, 1} ∀ i = 0,...,n
which is the same as
(P')
min |f(x)| = |d_0 * (2*x_0 - 1) + ... + d_n * (2*x_n - 1)|
s.t. x_i ∈ {0, 1} ∀ i = 0,...,n
and can be implemented like this
min abs_obj
s.t. f(x) <= abs_obj
f(x) >= -1.0*abs_obj
x_i ∈ {0, 1} ∀ i = 0,...,n
In code:
import pulp
import random
data = random.sample(range(1, 100), 5)
# pulp model
mdl = pulp.LpProblem("our_model", sense=pulp.LpMinimize)
# the binary variables x
x = pulp.LpVariable.dicts("x", range(5), cat="Binary")
# the variable that stores the absolute value of the objective
abs_obj = pulp.LpVariable("abs_obj")
# set the MIP objective
mdl += abs_obj
# Define the objective: |f(x)| = abs_obj
mdl += pulp.lpSum((2 * x[i] - 1) * data[i] for i in range(5)) <= abs_obj
mdl += pulp.lpSum((2 * x[i] - 1) * data[i] for i in range(5)) >= -1.0*abs_obj
# solve the problem
mdl.solve()
# your solution
signs = [1 if var.varValue > 0 else -1 for var in x.values()]
Alternatively, if you don't want to use another package, you can use scipy.optimize.minimize and implement a simple penalty method. Thereby you solve the problem (P') by solving the penalty problem
min |f(x)| + Ɛ * (x_0 * (1 - x_0) + ... + x_n * (1 - x_n))
with 0 <= x_i <= 1
where Ɛ is a given penalty parameter. Here, the idea is that the right penalty term equals zero for an integer solution.
Note that as the case may be that you need to solve a sequence of penalty problems to achieve convergence to an integer solution. Thus, I'd highly recommend sticking to a MILP solver instead of implementing a penalty method on your own.
Yes, you can do it using scipy.optimize.minimize:
from scipy.optimize import minimize
minimize(f, [0] * len(data), args=data, bounds=[(-1, 1)] * len(data))
This call minimizes f which you defined in the original post.
It passes a zero array as an initial guess for the minimization problem.
The argument f requires is 'data' which is specified by the argument 'args'.
The constraints you want are specified by the argument 'bounds' as a list of min/max tuples with the length of the input data.
I'm using cvxpy library to perform Portfolio Optimization.
However, instead of using the Markowitz covariance model, I would like to introduce new variables where yi variable is a binary variable that assumes value 1 if the asset i is included in the portfolio and 0 otherwise; m is the maximum number of assets I want to include in the portfolio; r is the return I want to get.
The Markowitz model, with constraint on the return is the following:
import numpy as np
import pandas as pd
from cvxpy import *
# assets names
tickers = ["AAA", "BBB", "CCC", "DDD", "EEE", "FFF"]
# return matrix
ret = pd.DataFrame(np.random.rand(1,6), columns = tickers)
# Variance_Coviariance matrix
covm = pd.DataFrame(np.random.rand(6,6), columns = tickers, index = tickers)
# problem setting
x = Variable(len(tickers)) # xi variables
er = np.asarray(ret.T) * x # expected return
min_ret = 0.2 # minimum return
risk = quad_form(x, np.asmatrix(covm)) # risk
prob = Problem(Minimize(risk), # problem setting function
[sum(x) == 1, er >= min_ret, x >= 0])
prob.solve()
The solution of this problem gives out a percentage to invest in each asset. But what if I want to invest on a limited number of asset m?
In order to do that I need to implement yi variables and make sure that their sum is equal to m
Hence, it should be something like this:
x = Variable(n)
er = np.asarray(ret.T) * x
risk = quad_form(x, np.asmatrix(covm))
y = Variable(n, boolean=True) #adding boolean variables
prob = Problem(Minimize(risk), [sum(x) == 1, er >= min_ret, x >= 0, sum(y) == k, sum(x) <= sum(y)])
prob.solve()
print(x.value)
print(y.value)
Unfortunately, this last chunk of code doesn't produce any result. Do you know why? Is there another method to solve this problem?
In short, you have to link the variables x and y.
In case of long only constraints:
eps = 1e-5
[-1 + eps <= x - y, x - y <= 0]
This will set y to 1 if x > 0 and y to 0 if x == 0.
To make it work properly and not to be bothered by assets being just marginally above 0, you should also introduce a buy-in threshold.
[x - y >= buy_in_threshold - 1]
Note, that this problem is a mixed integer problem.
The ECOS BB solver can deal with that, if the problem remains small. Otherwise, you will need a commercial grade optimizer.
I want to use numpy matrix with PuLP to set constraints.
I've a 2x4x4 numpy matrix and I want to use this matrix for constraints but the problem I've is how to use this. Actually I'm facing problem in indexing as I've to loop over all variables and fix the contraints.
These are the matrices.
P = np.array([[[0.7, 0.3,0,0],
[0,0.7,0.3,0],
[0,0,0.6,0.4],
[0,0,0,1]],
[[0.7,0.3,0,0],
[0.7,0.3,0,0],
[0.7,0.3,0,0],
[0.7,0.3,0,0]]])
C = np.array([[100,80,50,10],[-100,-100,-100,-100]])
beta = 0.9
P matrix is probability matrix and second one is cost matrix.
Every 4x4 matrix depicts the transition probability from one state to another.
and my constraint is
Here V is variable.
I'm going to assume two things;
That in that last constraint you mean C[d][i] on right-hand side, rather than C[i][d]... because P.shape[0] = d = 2, and C.shape[0] = 2.
That you are wanting the constraints to be for all d, as well as for all i.
Assuming the above, the following should do what you want:
from pulp import *
import numpy as np
P = np.array([[[0.7, 0.3,0,0],
[0,0.7,0.3,0],
[0,0,0.6,0.4],
[0,0,0,1]],
[[0.7,0.3,0,0],
[0.7,0.3,0,0],
[0.7,0.3,0,0],
[0.7,0.3,0,0]]])
C = np.array([[100,80,50,10],[-100,-100,-100,-100]])
beta = 0.9
set_D = range(0, P.shape[0])
set_I = range(0, P.shape[1])
# Generate proble, & Create variables
prob = LpProblem("numpy_constraints", LpMinimize)
V = pulp.LpVariable.dicts("V", set_I, cat='Continuous')
# Make up an objective, let's say sum of V_i
prob += lpSum([V[i] for i in set_I])
# Apply constraints
for d in set_D:
for i in set_I:
prob += V[i] - beta*lpSum([P[d][i][j]*V[j] for j in set_I]) >= C[d][i]
# Solve problem
prob.solve()
# Print results:
V_soln = np.array([V[i].varValue for i in set_I])
print (("Status:"), LpStatus[prob.status])
print("V_soln: ")
print(V_soln)
With which I get the following. I've not checked your constraints are satisfied but they should be.
Status: Optimal
V_soln:
[690.23142 575.50231 492.35502 490.23142]
I have a second order differential equation that I want to solve it in python. The problem is that for one of the variables I don't have the initial condition in 0 but only the value at infinity. Can one tell me what parameters I should provide for scipy.integrate.odeint ? Can it be solved?
Equation:
Theta needs to be found in terms of time. Its first derivative is equal to zero at t=0. theta is not known at t=0 but it goes to zero at sufficiently large time. all the rest is known. As an approximate I can be set to zero, thus removing the second order derivative which should make the problem easier.
This is far from being a full answer, but is posted here on the OP's request.
The method I described in the comment is what is known as a shooting method, that allows converting a boundary value problem into an initial value problem. For convenience, I am going to rename your function theta as y. To solve your equation numerically, you would first turn it into a first order system, using two auxiliary function, z1 = y and z2 = y', and so your current equation
I y'' + g y' + k y = f(y, t)
would be rewitten as the system
z1' = z2
z2' = f(z1, t) - g z2 - k z1
and your boundary conditions are
z1(inf) = 0
z2(0) = 0
So first we set up the function to compute the derivative of your new vectorial function:
def deriv(z, t) :
return np.array([z[1],
f(z[0], t) - g * z[1] - k * z[0]])
If we had a condition z1[0] = a we could solve this numerically between t = 0 and t = 1000, and get the value of y at the last time as something like
def y_at_inf(a) :
return scipy.integrate.odeint(deriv, np.array([a, 0]),
np.linspace(0, 1000, 10000))[0][-1, 0]
So now all we need to know is what value of a makes y = 0 at t = 1000, our poor man's infinity, with
a = scipy.optimize.root(y_at_inf, [1])