Repository: Pyomo/pyomo

Issue:
5.6.2 GAMS writer probelm: AttributeError: '_GeneralExpressionData' object has no attribute '_associativity'
I have noticed that for the same Pyomo model, using the GAMS writer in Pyomo 5.6.2 gives this error:

 AttributeError: '_GeneralExpressionData' object has no attribute '_associativity'

Which I believe is being raised in the gams_writer.py "visit" function. I do not get this error with Pyomo 5.6.1 for that same model.




Discussion:
Do you have a small example you can share that recreates this behavior?

Resolving patch:
diff --git a/pyomo/core/base/expression.py b/pyomo/core/base/expression.py
--- a/pyomo/core/base/expression.py
+++ b/pyomo/core/base/expression.py
@@ -80,6 +80,9 @@ def nargs(self):
     def _precedence(self):
         return 0
 
+    def _associativity(self):
+        return 0
+
     def _to_string(self, values, verbose, smap, compute_values):
         if verbose:
             return "%s{%s}" % (str(self), values[0])


Test patch:
diff --git a/pyomo/core/tests/unit/kernel/test_expression.py b/pyomo/core/tests/unit/kernel/test_expression.py
--- a/pyomo/core/tests/unit/kernel/test_expression.py
+++ b/pyomo/core/tests/unit/kernel/test_expression.py
@@ -99,6 +99,10 @@ def test_pprint(self):
         # tests compatibility with _ToStringVisitor
         pyomo.kernel.pprint(noclone(v)+1)
         pyomo.kernel.pprint(noclone(v+1))
+        x = variable()
+        y = variable()
+        pyomo.kernel.pprint(y + x*noclone(noclone(x*y)))
+        pyomo.kernel.pprint(y + noclone(noclone(x*y))*x)
 
     def test_pickle(self):
         v = variable()
@@ -550,6 +554,12 @@ class Test_expression(_Test_expression_base,
                       unittest.TestCase):
     _ctype_factory = expression
 
+    def test_associativity(self):
+        x = variable()
+        y = variable()
+        pyomo.kernel.pprint(y + x*expression(expression(x*y)))
+        pyomo.kernel.pprint(y + expression(expression(x*y))*x)
+
     def test_ctype(self):
         e = expression()
         self.assertIs(e.ctype, IExpression)
@@ -613,6 +623,14 @@ class Test_data_expression(_Test_expression_base,
 
     _ctype_factory = data_expression
 
+    def test_associativity(self):
+        x = parameter()
+        y = parameter()
+        pyomo.kernel.pprint(
+            y + x*data_expression(data_expression(x*y)))
+        pyomo.kernel.pprint(
+            y + data_expression(data_expression(x*y))*x)
+
     def test_ctype(self):
         e = data_expression()
         self.assertIs(e.ctype, IExpression)
diff --git a/pyomo/core/tests/unit/test_visitor.py b/pyomo/core/tests/unit/test_visitor.py
--- a/pyomo/core/tests/unit/test_visitor.py
+++ b/pyomo/core/tests/unit/test_visitor.py
@@ -393,6 +393,16 @@ def test_expression_to_string(self):
         M.w.fixed = True
         self.assertEqual("sin(x) + x*2 + 3", expression_to_string(e, compute_values=True))
 
+    def test_expression_component_to_string(self):
+        m = ConcreteModel()
+        m.x = Var()
+        m.y = Var()
+        m.e = Expression(expr=m.x*m.y)
+        m.f = Expression(expr=m.e)
+
+        e = m.x + m.f*m.y
+        self.assertEqual("x + ((x*y))*y", str(e))
+        self.assertEqual("x + ((x*y))*y", expression_to_string(e))
 #
 # Replace all variables with a product expression
 #