I'm trying to calculate just in time purchasing. This seems to work just fine, but I want to have
- a minimum purchase of 5.
- a minimum inventory of 3.
Unfortunately, this code makes sure I purchase every month, and I only want to purchase some of the time. How do I fix that?
model=ConcreteModel(name='Inventory-MIN')
demand=[4,3,2,1,2,3,4]
months=len(demand)
holding_cost=1
purchase_cost=2
model.purchase_qty=Var(range(months), domain=NonNegativeIntegers) # Not NonNegativeReals
# ending quantity of inventory per month
model.end_inventory=Var(range(months), domain=NonNegativeIntegers)
model.cost=Objective(expr=sum(
    model.purchase_qty[month]* # Purchase quantity
    purchase_cost+ # Purchase price
    model.end_inventory[month]* # previous inventory
    holding_cost # Holding cost
    for month in range(1,months,1)), sense=minimize) # start at 1, stop at months, iterate by 1
model.constraints=ConstraintList() # empty list of constraints
model.constraints.add(model.end_inventory[0] == 0)
for month in range(1,months,1):
  next_end_inventory = model.end_inventory[month-1]+model.purchase_qty[month]-demand[month]
  model.constraints.add(model.end_inventory[month] == next_end_inventory)
  # here is where we define a minimum inventory level
  model.constraints.add(model.end_inventory[month]>=3)
  # must buy in bulk
  model.constraints.add(model.purchase_qty[month]>=5) # none of the months have ZERO ORDER !??!?!?!?
# Solve the model
SolverFactory('glpk',executable='/usr/bin/glpsol').solve(model).write()
I'm getting this, clearly having an inventory of 16 is too much.
# How much we purchase every month
purchase_qty : Size=7, Index=purchase_qty_index
    Key : Lower : Value : Upper : Fixed : Stale : Domain
      0 :     0 :  None :  None : False :  True : NonNegativeIntegers
      1 :     0 :   6.0 :  None : False : False : NonNegativeIntegers
      2 :     0 :   5.0 :  None : False : False : NonNegativeIntegers
      3 :     0 :   5.0 :  None : False : False : NonNegativeIntegers
      4 :     0 :   5.0 :  None : False : False : NonNegativeIntegers
      5 :     0 :   5.0 :  None : False : False : NonNegativeIntegers
      6 :     0 :   5.0 :  None : False : False : NonNegativeIntegers
#
# Inventory at end of month
end_inventory : Size=7, Index=end_inventory_index
    Key : Lower : Value : Upper : Fixed : Stale : Domain
      0 :     0 :   0.0 :  None : False : False : NonNegativeIntegers
      1 :     0 :   3.0 :  None : False : False : NonNegativeIntegers
      2 :     0 :   6.0 :  None : False : False : NonNegativeIntegers
      3 :     0 :  10.0 :  None : False : False : NonNegativeIntegers
      4 :     0 :  13.0 :  None : False : False : NonNegativeIntegers
      5 :     0 :  15.0 :  None : False : False : NonNegativeIntegers
      6 :     0 :  16.0 :  None : False : False : NonNegativeIntegers
I tried putting the critical constraint into an if function but got this error:
Cannot convert non-constant Pyomo expression (%s) to bool.
 
                        
You're on the right track. Here are a couple of fixes...
The main concept you are missing is a "semi-continuous variable" meaning that it is either zero or within some range. That can be done (as shown below) with a "big-M" type of constraint. (google it or see any LP textbook)
I also changed up a bit of the 0-month logic in your objective and in the constraints. In the obj, you still need to include the first month to capture purchase costs during the month.
Code:
(truncated) Output: