Below is an (over)simplified Pyomo model:
model.WEEKS = Set(initialize = [1,2,3], ordered = True)
model.PRODS = Set(initialize = ['Q24','J24','F24'], ordered = True)
model.volume = Var(model.WEEKS,model.PRODS, within = NonNegativeIntegers)
Which gives:
---
3 Set Declarations
PRODS : Size=1, Index=None, Ordered=Insertion
Key : Dimen : Domain : Size : Members
None : 1 : Any : 3 : {'Q24', 'J24', 'F24'}
WEEKS : Size=1, Index=None, Ordered=Insertion
Key : Dimen : Domain : Size : Members
None : 1 : Any : 3 : {1, 2, 3}
volume_index : Size=1, Index=None, Ordered=True
Key : Dimen : Domain : Size : Members
None : 2 : WEEKS*PRODS : 9 : {(1, 'Q24'), (1, 'J24'), (1, 'F24'), (2, 'Q24'), (2, 'J24'), (2, 'F24'), (3, 'Q24'), (3, 'J24'), (3, 'F24')}
1 Var Declarations
volume : Size=9, Index=volume_index
Key : Lower : Value : Upper : Fixed : Stale : Domain
(1, 'F24') : 0 : None : None : False : True : NonNegativeIntegers
(1, 'J24') : 0 : None : None : False : True : NonNegativeIntegers
(1, 'Q24') : 0 : None : None : False : True : NonNegativeIntegers
(2, 'F24') : 0 : None : None : False : True : NonNegativeIntegers
(2, 'J24') : 0 : None : None : False : True : NonNegativeIntegers
(2, 'Q24') : 0 : None : None : False : True : NonNegativeIntegers
(3, 'F24') : 0 : None : None : False : True : NonNegativeIntegers
(3, 'J24') : 0 : None : None : False : True : NonNegativeIntegers
(3, 'Q24') : 0 : None : None : False : True : NonNegativeIntegers
---
I'm attempting to write a constraint that limits the sum volume per PROD
over all WEEKS
to i.e. 300. This is simple enough using:
def volMax_rule(model,j):
return sum(model.volume[i,j] for i in model.WEEKS) <= 300
model.volMax_calc = Constraint(model.PRODS, rule = volMax_rule)
Which creates:
---
1 Constraint Declarations
volMax_calc : Size=3, Index=PRODS, Active=True
Key : Lower : Body : Upper : Active
F24 : -Inf : volume[1,F24] + volume[2,F24] + volume[3,F24] : 300.0 : True
J24 : -Inf : volume[1,J24] + volume[2,J24] + volume[3,J24] : 300.0 : True
Q24 : -Inf : volume[1,Q24] + volume[2,Q24] + volume[3,Q24] : 300.0 : True
---
However, the challenge I am facing is that I don't want Q24
to be summed up separately but rather summed within both J24
and F24
. As in:
Key : Lower : Body : Upper : Active
F : -Inf : volume[1,F24] + volume[2,F24] + volume[3,F24] + volume[1,Q24] + volume[2,Q24] + volume[3,Q24]: 300.0 : True
J : -Inf : volume[1,J24] + volume[2,J24] + volume[3,J24] + volume[1,Q24] + volume[2,Q24] + volume[3,Q24]: 300.0 : True
---
I'm unsure how to achieve this. My guess is to perhaps create an additional parameter containing `['JAN', 'FEB'] for each week and then creating a conditional constraint to assign values? However, I'm pretty inexperienced and am probably off the mark. Some guidance would be appreciated please.
Many thanks!
You can do this a handful of different ways. 2 are shown below. As long as what you pass into the rule are legal indices for the variables used, it will construct. You can make any number of arbitrary subsets as they are frequently needed. Putting them "in the model" as model elements has some advantages in that they show when printing the model and T/S (good!) and you can add the within
command to verify membership, etc. But in many cases, it isn't necessary.
The first method uses set subtraction, If you had things in lists, you could pop them out, use a list/set comprehension, etc. etc.
import pyomo.environ as pyo
model = pyo.ConcreteModel('example')
model.WEEKS = pyo.Set(initialize = [1,2,3], ordered = True)
model.PRODS = pyo.Set(initialize = ['Q24','J24','F24'], ordered = True)
model.volume = pyo.Var(model.WEEKS,model.PRODS, within = pyo.NonNegativeIntegers)
# just make a subset... or any legal collection of index values
# method 1:
model.MY_SPECIAL_PRODS = pyo.Set(within=model.PRODS, initialize=model.PRODS - {'Q24',})
def v_max_basic(model,j):
return sum(model.volume[i,j] for i in model.WEEKS) <= 300
model.C1a = pyo.Constraint(model.MY_SPECIAL_PRODS, rule = v_max_basic
)
# method 2: you can just make one "on the fly" without putting it in the model
model.C1b = pyo.Constraint(['J24', 'F24'], rule=v_max_basic)
# making the full constraint.
# method 1: just write it and hard-code the unique index
def v_max_rule_2(model, j):
return sum(model.volume[i, j] for i in model.WEEKS) + sum(model.volume[i, 'Q24'] for i in model.WEEKS) <= 300
model.C2a = pyo.Constraint(model.MY_SPECIAL_PRODS, rule=v_max_rule_2)
# method 2: or we can just make an arbitrary exrpession in terms of variables & constants and stuff it in the model for convenience...
model.my_handy_expression = sum(model.volume[i, 'Q24'] for i in model.WEEKS)
def v_max_rule_3(model, j):
return sum(model.volume[i, j] for i in model.WEEKS) + model.my_handy_expression <= 300
model.C2b = pyo.Constraint(model.MY_SPECIAL_PRODS, rule=v_max_rule_3)
model.pprint()
5 Set Declarations
C1b_index : Size=1, Index=None, Ordered=Insertion
Key : Dimen : Domain : Size : Members
None : 1 : Any : 2 : {'J24', 'F24'}
MY_SPECIAL_PRODS : Size=1, Index=None, Ordered=Insertion
Key : Dimen : Domain : Size : Members
None : 1 : PRODS : 2 : {'J24', 'F24'}
PRODS : Size=1, Index=None, Ordered=Insertion
Key : Dimen : Domain : Size : Members
None : 1 : Any : 3 : {'Q24', 'J24', 'F24'}
WEEKS : Size=1, Index=None, Ordered=Insertion
Key : Dimen : Domain : Size : Members
None : 1 : Any : 3 : {1, 2, 3}
volume_index : Size=1, Index=None, Ordered=True
Key : Dimen : Domain : Size : Members
None : 2 : WEEKS*PRODS : 9 : {(1, 'Q24'), (1, 'J24'), (1, 'F24'), (2, 'Q24'), (2, 'J24'), (2, 'F24'), (3, 'Q24'), (3, 'J24'), (3, 'F24')}
1 Var Declarations
volume : Size=9, Index=volume_index
Key : Lower : Value : Upper : Fixed : Stale : Domain
(1, 'F24') : 0 : None : None : False : True : NonNegativeIntegers
(1, 'J24') : 0 : None : None : False : True : NonNegativeIntegers
(1, 'Q24') : 0 : None : None : False : True : NonNegativeIntegers
(2, 'F24') : 0 : None : None : False : True : NonNegativeIntegers
(2, 'J24') : 0 : None : None : False : True : NonNegativeIntegers
(2, 'Q24') : 0 : None : None : False : True : NonNegativeIntegers
(3, 'F24') : 0 : None : None : False : True : NonNegativeIntegers
(3, 'J24') : 0 : None : None : False : True : NonNegativeIntegers
(3, 'Q24') : 0 : None : None : False : True : NonNegativeIntegers
4 Constraint Declarations
C1a : Size=2, Index=MY_SPECIAL_PRODS, Active=True
Key : Lower : Body : Upper : Active
F24 : -Inf : volume[1,F24] + volume[2,F24] + volume[3,F24] : 300.0 : True
J24 : -Inf : volume[1,J24] + volume[2,J24] + volume[3,J24] : 300.0 : True
C1b : Size=2, Index=C1b_index, Active=True
Key : Lower : Body : Upper : Active
F24 : -Inf : volume[1,F24] + volume[2,F24] + volume[3,F24] : 300.0 : True
J24 : -Inf : volume[1,J24] + volume[2,J24] + volume[3,J24] : 300.0 : True
C2a : Size=2, Index=MY_SPECIAL_PRODS, Active=True
Key : Lower : Body : Upper : Active
F24 : -Inf : volume[1,F24] + volume[2,F24] + volume[3,F24] + volume[1,Q24] + volume[2,Q24] + volume[3,Q24] : 300.0 : True
J24 : -Inf : volume[1,J24] + volume[2,J24] + volume[3,J24] + volume[1,Q24] + volume[2,Q24] + volume[3,Q24] : 300.0 : True
C2b : Size=2, Index=MY_SPECIAL_PRODS, Active=True
Key : Lower : Body : Upper : Active
F24 : -Inf : volume[1,F24] + volume[2,F24] + volume[3,F24] + volume[1,Q24] + volume[2,Q24] + volume[3,Q24] : 300.0 : True
J24 : -Inf : volume[1,J24] + volume[2,J24] + volume[3,J24] + volume[1,Q24] + volume[2,Q24] + volume[3,Q24] : 300.0 : True
10 Declarations: WEEKS PRODS volume_index volume MY_SPECIAL_PRODS C1a C1b_index C1b C2a C2b