Search code examples
pythonoptimizationpyomo

The parameter values are not updating in the Pyomo model with PySP callback


I am using PySP (Pyomo) for one stochastic optimization problem. I created a concrete model for my problem and also defined the scenarios based on the farmer's example given in

https://github.com/Pyomo/pysp/blob/main/examples/farmer/concrete/ReferenceModel.py

In the above example a pysp_instance_creation_callback() function is called for each of the scenarios. In the function, an instance of the model is cloned for each scenario so that the scenario variable (Yield in this case) is updated for each of the scenarios using instance.Yield.store_values(Yield[scenario_name]).

I followed a similar approach to my problem. However, in my case, for each scenario the size of the unknown varies unlike the farmer's example, where the scenarios are for just three crops (wheat, sugar, corn). For instance, my scenarios would look like this,

Scenario1 = {123, 124, 118}
Scenario2 = {117, 10}
Scenario3 = {118, 120, 125, 126}
Scenario4 = {0, 125}
...

My code snippet looks something like the one below (I have only mentioned useful constraints and variables for simplicity)

# Variable:
model.nEdges = 129
model.x_ij = range(0, model.nEdges)  # line switching variable range
model.xij = Var(model.x_ij, bounds=(0, 1), within=Binary)

# Scenario parameter:
model.Fault = Param(mutable=True, initialize={123,124,118}, within=Any)

# Constraint:
for key, ite in model.Fault.items():
    for faulty in ite.value:
        model.c.add(model.xij[faulty] == 0)

# Scenarios:
Fault = {}
Fault['Scenario1'] = {123, 124, 118}
Fault['Scenario2'] = {120, 124, 118}
Fault['Scenario3'] = {1, 125}

# callback function to update the model parameter
def pysp_instance_creation_callback(scenario_name, node_names):
    instance = model.clone()
    instance.Fault.store_values(Fault[scenario_name])
    return instance

However, this method did not work for me. The model.Fault value remains the same for each of the scenarios as it was initialized i.e., {123,124,118}. Although if I check the instance value for each of the scenarios, i.e., instance.Fault.value, then it seems like the values are updating (instance.Fault.value gives different values consistent with different scenarios) but while checking the output lp file for the actual model, the constraints are not updated as desired and the final solution comes the same for each of the scenarios as mentioned before. I am not sure how to tackle this issue and I have been stuck in this problem for days. Can anybody help me here?


Solution

  • The short answer is that you are not using Param correctly. Mutable Params are meant to hold scalar values that appear in the expression tree (used in in either objectives or constraints). You are putting a complex data structure into the Param and then iterating over it when you create the original model, using the data in indirection (as the index of another variable). The reason that this is not working is that model.clone() makes a copy of model as it currently exists, which copies the original constraints. The constraints that you added to model.c are actually independent of the mutable Param Fault, so there is no way for Pyomo to know what / how to update the constraints when you change the values in it.

    A better approach for this specific situation would be to fix variables instead of creating additional constraints:

    model.xij = Var(model.x_ij, bounds=(0, 1), within=Binary)
    
    # Scenarios:
    Fault = {}
    Fault['Scenario1'] = {123, 124, 118}
    Fault['Scenario2'] = {120, 124, 118}
    Fault['Scenario3'] = {1, 125}
    
    # callback function to update the model parameter
    def pysp_instance_creation_callback(scenario_name, node_names):
        instance = model.clone()
        for faulty in Fault[scenario_name]:
            instance.xij[faulty].fix(0)
        return instance