I have a bunch of gurobi variables
y[0],y[1],...,y[n]
x[0],x[1],...,x[m].
I would like to be able to figure out the indices of the optimal y's that are not zero. In other words, if the optimal solution is y[0]=0, y[1]=5, y[2]=0, y[3]=1, I would like to return [1,3]. So far, I have
F = []
for v in model.getVars():
if v.varName[0]=='y' and v.x>0:
F.append[v.varName]
This, in the above example, would give me ['y[1]', 'y[3]']. Since this output is a string, I'm not sure how I can get the 1 and 3 out of it. Please help.
Thanks in advance!
I am using the following which works:
Index_List = []
for v in m.getVars():
if v.varName[0] == 'y' and v.x>0:
Index = int(v.varName[2])
for j in range(3,3+100)):
BOOL = False
try:
IndexNEW =int(v.varName[j])
Index = 10*Index+IndexNEW
BOOL = True
except ValueError:
()
if not BOOL:
break
Index_List.append(Index)
The resulting Index_List is as desired. There must be a better way though.
Assuming
from gurobipy import *
m = Model()
If you create a gurobi tupledict for your variables with
x = m.addVars(nx, vtype=GRB.INTEGER, name="x")
y = m.addVars(ny, vtype=GRB.INTEGER, name="y")
# ...your constraints and objective here..
then you can directly call the attributes for your variables (in your case the .X attribute for the variable value in the current solution). Using a list comprehension it could be done with:
m.optimize()
if m.status == GRB.OPTIMAL:
indices = [i for i in range(ny) if y[i].X > 0]
where nx and ny are the number of your variables.
Related
Hi I am trying to index a range of a vector of symbols.
My first idea was to use a MatrixSymbol
A = MatrixSymbol('A',10,1)
B = Matrix(A[0:2]) # does not work!
Then I thought I could use an IndexBased, but how do I generate the submatrix B (I can't index a range)
A=IndexedBased('A')
B=Matrix([A[0],A[1]]) # works but I want to define a range
There must be a proper way to do this.
IndexedBase will give you a 1-d vector like symbol.
You could use a list comprehension with a range giving the indices:
>>> a = IndexedBase("A")
>>> Matrix([a[i] for i in range(2)])
Matrix([
[A[0]],
[A[1]]])
Since you are working in Python, you are free to modify routines to suit your needs if you want to use direct slicing on the IndexedBase:
def gi(self, slc):
if isinstance(slc, slice):
a,b,s=slc.start, slc.stop, slc.step
s = s or 1
a = a or 0
assert all(type(i) is int or i is None for i in (a,b,s))
assert b is not None
rv = []
for i in range(a,b,s):
rv.append(self[i])
return rv
return _gi(self, slc)
_gi = IndexedBase.__getitem__
IndexedBase.__getitem__ = gi
With that modification you ca now do:
>>> x = IndexedBase('x')
>>> x[0]
x[0]
>>> x[:2]
[x[0], x[1]]
(This does not attempt to handle the key words argument; it's more a proof of concept.)
I have this MILP model that I am solving by cplex python API:
def CModel():
mdl=Model('Generate')
#variable declaration
y=mdl.binary_var_dict(ijk,name='y')
Sum=mdl.integer_var_dict(ij,name='S')
#objective
mdl.minimize(0)
#constraints
#1
mdl.add_constraints(mdl.sum(y[(i,j,k)]+y[(j,i,k)] for j in T)==1 for i in T for k in K)
#2
mdl.add_constraints(mdl.sum(y[(i,j,k)]+y[(j,i,k)] for k in K1 )==1 for i in T for j in T if i!=j)
#3
mdl.add_constraints(mdl.sum(y[(i,j,k)]+y[(j,i,k)] for k in K2 )==1 for i in T for j in T if i!=j)
#4
mdl.add_constraints(mdl.sum(y[(i,j,k)] for k in K )==1 for i in T for j in T if i!=j)
#5
for i,j in ij:
for k in K4:
Sum=mdl.sum(y[(j,i,k1)] for k1 in range(k+1,k+8 ))
mdl.add(mdl.if_then(y[(i,j,k)]==1, Sum==0))
return mdl
Sum is a variable that I am using to make the 5th constraint (I'm just using it to restrict y on certain ranges to 0). In the solution pool I just need the indices of y where y==1. I have this solution pool for solving the model:
def soln_pool(mdl):
cpx = mdl.get_cplex()
cpx.parameters.parallel.set(1)
cpx.parameters.mip.pool.intensity.set(4)
cpx.populatelim=50
st2=time.time()
try:
cpx.populate_solution_pool()
except CplexSolverError:
print("Exception raised during populate")
return []
numsol = cpx.solution.pool.get_num() #max timing by 29 second
sol_pool = []
pool=[]
pool2=[]
if numsol!=0:
for i in range(numsol):
indices = [j for j, a in enumerate(cpx.solution.pool.get_values(i)) if a > 0.5]
for element in sol_pool:
for j in element:
v = mdl.get_var_by_index(j)
i1 = int(v.name.split('_')[1])
i2 = int(v.name.split('_')[2])
i3 = int(v.name.split('_')[3])
pool.append([i1,i2,i3])
pool2.append(pool)
Before I add constriant #5 there was no problem with the solution pool function but after adding #5 I get this error:
i1 = int(v.name.split('_')[1])
ValueError: invalid literal for int() with base 10: '{y'
Hiw can I access only variable y values on solution pool and convert then to integers?
You can just check v.name.startswith('y') to filter out anything that is not a plain y variable. My guess is that the offending variable is some auxiliary variable that was created under the hood. You could print the full v.name to see the name of the offending variable's name. If it is an autocreated variable then its name will tell for which constraint it was created.
Also note that you have
Sum=mdl.integer_var_dict(ij,name='S')
and later
Sum=mdl.sum(y[(j,i,k1)] for k1 in range(k+1,k+8 ))
The latter overwrites the former. That is probably not what you intend to do.
Daniel is right. Model.if_then generates a boolean variable. You can check this with this small code:
def is_gen(dv):
return 'yes' if dv.is_generated() else 'no'
for dv in cm.iter_variables():
print(f'-- variable: {dv.name}, index={dv.index}, generated={is_gen(dv)}')
which generates output like:
-- variable: _bool{y_10_2_3 == 1}, index=5887, generated=yes
which represents the truth variable for constraint y_20_2_3==1.
As you can see, calling is_generated() on a variable return True when the variable has been generated by Docplex, so you can just filter out those variables in you split code.
I'm working on a graph-theoretical problem. Suppose we want to find a Graph G=(V,E), such that there exists a partition X of V containing at most k equivalence classes. A variable p_S takes value 1 exactly when S is a member of partition X, and zero otherwise. So we have a constraint that the sum over all variables p_S is at most k, for all subsets S of V.
So what I want to do is to iterate over all p_S that have value 1 and define more constraints based on the elements I draw out of S. These constraints would preserve that members of an equivalence class share some mutual property.
Is it possible to access the p_S variables that way and how could I do it?
ALternatively I know I can do without iterating on my binary variables if I'm allowed to use binary variables as coefficients in my constraints. Is that possible?
Thanks in advance!
The CPLEX Python API is index based. To iterate over all binary variables with a solution value set to 1 we need to query the variables types and the solution values and filter accordingly. Here is a simple example:
import sys
import cplex
def main(modelfile):
# Read in a model file.
c = cplex.Cplex()
c.read(modelfile)
# Solve the model and print the solution and status.
c.solve()
print("Solution value:", c.solution.get_objective_value())
print("Solution status: {0} ({1})".format(
c.solution.get_status_string(),
c.solution.get_status()))
# Display all binary variables that have a solution value of 1.
types = c.variables.get_types()
nvars = c.variables.get_num()
binvars = [idx for idx, typ
in zip(range(nvars), c.variables.get_types())
if typ == c.variables.type.binary]
inttol = c.parameters.mip.tolerances.integrality.get()
binvars_at_one = [idx for idx, val
in zip(binvars, c.solution.get_values(binvars))
if abs(val - 1.0) <= inttol]
print("Binary variables with a solution value equal to one:")
for varname in c.variables.get_names(binvars_at_one):
print(" ", varname)
if __name__ == "__main__":
if len(sys.argv) != 2:
raise ValueError("usage: {0} <model>".format(sys.argv[0]))
main(sys.argv[1])
For more, see the documentation for Cplex.variables and Cplex.solution.
linearization example:
from docplex.mp.model import Model
mdl = Model(name='binaryproduct')
x = mdl.binary_var(name='x')
y = mdl.binary_var(name='y')
z = mdl.binary_var(name='z')
# z==x*y
mdl.add_constraint(x+y<=1+z, 'ct1')
mdl.add_constraint(z<=x, 'ct2')
mdl.add_constraint(z<=y, 'ct3')
#end of z==x*y
mdl.solve()
for v in mdl.iter_binary_vars():
print(v," = ",v.solution_value)
so I've got a list of questions as a dictionary, e.g
{"Question1": 3, "Question2": 5 ... }
That means the "Question1" has 3 points, the second one has 5, etc.
I'm trying to create all subset of question that have between a certain number of questions and points.
I've tried something like
questions = {"Q1":1, "Q2":2, "Q3": 1, "Q4" : 3, "Q5" : 1, "Q6" : 2}
u = 3 #
v = 5 # between u and v questions
x = 5 #
y = 10 #between x and y points
solution = []
n = 0
def main(n_):
global n
n = n_
global solution
solution = []
finalSolution = []
for x in questions.keys():
solution.append("_")
finalSolution.extend(Backtracking(0))
return finalSolution
def Backtracking(k):
finalSolution = []
for c in questions.keys():
solution[k] = c
print ("candidate: ", solution)
if not reject(k):
print ("not rejected: ", solution)
if accept(k):
finalSolution.append(list(solution))
else:
finalSolution.extend(Backtracking(k+1))
return finalSolution
def reject(k):
if solution[k] in solution: #if the question already exists
return True
if k > v: #too many questions
return True
points = 0
for x in solution:
if x in questions.keys():
points = points + questions[x]
if points > y: #too many points
return True
return False
def accept(k):
points = 0
for x in solution:
if x in questions.keys():
points = points + questions[x]
if points in range (x, y+1) and k in range (u, v+1):
return True
return False
print(main(len(questions.keys())))
but it's not trying all possibilities, only putting all the questions on the first index..
I have no idea what I'm doing wrong.
There are three problems with your code.
The first issue is that the first check in your reject function is always True. You can fix that in a variety of ways (you commented that you're now using solution.count(solution[k]) != 1).
The second issue is that your accept function uses the variable name x for what it intends to be two different things (a question from solution in the for loop and the global x that is the minimum number of points). That doesn't work, and you'll get a TypeError when trying to pass it to range. A simple fix is to rename the loop variable (I suggest q since it's a key into questions). Checking if a value is in a range is also a bit awkward. It's usually much nicer to use chained comparisons: if x <= points <= y and u <= k <= v
The third issue is that you're not backtracking at all. The backtracking step needs to reset the global solution list to the same state it had before Backtracking was called. You can do this at the end of the function, just before you return, using solution[k] = "_" (you commented that you've added this line, but I think you put it in the wrong place).
Anyway, here's a fixed version of your functions:
def Backtracking(k):
finalSolution = []
for c in questions.keys():
solution[k] = c
print ("candidate: ", solution)
if not reject(k):
print ("not rejected: ", solution)
if accept(k):
finalSolution.append(list(solution))
else:
finalSolution.extend(Backtracking(k+1))
solution[k] = "_" # backtracking step here!
return finalSolution
def reject(k):
if solution.count(solution[k]) != 1: # fix this condition
return True
if k > v:
return True
points = 0
for q in solution:
if q in questions:
points = points + questions[q]
if points > y: #too many points
return True
return False
def accept(k):
points = 0
for q in solution: # change this loop variable (also done above, for symmetry)
if q in questions:
points = points + questions[q]
if x <= points <= y and u <= k <= v: # chained comparisons are much nicer than range
return True
return False
There are still things that could probably be improved in there. I think having solution be a fixed-size global list with dummy values is especially unpythonic (a dynamically growing list that you pass as an argument would be much more natural). I'd also suggest using sum to add up the points rather than using an explicit loop of your own.
I'm having some troubles trying to use four lists with the zip function.
In particular, I'm getting the following error at line 36:
TypeError: zip argument #3 must support iteration
I've already read that it happens with not iterable objects, but I'm using it on two lists! And if I try use the zip only on the first 2 lists it works perfectly: I have problems only with the last two.
Someone has ideas on how to solve that? Many thanks!
import numpy
#setting initial values
R = 330
C = 0.1
f_T = 1/(2*numpy.pi*R*C)
w_T = 2*numpy.pi*f_T
n = 10
T = 1
w = (2*numpy.pi)/T
t = numpy.linspace(-2, 2, 100)
#making the lists c_k, w_k, a_k, phi_k
c_karray = []
w_karray = []
A_karray = []
phi_karray = []
#populating the lists
for k in range(1, n, 2):
c_k = 2/(k*numpy.pi)
w_k = k*w
A_k = 1/(numpy.sqrt(1+(w_k)**2))
phi_k = numpy.arctan(-w_k)
c_karray.append(c_k)
w_karray.append(w_k)
A_karray.append(A_k)
phi_karray.append(phi_k)
#making the function w(t)
w = []
#doing the sum for each t and populate w(t)
for i in t:
w_i = ([(A_k*c_k*numpy.sin(w_k*i+phi_k)) for c_k, w_k, A_k, phi_k in zip(c_karray, w_karray, A_k, phi_k)])
w.append(sum(w_i)
Probably you mistyped the last 2 elements in zip. They should be A_karray and phi_karray, because phi_k and A_k are single values.
My result for w is:
[-0.11741034896740517,
-0.099189027720991918,
-0.073206290274556718,
...
-0.089754003567358978,
-0.10828235682188027,
-0.1174103489674052]
HTH,
Germán.
I believe you want zip(c_karray, w_karray, A_karray, phi_karray). Additionally, you should produce this once, not each iteration of the for the loop.
Furthermore, you are not really making use of numpy. Try this instead of your loops.
d = numpy.arange(1, n, 2)
c_karray = 2/(d*numpy.pi)
w_karray = d*w
A_karray = 1/(numpy.sqrt(1+(w_karray)**2))
phi_karray = numpy.arctan(-w_karray)
w = (A_karray*c_karray*numpy.sin(w_karray*t[:,None]+phi_karray)).sum(axis=-1)