Summary
Sometimes, the LinearStandardFormCompiler throws an error saying that an expression is nonlinear even if the variables that would make it nonlinear are fixed.
Steps to reproduce the issue
In order to get a simple example that illustrates the problem, I created an instance of LinearRepnVisitor the same way that LinearStandardFormCompiler makes one. See this line and the few lines before it.
In [1]: import pyomo.environ as pe
...: from pyomo.repn.linear import LinearRepnVisitor
...: from pyomo.repn.util import TemplateVarRecorder, SortComponents
...: m = pe.ConcreteModel()
...: m.a = pe.Set(initialize=[1,2,3])
...: m.x = pe.Var(m.a)
...: e1 = m.x[1] * (m.x[2] + m.x[3])
...: e2 = m.x[1] - 1/m.x[2]
...: m.x[2].fix(2)
...: sorter = SortComponents.ORDERED_INDICES
...: vmap = {}
...: vr = TemplateVarRecorder(vmap, sorter)
...: visitor = LinearRepnVisitor(subexpression_cache={}, var_recorder=vr)
...: r1 = visitor.walk_expression(e1)
In [2]: r2 = visitor.walk_expression(e2)
In [3]: print(r2)
LinearRepn(mult=1, const=0, linear={140176910789184: 1}, nonlinear=- (1/x[2]))
Here, e2 should be linear because x[2] is fixed, but the LinearRepnVisitor says it is nonlinear. The problem is that the TemplateVarRecorder inserts variables in bulk based on the parent component. So, when the LinearRepnVisitor is used to walk e1, x[2] gets added to the var_map even though it is fixed. Then, LinearBeforeChildDispatcher._before_var does not behave as expected. With the variable fixed, we want to hit this line. However, because x[2] is in the var_map, we instead continue to this line, and the visitor thinks the variable is not fixed.
Note that this does not happen if we create the LinearRepnVisitor without the var_recorder.
Information on your system
Pyomo version: just pulled main
Summary
Sometimes, the
LinearStandardFormCompilerthrows an error saying that an expression is nonlinear even if the variables that would make it nonlinear are fixed.Steps to reproduce the issue
In order to get a simple example that illustrates the problem, I created an instance of
LinearRepnVisitorthe same way thatLinearStandardFormCompilermakes one. See this line and the few lines before it.Here,
e2should be linear becausex[2]is fixed, but theLinearRepnVisitorsays it is nonlinear. The problem is that theTemplateVarRecorderinserts variables in bulk based on the parent component. So, when theLinearRepnVisitoris used to walke1,x[2]gets added to thevar_mapeven though it is fixed. Then,LinearBeforeChildDispatcher._before_vardoes not behave as expected. With the variable fixed, we want to hit this line. However, becausex[2]is in thevar_map, we instead continue to this line, and the visitor thinks the variable is not fixed.Note that this does not happen if we create the
LinearRepnVisitorwithout thevar_recorder.Information on your system
Pyomo version: just pulled main