8
$\begingroup$

I am creating an optimization model with 2 sets of binary decision variables. The first, site, is regarding which of 380 cities to place manufacturing sites in, and the second, ASSG is regarding which of 12 cities each manufacturing site will be assigned to service. There will be 3 manufacturing sites total.

I need to add a constraint that the sum of all cities serviced by a potential manufacturing site is the number of cities that need serviced, 12, but this limit needs to be 0 if a manufacturing site is not placed in that location.

This is difficult for me to explain so I've included a screenshot of the Excel model I am trying to scale-up using Python. The "Logical constraint" is what I am trying to code into Python:

enter image description here

I know this code is a bit of a mess, I'm not great with dictionaries and there's too much going on here for me to keep track of it, but this is what I have so far:

site = m.addVars(siteLoc, vtype=GRB.BINARY, name='site')  # siteLoc is a list of 380 potential mfg site locations
ASSG = m.addVars(siteASSG, vtype=GRB.BINARY, name='ASSG') # siteASSG is a gurobi tuplelist of the format: (potential mfg site location, one of 12 cities mfg site will service)
m.update()

m.addConstr(sum(val for key, val in ASSG.items() if key[0] == k for k in site.keys()) <= 12 * val for key, val in site.items())
$\endgroup$
  • 2
    $\begingroup$ I noticed that in the code you shared, you have if key[0] ==k but you use k in the second for loop. If this is your problem, you may want to change that order in your sum i.e. sum(val for key, val in ASSG.items() for k in site.keys() if key[0] == k) <= ... $\endgroup$ – EhsanK Oct 16 '19 at 5:25
  • $\begingroup$ @EhsanK Thank you. That does make more sense, what would you suggest I do with the right hand side? That side should be either 0 or 12 depending on that item's value in site. $\endgroup$ – Jacob Myer Oct 16 '19 at 5:40
  • $\begingroup$ @EhsanK I have tried <= 12 * k for k in site.keys() on the right-hand side but that gives me TypeError: unsupported operand type(s) for -: 'generator' and 'NoneType' $\endgroup$ – Jacob Myer Oct 16 '19 at 6:08
  • 1
    $\begingroup$ @JacobMyer are you using Gurobi interface for modeling your problem? I am not familiar with that but I think you need to mention the type of variables (integer, real, binary...) when you define the variables. $\endgroup$ – Oguz Toragay Oct 16 '19 at 14:13
  • $\begingroup$ @OguzToragay I usually do but it was left out of an example that I was following. I've added vtype=GRB.BINARY to be sure. $\endgroup$ – Jacob Myer Oct 16 '19 at 14:22
4
$\begingroup$

If I understood the problem correctly, you can have $380$ binary variables $x_{i}$ that can be defined as follows:

$$x_{i}=\begin{cases} 1, & \text{If location}\,\,i \,\,\text{has been selected for a manufacturing site}\\ 0, & \text{Otherwise} \\ \end{cases}$$

and for the chosen sites:

$$y_{ij}=\begin{cases} 1, & \text{If site}\, \, i \, \, \text{serves city} \, j\\ 0, & \text{Otherwise} \\ \end{cases}$$

then you need to add the following constraints to your model:

\begin{cases}\sum\limits_i x_{i}=3 \ \ \forall i \in \{1,2,\dots,380\}\\\sum\limits_j y_{ij} \le 12 \ \ \forall i \in \{\text{chosen sites}\}\\\sum\limits_i y_{ij}=1 \ \ \forall i \in \{\text{chosen sites}\}\,\,\text{and} \ \ \forall j \in \{1,2,\cdots,12\}\end{cases}

first and second constraints can be combined to force the model to assign the $12$ cities to those $3$ cities (out of $380$ potentials) that have a manufacturing site placed in. The constraint would be as follow:

$$\sum_j y_{ij} \le 12 \times x_i \ \ \forall i$$

Now, to define the aforementioned constraint in Python (the code is based on Pyomo but I believe it is almost the same if you use solver interfaces as well):

import numpy as np
from pyomo.environ import *
model.sites = set(np.arange(1,381)) #Index for the potential locations
model.cities  = set(np.arange(1,13)) #Index for the 12 cities with demands
cons = pyomo.constraint((sum y[i,j] for j in model.cities) <= 12*x[i] for i in model.sites) 
$\endgroup$
  • $\begingroup$ I probably should have been more clear about what ASSG and site were. I've edited my post to clarify that. These are my decision variables, would you suggest I modify the decision variables I am using perhaps? $\endgroup$ – Jacob Myer Oct 16 '19 at 14:08
  • $\begingroup$ Even after modifying my decision variables so that ASSG is just the names of the 12 cities with demands, I am receiving the same unsupported operand type(s)... error. What could be causing this? @Oguz $\endgroup$ – Jacob Myer Oct 16 '19 at 14:27
  • 1
    $\begingroup$ @JacobMyer have a look at this similar question: stackoverflow.com/a/46476277 $\endgroup$ – Oguz Toragay Oct 16 '19 at 17:16
  • 1
    $\begingroup$ Thanks for your help! I've gotten all of the error messages sorted out. I'm not used to reading mathematical notation like you have written here so I am trying to make sense of the constraints you have listed and I think that I still need another? My code so far looks like: line 1: m.addConstr(sum(Loc.values()) <= 3) # max number of manufacturing sites line 2:m.addConstrs((quicksum(ASSG[i,j[1]] for j in siteASSG) <= 12 * site[i] for i in siteLoc), 'site-city-relation') line 3: m.update() $\endgroup$ – Jacob Myer Oct 16 '19 at 17:29
  • $\begingroup$ Do my constraints look correct to you? I still need one to assure that all of the 12 cities with demand are associated with a mfg site and I'm not sure how to do this. $\endgroup$ – Jacob Myer Oct 16 '19 at 18:28
3
$\begingroup$

My answer was longer than I could put it in a comment:

I don't know how you defined your variables site and ASSG, so here is what I think should be your constraint (I based it on @Oguz formulation):

from gurobipy import * # I assume this is how you imported

SITES = range(380)  # index i
CITIES = range(12)  # index j
m.addConstrs((quicksum(ASSG[i,j] for j in CITIES) <= 12 * site[i] for i in SITES), 'site-city-relation')

Note that I'm using addConstrs (plural)

$\endgroup$
  • $\begingroup$ One difference between my code and your solution here that I think is important is that site and ASSG are my model's decision variables. I will update my post to show how they were created. I think this might have something to do with why I am receiving: TypeError: unsupported operand type(s) for -: 'generator' and 'NoneType' @EhsanK $\endgroup$ – Jacob Myer Oct 16 '19 at 13:53
  • 1
    $\begingroup$ @JacobMyer Based on your update, both site and ASSG are tupledict which means tuplelist as keys and gurobi Var as values. But you can still access them as I put above. You just need to change SITES to siteLoc and CITIES to siteASSG in the constraint (again note that it's addConstrs (plural)). Your error is likely to go away $\endgroup$ – EhsanK Oct 16 '19 at 15:12
  • $\begingroup$ Okay I still have to create the objective function and make sure that I have all of the necessary constraints (see my comment under Oguz's response), but this has gotten rid of the error messages :D @EhsanK $\endgroup$ – Jacob Myer Oct 16 '19 at 17:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.