Search code examples
pythonpyomo

Pyomo: How to pass None as parameter value


This is hopefully an easy question to answer. I'm trying to create a model that allows the user to pass None for a parameter value. This is so that they can define the upper/lower bound, or choose to have no bound. An example problem (diet problem) with set/param definitions, AMPL style data file section, and constraint function are below:

Set/Param Defs

model.Ingredients = pyo.Set()
model.Properties = pyo.Set()
model.IngredientProperties = pyo.Param(model.Ingredients, model.Properties)
model.MinProperty = pyo.Param(model.Properties, within=pyo.Any)
model.MaxProperty = pyo.Param(model.Properties, within=pyo.Any)

Data File Section

set Ingredients := Banana Milk Yogurt ;
set Properties := Fat Protein Carbs ;
param:  MinProperty  MaxProperty :=
      Fat  0.009       0.013
  Protein  0.200        None
    Carbs   None       0.070;
param IngredientProperties:    Fat    Protein  Carbs :=
                    Banana    0.375    0.020   0.010
                      Milk    0.003    0.075   0.015
                    Yogurt    0.015    0.650   0.075;

Constraint Function

def _property_constraint_rule(model, p):
    return (model.MinProperty[p], sum(
        model.IngredientProperties[i, p]
        * model.Blend[i]
        for i in model.Ingredients
        ), model.MaxProperty[p])

Unfortunately, when I try this, I get the error message: TypeError: Cannot treat the value 'None' as a constant. Is there another way to define parameters as None? I know that I could just use huge positive / negative numbers, but there has to be a better way.


Solution

  • If you are going to go forward with an Abstract model, I think you are stuck using a default value, which is fine. If you were using a concrete model, you could conditionally construct the constraint because the data is known at time of construction.

    Also, in your data file, you need to represent None values in this format as a period .. If you use the word 'None' it will barf as it is not recognized as Python's None

    This trial build worked fine for me...

    import pyomo.environ as pyo
    
    model = pyo.AbstractModel()
    
    model.Ingredients = pyo.Set()
    model.Properties = pyo.Set()
    model.IngredientProperties = pyo.Param(model.Ingredients, model.Properties)
    model.MinProperty = pyo.Param(model.Properties, within=pyo.Any, default=0.0)
    model.MaxProperty = pyo.Param(model.Properties, within=pyo.Any, default=100)
    
    model.Blend = pyo.Var(model.Ingredients, domain=pyo.NonNegativeReals)
    
    
    def _property_constraint_rule(model, p):
        return (model.MinProperty[p], sum(
            model.IngredientProperties[i, p] * model.Blend[i]
            for i in model.Ingredients), model.MaxProperty[p])
    model.c1 = pyo.Constraint(model.Properties, rule=_property_constraint_rule)
    
    data = pyo.DataPortal()
    data.load(filename='data.dat')
    
    instance = model.create_instance(data)
    
    instance.pprint()
    

    Yields:

    3 Set Declarations
        IngredientProperties_index : Dim=0, Dimen=2, Size=9, Domain=None, Ordered=False, Bounds=None
            Virtual
        Ingredients : Dim=0, Dimen=1, Size=3, Domain=None, Ordered=False, Bounds=None
            ['Banana', 'Milk', 'Yogurt']
        Properties : Dim=0, Dimen=1, Size=3, Domain=None, Ordered=False, Bounds=None
            ['Carbs', 'Fat', 'Protein']
    
    3 Param Declarations
        IngredientProperties : Size=9, Index=IngredientProperties_index, Domain=Any, Default=None, Mutable=False
            Key                   : Value
              ('Banana', 'Carbs') :  0.01
                ('Banana', 'Fat') : 0.375
            ('Banana', 'Protein') :  0.02
                ('Milk', 'Carbs') : 0.015
                  ('Milk', 'Fat') : 0.003
              ('Milk', 'Protein') : 0.075
              ('Yogurt', 'Carbs') : 0.075
                ('Yogurt', 'Fat') : 0.015
            ('Yogurt', 'Protein') :  0.65
        MaxProperty : Size=3, Index=Properties, Domain=Any, Default=100, Mutable=False
            Key   : Value
            Carbs :  0.07
              Fat : 0.013
        MinProperty : Size=3, Index=Properties, Domain=Any, Default=0.0, Mutable=False
            Key     : Value
                Fat : 0.009
            Protein :   0.2
    
    1 Var Declarations
        Blend : Size=3, Index=Ingredients
            Key    : Lower : Value : Upper : Fixed : Stale : Domain
            Banana :     0 :  None :  None : False :  True : NonNegativeReals
              Milk :     0 :  None :  None : False :  True : NonNegativeReals
            Yogurt :     0 :  None :  None : False :  True : NonNegativeReals
    
    1 Constraint Declarations
        c1 : Size=3, Index=Properties, Active=True
            Key     : Lower : Body                                                          : Upper : Active
              Carbs :   0.0 :  0.01*Blend[Banana] + 0.015*Blend[Milk] + 0.075*Blend[Yogurt] :  0.07 :   True
                Fat : 0.009 : 0.375*Blend[Banana] + 0.003*Blend[Milk] + 0.015*Blend[Yogurt] : 0.013 :   True
            Protein :   0.2 :   0.02*Blend[Banana] + 0.075*Blend[Milk] + 0.65*Blend[Yogurt] : 100.0 :   True
    
    8 Declarations: Ingredients Properties IngredientProperties_index IngredientProperties MinProperty MaxProperty Blend c1
    

    Modified data.dat

    set Ingredients := Banana Milk Yogurt ;
    set Properties := Fat Protein Carbs ;
    param:  MinProperty  MaxProperty :=
          Fat  0.009        0.013
      Protein  0.200        .
        Carbs  .            0.070;
    param IngredientProperties:    Fat    Protein  Carbs :=
                        Banana    0.375    0.020   0.010
                          Milk    0.003    0.075   0.015
                        Yogurt    0.015    0.650   0.075;