Search code examples
pythonpicklepyomo

How to fix 'recursion error' when pickling Pyomo Expressions


I haven't had trouble with pickling a Pyomo model before, but now a recursion error is being raised when trying to pickle Expressions.

Interestingly, the error can be avoided in the example below via a couple of ways:

  • by removing the "mutable" flag from the parameter
  • by reducing the size of the set to a very small value, to e.g. range(0, 10)

...but I don't know why these would fix the error, nor are they workable solutions for the actual optimization model I am trying to pickle.

The following example generates the error from the pickling of just a single Expression. (I am using pyomo=5.6.2, cloudpickle=0.6.1, and python=3.7.4)

import cloudpickle
import pyomo.environ as pyo

test_model = pyo.ConcreteModel()

# A set is added.
set_elements = list(range(0, 500))
test_model.my_set = pyo.Set(initialize=set_elements)

# A parameter is added.
param_values = dict()
for e in set_elements:
    param_values[e] = 1
test_model.my_param = pyo.Param(test_model.my_set, initialize=param_values, mutable=True)

# An expression is added.
def calculation_rule(mdl):
    return sum(mdl.my_param[e] for e in mdl.my_set)
test_model.calculation_expr = pyo.Expression(rule=calculation_rule)

# We attempt to pickle the expression.
pickle_str = cloudpickle.dumps(test_model.calculation_expr)

The last line of the above code raises the following exception:

PicklingError: Could not pickle object as excessively deep recursion required.

QUESTION: Do I need to modify the way the Expression is written in order to pickle the model, or should I save the model using something other than Cloudpickle?

Thanks in advance for any help!


Solution

  • One fix is to use Pyomo's quicksum instead of Python's sum. It leads to more compact expression trees and seems to fix the recursion issue you're seeing:

    # An expression is added.
    def calculation_rule(mdl):
        return pyo.quicksum(mdl.my_param[e] for e in mdl.my_set)
    test_model.calculation_expr = pyo.Expression(rule=calculation_rule)
    

    Documentation here.