continues variable that cannot take some values in a range
AnsweredHello everyone,
I have a non-negative continues variable, that for some indices it cannot take a value between two numbers.
Let me show you with my code.
let's say the variable is defined over a list of 10 indices:
varlist = list(range(10))
Now let's define our variable
x = m.addVars(varlist, vtype=GRB.CONTINUOUS)
Now, I have 3 lists of what variables that needs to be either greater than a specific value or less than another value.
restvar = [4, 7]
upval = [8, 11]
lowval = [2, 4]
What these tell me is,
variable x4 can either be greater than 8 or less than 2 but cannot be anything in between.
Similarly, variable x7 can either be greater than 11 or less than 4 but nothing in between.
I'm having two problems here- sorry for the bother ;)
First and the most urgent problem is formulating this set of constraint. I have looked at semi-continuous variables and I couldn't really figure out how to use them, and also in my actual model I have tens of thousands of variables x, but only a handful of them need to take this either-or value, but the rest - fast majority, are just regular non negative continuous variables so I'm not sure if it's the change the type of only a few or not. I have also looked at other type of constraints such as model.addGenConstrOr
() and model.addGenConstrIndicator()
but to be completely honest, I couldn't find enough resources to understand how they are used and what I found in the documentation is way over my head although it seems like this is the way to do it.
The second problem is more of a data structure issue. The three lists above showing me what variables to take these either-or values and what these values are, I get these list as request from some database. Of course I can do any pre-processing I want and store them in the structure I want. But since I cannot figure out how to write the constraints, I'm not sure what data structure is best to use to feed the data into the constraint. But when I tried to write the constraint in many different WRONG ways, I found myself using loops a lot. Here is an example of obviously infeasible constraint but I just I want to show you how I'm using the loop which seems inefficient.
for i in range(len(restvar)):
m.addConstrs(x[restvar[i]] <= lowval[i])
m.addConstrs(x[restvar[i]] >= upval[i])
I want to stress that I know this is not how the constraint is modeled but I just want to show you that way I was thinking of using the loops since I have my indices and values in these three separate lists and I did not process them in any way, but maybe you can suggest a better way to structure and store these lists before constructing the constraints.
This community has been nothing but very helpful for me and I really appreciate everything you do here. My apologies if my question is silly, but I spent mode of weekend looking at these semi-continuous valuables and other type of constraints with no luck.
Thank you!
-
Hi Ashraf,
you can perhaps solve this challenge the following way:
1. We need to add two binary indicator variables per case. One of them will denote the "less than or equal" case, while the other will denote the "more than or equal case. We will call them z.
2. Then, we need to ensure that exactly one of these variables is set to one (since your variable needs to fall in one of the two ranges). We will add a linear constraint for this end.
3. Finally we need two indicator constraints to tell Gurobi to set the respective x appropriately.
In terms of code, it could look something like this:
z = gp.tupledict()
for i, var_i in enumerate(restvar):
z[i, 1] = m.addVar(vtype="B",name=f"indicator_{i}_1")
z[i, 2] = m.addVar(vtype="B",name=f"indicator_{i}_2")
m.addConstr(z[i, 1] + z[i, 2] == 1)
m.addGenConstrIndicator(z[i, 1], True, x[var_i], gp.GRB.GREATER_EQUAL, upval[i])
m.addGenConstrIndicator(z[i, 2], True, x[var_i], gp.GRB.LESS_EQUAL, lowval[i])Hope this helps. For future readers, I would also like to point to a great post on OR Stack Exchange about linearizing membership in a linear set.
Best regards
Jonasz0 -
Thank you Jonasz! That was very helpful.
On another note, is there a way to report the time the solver took to find a feasible solution other than going back and looking at the log? Like a parameter similar to RunTime for instance.
0 -
If you are interested in the time taken to find the optimal solution, you can use Python's built-in time module (if you are happy with clock time) - you store the time just before and after the model.optimize() call and then subtract the two and store the difference in a variable.
For example,
timestamp = time.time()
model.optimize()
duration = time.time() - timestampIf the time to find any feasible solution is needed - you could perhaps use callbacks to store the timestamp when each feasible solution was found, and then retrieve it after the optimization. You can find all the relevant information under the link. Perhaps Gurobi staff can point to a more straightforward solution to this question?
Best regards
Jonasz0 -
Hi Ashraf, hi Jonasz,
Unfortunately, there is no direct or built-in way to query when a solution has been found. You might want to look into Gurobi/grblogtools: Extract and visualize information from Gurobi log files (github.com) to parse your log files and this will also provide you with this piece of information.
Cheers,
Matthias0
Please sign in to leave a comment.
Comments
4 comments