docplex: Find the binary decision variables with value 1 - python

I have an MIP model, which includes some sets of binary variables, e.g., y_{rnt}. Instead of having a loop over each index (like below), I would like to only print (an store) variables such that y_{rnt}=1. Does the docplex offer any function in python?
The example for identifying with multiple loops (inefficient):
# R, N, and T are inputs. We also have the solution as the output of the MIP model
x = 0
for r in range(1, R+1):
for n in range(1, N+1):
for t in range(1, T+1):
if solution.get_value(y[r, n, t]) == 1:
x = x + 1

Assuming your y variables are defined as a variable dict, Docplex provides the get_value_dict method on a solution object. This method converts the variable dictionary into a value dictionary, with identical keys.
In addition, if you pass keep_zeros=False then zero values are discarded.
Note that zero values are tested with a precision (default is 1e-6) as all values in Cplex are floating-point, including binary variable values. This means your above code might well "miss" binary variables the value of which would be output as 0.99999 by Cplex.
To summarize:
xd = sol.get_value_dict(vd, keep_zeros=False)
where vd is a variable dictionary, returns a new dictionary with identical keys, where values are non-zero values from sol.
If you're only interested in the variables, use the keys of this dict.

Related

Recommendations to minimize unknown function with categorical variables in Python

Goal
The goal is to minimize an output variable of a computer code whose function is unknown, we will call it F. There are 25 input variables, xij, and 5 output variables, yk.
xij, where i ∈ [1,5] and j ∈ [1,5]
yk, where k ∈ [1,5]
F(x) = y
Inputs
Each input variable, xij, represents a position in a matrix-like pattern and holds a categorical value, el. There are 50 different categorical values to choose from.
el, where l ∈ [1,50]
For example, one possible assignation of values to input variables would be:
x1,1=e1
x1,2=e2
⋯
x5,5=e25
The last 25 values [e26, e50] will be unassigned, no problem.
Moreover, there are restrictions on all input variables. Each input variable, xij, has a restriction on the value it can hold, el. For example:
xij ∈ {em, …, eq}
Imagine that the above assignation (xij = e(i-1)5+j) fulfills these restrictions. That is not a problem.
Outputs
Once all input variables are assigned and feed into the code, the code spits out 5 output variables, yk.
yk, where k ∈ [1,5]
I want to minimize y1, but there are restrictions on all output variables.
yk < yk,lim, where k ∈ [1,5]
Objective function
Hence, I can obtain an objective function as follows:
Where:
ωk = 1 if yk > yk,lim ; 0 otherwise
The last term in the right hand side of the O.F. is a penalization when yk restriction is not fulfilled. Each ωk has value 1 if the corresponding restriction is not fulfilled, 0 otherwise. The normalization of each yk is done so all penalizations and the main output, y1, are on the same range, that is around 1.0.
Question
Do you think this can be done? That is, can I minimize the O.F.? What method or algorithm do you recommend?
Please, take into account the following.
All input variables are categorical
As far as I understood, MINPL is recommended for categorical optimization
Use of any Python library (scipy, mystic, openopt, pyomo…) is preferred
Any other suggestions or recommendations that I did not came up with are also welcome.
Thank you.

How to extract a single column from a set with multiple indices in Pyomo?

I am using a sparse index (called a Set in Pyomo) model.x_index to subset the indices of my Pyomo variable model.x before I create it. This is to avoid creating 10+ million instances of my model.x variable (that is the total number of original indices).
model.x_index = Set(initialize=[(a, i, j, y) for a in model.Biomass for i in model.SourceCounty for j in model.ProdCounty
for y in model.Year if model.TD[i, j] < 1000 if model.BP[a, i, y] > 0 if model.FP[a, i, y] < 1000])
model.x = Var(model.x_index, domain=PositiveReals)
I now want to extract the fourth column y from my model.x_index Pyomo Set and store it as a separate Pyomo Set model.y_index for indexing another variable I am creating. How do I go about doing that?
I'm more than sure that there will be a more efficient way to do this, but while you get that answer, this may work: Just extract the created index_set from model.x and extract just turn them into an actual python set
y_index = []
for (a,i,j,y) in model.x.index_set():
y_index.append(y)
model.y_index = pyo.Set(initialize=list(set(y_index)))
model.y = pyo.Var(model.y_index, domain=pyo.NonNegativeReals)
Since you got model-x_index you can just iterate through it to obtain the same using for (a,i,j,y) in model.x_index:
This will give you the basic idea, but you can construct from there.
Also you don't need to mutate y_index to set and then re-mutate again into a list. I did that because set(.) eliminate any repeated value, but if you just pass the original list as argument to pyo.Set(initialize=y_index) this will raise a WARNING but would work anyway

Pyomo: using a list for parametrising a constraint

I am trying to declare a constraint in Pyomo where one parameter would be a list instead of a scalar and then build the constraint by providing a set of the right dimension but it seems that Pyomo does the Cartesian product of every set and deduces the number of inputs (which are then considered as scalars).
Below a dummy example of what I want to achieve:
model.inputs = RangeSet(0,1)
model.x = model.inputs*model.inputs
model.p = Var()
def constraint_rule(model,x,i):
return x[i] > model.p
model.constraint = Constraint(model.x,model.inputs,rule=constraint_rule)
To be more precise on what I want to achieve. My constraint is of the form:
f(x_0,x_1,i) > p
And I'd like to input x as a vector instead of inputting separately x_0 and x_1 (or more if I have more x_i). So I want to input a list of lists for the first parameter and a iterator as the second parameter which could specify which element of the list I want.
I can of course decompose the list x of lenght n with n scalars x[i] but because I want to keep changing the size of the inputs, I wanted to only change model.x and hope it would automatically scale.
Below the full mathematical problem (I don't have enough reputation to put an image, sorry about that):
Tr(MixiNx) > p
Tr(Nx) = 1
Mi0 + Mi1 = Id
Mi0, Mi1 and Nx SDP
Here the Ms and Ns are 2x2 matrices.
We have 4 N for bitstrings of lenght 2 and 2 matrices M per value of i (then xi is either 0 or 1).
Also x specifies the bitstring (here we have only x_0 and x_1) and i specifies which bit (for instance for i=0, we want the value x_0 ie the first bit of x). But i could be larger than the number of the number of bits in x (for instance we could set that for i=2, we want the value x_0 xor x_1 ie the parity of the bits). So I wanted to encode the 1sr constraint such it receives a bitstring x and a value i which could specify which information I want about that bitstring.
I hope that's clearer.

Pyomo: is possible to assign to a design variable only defined values?

I want that a design variable assumes only specified values during optimization process.
For example:
Let x be the variable which can assume only specific value, e.g.:
x = [0.1,0.5,1.0,1.7,2.3]
How can be written in python using pyomo (if it's possible)?
I hope I was clear.
You have to do this with integer variables. For example, if there are N possible values of x, then let x[n] = 1 if x equals the nth possible value, and 0 otherwise. Any time you have an x in your original model, replace it with
sum {n=1,...,N} v[n] * x[n]
where v[n] is the nth possible value. Finally, add a constraint that says:
sum {n=1,...,N} x[n] == 1
I'm not writing these in Pyomo syntax, but this is a general modeling approach that is the same no matter what modeling language/package you use.

How to make this nested summation efficient in python?

I have a problem making an efficient function to be used in a rootfinding algorithm. I need to make a function that is a triple summation over lables, which contains a triple summation over a pair of lists. I have already tried several implementations such as using nested lists, dictionaries, splitting the inner triple summation in two double summations (with an intermediate lists), keeping the 'm' & 'n' dictionaries/lists apart, using itertools.izip() to go over R and E together and probably a few others I'm forgetting.
The idea is that I need to be able to discriminate the labels for other functions, so I need an efficient way to store, access and sum over these sets of numbers.
Now, this function is part of an iteration, so the first time, most of these lists are empty. Then I need to use a rootfinding algorithm(pretty simple) for each value in the E-dictionary. After using a root finding algorithm (which depends on this function), the lists are refilled with its solutions. This means that in the second iteration each list will contain about the order of 1000 numbers. After that the rootfinding is again used with this new function, giving (after rebinning) 1000 new numbers in each list.
Clearly I have a problem if this rootfinding already takes several minutes in the first iteration. I have a specific implementation for the first iteration (which is reduced to 3 loops over the labels because of all the empty/only one value in list stuff) which finds all these roots in two seconds.
How can I do this summation efficiently, while still being able to discriminate between the various lists?
Thanks in advance
Note: this code is not my most beautifull attempt, but it is the most clear in what I'm trying to accomplish.
E = {}
R = {}
for i in labels:
E[i] = {'m':[i], 'n':[]} #the label happens to be the value that's in here
E[-i] = {'m':[], 'n':[-i]}
R[i] = {'m':[1], 'n':[]}
R[-i] = {'m':[], 'n':[1]}
def function(A, En):
temp = 0
for a in E:
if (not(ACTIVATE) or a != A):
for b in E:
for c in E:
if ( not(ACTIVATE) or b != c):
for i in xrange(len(R[a]['n'])):
for j in xrange(len(R[b]['m'])):
for k in xrange(len(R[c]['m'])):
temp += R[a]['n'][i]*R[b]['m'][j]*R[c]['m'][k]/(En - (-E[a]['n'][i] + E[b]['m'][j] + E[c]['m'][k]))
for i in xrange(len(R[a]['m'])):
for j in xrange(len(R[b]['n'])):
for k in xrange(len(R[c]['n'])):
temp += R[a]['m'][i]*R[b]['n'][j]*R[c]['n'][k]/(En + (E[a]['m'][i] - E[b]['n'][j] - E[c]['n'][k]))
return .5*temp

Categories

Resources