I need to clone a built Pyomo model, but I'm uncertain about the behavior of model.clone(). After testing some code, I noticed that some variables are cloned while others are not.
Here’s my code example:
from pyomo.environ import *
from pyomo import *
cplex = SolverFactory('cplex')
model2 = ConcreteModel()
model3 = ConcreteModel()
model2.x = Var(within=Reals, bounds=(-10, 0))
model3.y = Var(within=Reals, bounds=(2, 3))
model3.obj = Objective(expr=(model2.x - model3.y) ** 2, sense=minimize)
model4 = model3.clone()
cplex.solve(model4)
assert model3.y.value is None
assert abs(model4.y.value - 2) < 1e-8
assert abs(model2.x.value - 0) < 1e-8
My question is: when a variable not belongs to the cloned model, does the clone() function create a new variable?
Can I infer that clone() is safe (clone will not cause confusion) when all the variables involved in the constraints and objective of a model belong to the same model?
Thank you for your help!
Is there one function in pyomo that can test this judgement for specific model
Pyomo models use Block
components to define a hierarchical structure and provide model scoping (and a ConcreteModel
/ AbstractModel
is just a special form of Block
). When modeling Component
objects are assigned to a block they are automatically added to that block’s scope.
The clone()
method implements a specialization of copy.deepcopy()
that will duplicate that BlockData
based on its scope: that is, copy that BlockData
and (recursively) all objects attached to the block (and any sub-block). Pyomo Component
/ ComponentData
objects that are referenced through objects that are being copied but are not in this block scope (i.e., are not owned by this block or a subblock of this block) are not duplicated.
Given the following model:
import pyomo.environ as pyo
m = pyo.ConcreteModel()
m.I = pyo.RangeSet(3)
m.x = pyo.Var()
m.b1 = pyo.Block()
m.b1.J = pyo.RangeSet(3)
m.b1.y = pyo.Var(domain=pyo.Reals)
m.b1.z = pyo.Var(m.I)
m.b1.c = pyo.Constraint(expr=m.x >= m.b1.y + sum(m.b1.z[:]))
m.b1.b2 = pyo.Block()
m.b1.b2.w = pyo.Var(m.b1.J)
m.b1.d = pyo.Constraint(expr=m.b1.y + sum(m.b1.b2.w[:]) == 5)
If we clone a block:
i = m.b1.clone()
All local components are copied:
assert m.b1 is not i
assert m.b1.J is not i.J
assert m.b1.y is not i.y
assert m.b1.z is not i.z
assert m.b1.b2 is not i.b2
assert m.b1.b2.w is not i.b2.w
References to local components (in this case, Sets) are copied and updated:
assert m.b1.b2.w.index_set() is not i.b2.w.index_set()
But references to out-of-scope Sets (either global or in a different block scope) are preserved:
assert m.b1.y.index_set() is i.y.index_set()
assert m.b1.z.index_set() is i.z.index_set()
assert m.b1.y.domain is i.y.domain
Expressions are also updated in a similar manner: the new expression will reference the new (copied) components for any components in scope, but references to out-of-scope components will be preserved:
from pyomo.core.expr.compare import compare_expressions
assert compare_expressions(i.c.expr, m.x >= i.y + sum(i.z[:]))
assert compare_expressions(i.d.expr, i.y + sum(i.b2.w[:]) == 5)