internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: Pyomo/pyomo

Issue:
Let's drop support for Python 2.6
I don't see any meaningful reason to continue support for Python2.6, but let's start a conversation here about this possible change.

I've seen evidence that we're spending time supporting this version of Python without deriving any meaningful utility.


Discussion:
Here's the pip installs for pyomo from PyPI for the last month (via `pypinfo --percent --pip --markdown pyomo pyversion`) -- no Python 2.6.

| python_version | percent | download_count |
| -------------- | ------: | -------------: |
| 2.7            |   54.4% |          1,141 |
| 3.5            |   20.6% |            433 |
| 3.6            |   20.1% |            421 |
| 3.4            |    4.7% |             99 |
| 3.7            |    0.1% |              3 |
@hugovk Nice!  I didn't realize that would could get that info from that data store.

Yeah, you can get all sorts of things out of it, see https://github.com/ofek/pypinfo for some other interesting examples.
I'm trying to think of things that we can start doing in code once we officially drop support for 2.6 (hoping others will add to the list). A few things that come to mind:

1. Alternative (and much faster) dict initialization syntax: {key:1.5 for key in range(10)}
2. Instead of nesting `with` blocks one can simply separate `with` arguments with a comma, which allows reducing the number of indentation levels needed (e.g., `with A() as a, B() as b:`)

Resolving patch:
diff --git a/admin/jenkins.py b/admin/jenkins.py
--- a/admin/jenkins.py
+++ b/admin/jenkins.py
@@ -12,11 +12,7 @@
 import sys
 import os
 import subprocess
-try:
-    from subprocess import check_output as _run_cmd
-except:
-    # python 2.6
-    from subprocess import check_call as _run_cmd
+from subprocess import check_output as _run_cmd
 import driver
 
 config = sys.argv[1]
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -81,7 +81,6 @@ def read(*rnames):
         'Operating System :: Unix',
         'Programming Language :: Python',
         'Programming Language :: Python :: 2',
-        'Programming Language :: Python :: 2.6',
         'Programming Language :: Python :: 2.7',
         'Programming Language :: Python :: 3',
         'Programming Language :: Python :: 3.4',
@@ -95,6 +94,7 @@ def read(*rnames):
       packages=packages,
       keywords=['optimization'],
       install_requires=requires,
+      python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*',
       entry_points="""
         [console_scripts]
         runbenders=pyomo.pysp.benders:Benders_main


Test patch:
Repository: Pyomo/pyomo

Issue:
Merge of improvements to CPLEX/Gurobi interfaces #331 conflicting with python virtual environments
I noticed some very strange behavior running tests after the merge of #331:
```
Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
  File "/home/qichen/git/pyomo/pyomo/solvers/plugins/solvers/GUROBI_RUN.py", line 15, in <module>
    from six.moves import zip
ImportError: No module named six.moves
```
This error usually means that I haven't done `pip install six`, but my activated virtual environment does in fact have `six` installed. I found that doing the following "fixes" the problem:
```
deactivate
sudo apt install python-pip
pip install six
source /path_to_my_venv/bin/activate
# run tests again
```
This makes me wonder if the import of `GUROBI_RUN.py` happens outside of my virtual environment.


Discussion:
That’s a good point. It actually does. It is run by gurobi.sh. We should remove the import.

Gabe
@qtothec Thanks for bringing this up. Good point. I will get it fixed asap.

Resolving patch:
diff --git a/pyomo/solvers/plugins/solvers/GUROBI_RUN.py b/pyomo/solvers/plugins/solvers/GUROBI_RUN.py
--- a/pyomo/solvers/plugins/solvers/GUROBI_RUN.py
+++ b/pyomo/solvers/plugins/solvers/GUROBI_RUN.py
@@ -11,8 +11,16 @@
 
 import re
 
+
+"""
+This script is run using the Gurobi/system python. Do not assume any third party packages
+are available!
+"""
 from gurobipy import *
-from six.moves import zip
+import sys
+if sys.version_info[0] < 3:
+    from itertools import izip as zip
+    
 
 GUROBI_VERSION = gurobi.version()
 


Test patch:
Repository: Pyomo/pyomo

Issue:
Review objects exposed by environ
At the request of @jsiirola after I brought this to his attention, some Pyomo objects are not exposed by environ that would otherwise be expected. One that I have encountered is `TerminationCondition`, which needs to be imported from `pyomo.opt`.


Discussion:
Aside from TerminationCondition, what else appears to be missing?  I'm not sure what the action is for this ticket other than exposing that object?

Resolving patch:
diff --git a/pyomo/environ/__init__.py b/pyomo/environ/__init__.py
--- a/pyomo/environ/__init__.py
+++ b/pyomo/environ/__init__.py
@@ -93,4 +93,7 @@ def _import_packages():
 # Expose the symbols from pyomo.core
 #
 from pyomo.core import *
-from pyomo.opt import SolverFactory, SolverManagerFactory, UnknownSolver
+from pyomo.opt import (
+    SolverFactory, SolverManagerFactory, UnknownSolver,
+    TerminationCondition, SolverStatus,
+)


Test patch:
Repository: Pyomo/pyomo

Issue:
component.py pprint method duped
In pyomo/base/core/component.py for class Component you have pprint() method defined starting at line 373 and again at 477.  If previously reported or I'm missing something, my apologies


Discussion:

Resolving patch:
diff --git a/pyomo/core/base/component.py b/pyomo/core/base/component.py
--- a/pyomo/core/base/component.py
+++ b/pyomo/core/base/component.py
@@ -474,26 +474,6 @@ def name(self, val):
                 "is assigned to a Block.\nTriggered by attempting to set "
                 "component '%s' to name '%s'" % (self.name,val))
 
-    def pprint(self, ostream=None, verbose=False, prefix=""):
-        """Print component information"""
-        if ostream is None:
-            ostream = sys.stdout
-        tab="    "
-        ostream.write(prefix+self.local_name+" : ")
-        if self.doc is not None:
-            ostream.write(self.doc+'\n'+prefix+tab)
-
-        _attr, _data, _header, _fcn = self._pprint()
-
-        ostream.write(", ".join("%s=%s" % (k,v) for k,v in _attr))
-        ostream.write("\n")
-        if not self._constructed:
-            ostream.write(prefix+tab+"Not constructed\n")
-            return
-
-        if _data is not None:
-            tabular_writer( ostream, prefix+tab, _data, _header, _fcn )
-
     def is_indexed(self):
         """Return true if this component is indexed"""
         return False


Test patch:
Repository: Pyomo/pyomo

Issue:
GDP bigM enforcing deactivated disjunctions
I have an indexed disjunction, among which several elements are disabled. However, the big-M transformation created active XOR constraints for the indicator variables of these deactivated disjunctions anyways.


Discussion:

Resolving patch:
diff --git a/pyomo/gdp/plugins/bigm.py b/pyomo/gdp/plugins/bigm.py
--- a/pyomo/gdp/plugins/bigm.py
+++ b/pyomo/gdp/plugins/bigm.py
@@ -314,6 +314,8 @@ def _transformDisjunction(self, obj, transBlock, bigM):
         obj.deactivate()
 
     def _transformDisjunctionData(self, obj, transBlock, bigM, index):
+        if not obj.active:
+            return  # Do not process a deactivated disjunction
         parent_component = obj.parent_component()
         transBlock.disjContainers.add(parent_component)
         orConstraint = self._getXorConstraint(parent_component)


Test patch:
diff --git a/pyomo/gdp/tests/test_bigm.py b/pyomo/gdp/tests/test_bigm.py
--- a/pyomo/gdp/tests/test_bigm.py
+++ b/pyomo/gdp/tests/test_bigm.py
@@ -2042,6 +2042,26 @@ def test_xor_constraint(self):
             self.assertIs(xorC[i].body.arg(0), m.disjunct[i, 0].indicator_var)
             self.assertIs(xorC[i].body.arg(1), m.disjunct[i, 1].indicator_var)
 
+    def test_partial_deactivate_indexed_disjunction(self):
+        """Test for partial deactivation of a indexed disjunction."""
+        m = ConcreteModel()
+        m.x = Var(bounds=(0, 10))
+        @m.Disjunction([0, 1])
+        def disj(m, i):
+            if i == 0:
+                return [m.x >= 1, m.x >= 2]
+            else:
+                return [m.x >= 3, m.x >= 4]
+
+        m.disj[0].disjuncts[0].indicator_var.fix(1)
+        m.disj[0].disjuncts[1].indicator_var.fix(1)
+        m.disj[0].deactivate()
+        TransformationFactory('gdp.bigm').apply_to(m)
+        self.assertEqual(
+            len(m._gdp_bigm_relaxation_disj_xor), 1,
+            "There should only be one XOR constraint generated. Found %s." %
+            len(m._gdp_bigm_relaxation_disj_xor))
+
 
 class BlocksOnDisjuncts(unittest.TestCase):
     def setUp(self):
@@ -2128,4 +2148,3 @@ def test_activeInnerDisjunction_err(self):
 
 if __name__ == '__main__':
     unittest.main()
-
Repository: Pyomo/pyomo

Issue:
Simulator fails when differential variables are not used in a constraint
This bug is isolated to when casadi is used as the integration package. The toy model below produces an error when the `simulate` function is called:

```
from pyomo.environ import *
from pyomo.dae import *

m = ConcreteModel()
m.t = ContinuousSet(bounds=(0,10))
m.p = Param(initialize=5, mutable=True)
m.v1 = Var(m.t)
m.dv1 = DerivativeVar(m.v1)

def _con1(m, t):
    return m.dv1[t] == 10 + m.p
m.con1 = Constraint(m.t, rule=_con1)

m.v1[0] = 0

mysim = Simulator(m, package='casadi')
tsim, profiles = mysim.simulate(numpoints=100)
```

```
Traceback (most recent call last):
  File "temp.py", line 17, in <module>
    tsim, profiles = mysim.simulate(numpoints=100)
  File "/home/blnicho/Research/pyomo/pyomo/dae/simulator.py", line 773, in simulate
    integrator_options)
  File "/home/blnicho/Research/pyomo/pyomo/dae/simulator.py", line 813, in _simulate_with_casadi_no_inputs
    xalltemp = [self._templatemap[i] for i in self._diffvars]
KeyError: <pyomo.core.base.template_expr._GetItemIndexer object at 0x7fbf3c852590>
>>> 

```

The fix is to make sure all differential variables are added to the template map when a `Simulator` object is initialized.


Discussion:

Resolving patch:
diff --git a/pyomo/dae/simulator.py b/pyomo/dae/simulator.py
--- a/pyomo/dae/simulator.py
+++ b/pyomo/dae/simulator.py
@@ -622,6 +622,14 @@ def _rhsfun(t, x):
                 return residual
             self._rhsfun = _rhsfun   
             
+        # Add any diffvars not added by expression walker to self._templatemap
+        if self._intpackage == 'casadi':
+            for _id in diffvars:
+                if _id not in templatemap:
+                    name = "%s[%s]" % (
+                        _id._base.name, ','.join(str(x) for x in _id._args))
+                    templatemap[_id] = casadi.SX.sym(name)
+
         self._contset = contset
         self._cstemplate = cstemplate
         self._diffvars = diffvars


Test patch:
diff --git a/pyomo/dae/tests/test_simulator.py b/pyomo/dae/tests/test_simulator.py
--- a/pyomo/dae/tests/test_simulator.py
+++ b/pyomo/dae/tests/test_simulator.py
@@ -888,6 +888,25 @@ def _diffeq2(m, s, t):
         m.del_component('con2')
         m.del_component('con2_index')
 
+    # check that all diffvars have been added to templatemap even if not
+    # appearing in RHS of a differential equation
+    @unittest.skipIf(not casadi_available, "casadi not available")
+    def test_nonRHS_vars(self):
+
+        m = self.m
+        m.v2 = Var(m.t)
+        m.dv2 = DerivativeVar(m.v2)
+        m.p = Param(initialize=5)
+        t = IndexTemplate(m.t)
+
+        def _con(m, t):
+            return m.dv2[t] == 10 + m.p
+        m.con = Constraint(m.t, rule=_con)
+
+        mysim = Simulator(m,package='casadi')
+        self.assertEqual(len(mysim._templatemap), 1)
+        self.assertEqual(mysim._diffvars[0], _GetItemIndexer(m.v2[t]))
+        m.del_component('con')
 
 class TestExpressionCheckers(unittest.TestCase):
     """
Repository: Pyomo/pyomo

Issue:
Ipopt has_capability('integer') returns True
```
>>> import pyomo.environ as pe
>>> pe.SolverFactory('ipopt').has_capability('integer')
True
```

I think this should return False. There is a comment in the code that says returning False might create headaches for some people, but I don't see how. Can I change this?


Discussion:

Resolving patch:
diff --git a/pyomo/solvers/plugins/solvers/IPOPT.py b/pyomo/solvers/plugins/solvers/IPOPT.py
--- a/pyomo/solvers/plugins/solvers/IPOPT.py
+++ b/pyomo/solvers/plugins/solvers/IPOPT.py
@@ -52,13 +52,11 @@ def __init__(self, **kwds):
         # Note: Undefined capabilities default to 'None'
         self._capabilities = pyutilib.misc.Options()
         self._capabilities.linear = True
-        # Should we set this to False? Doing so might cause
-        # a headache for some folks.
-        self._capabilities.integer = True
+        self._capabilities.integer = False
         self._capabilities.quadratic_objective = True
         self._capabilities.quadratic_constraint = True
-        self._capabilities.sos1 = True
-        self._capabilities.sos2 = True
+        self._capabilities.sos1 = False
+        self._capabilities.sos2 = False
 
     def _default_results_format(self, prob_format):
         return ResultsFormat.sol


Test patch:
Repository: Pyomo/pyomo

Issue:
BARON writer sqrt(x) issue
I believe that the BARON writer needs to produce `x^0.5` rather than the current `sqrt(x)` in order to avoid a syntax error.

Found by @johnnybatesjr


Discussion:
@johnnybatesjr has a fix locally. I've asked him to submit a PR for it.

Resolving patch:
diff --git a/pyomo/repn/plugins/baron_writer.py b/pyomo/repn/plugins/baron_writer.py
--- a/pyomo/repn/plugins/baron_writer.py
+++ b/pyomo/repn/plugins/baron_writer.py
@@ -84,6 +84,8 @@ def visit(self, node, values):
                 return "{0} * {1}".format(tmp[0], tmp[1])
         elif node.__class__ is EXPR.PowExpression:
             return "{0} ^ {1}".format(tmp[0], tmp[1])
+        elif node.__class__ is EXPR.UnaryFunctionExpression and node.name == "sqrt":
+            return "{0} ^ 0.5".format(tmp[0])
         else:
             return node._to_string(tmp, None, self.smap, True)
 


Test patch:
diff --git a/pyomo/repn/tests/baron/small14.pyomo.bar b/pyomo/repn/tests/baron/small14.pyomo.bar
--- a/pyomo/repn/tests/baron/small14.pyomo.bar
+++ b/pyomo/repn/tests/baron/small14.pyomo.bar
@@ -24,7 +24,7 @@ c12: asinh(x2) == 0;
 c13: acosh((7.3890560989306495 + x1) * 0.18393972058572117) == 0;
 c14: atanh(x2) == 0;
 c15: exp(x2) == 1;
-c16: sqrt(x1) == 1;
+c16: x1 ^ 0.5 == 1;
 c17: ceil(x1) == 1;
 c18: floor(x1) == 1;
 c19: abs(x1) == 1;
Repository: Pyomo/pyomo

Issue:
gams writer, splitting lines with characters > 80,000
if line is > 80,000 the line is splitted at the last space within the fist 80,000 characters '(function 'split_long_line' of 'gams_writer.py' This mostly works but sometimes leads to an error if the space is followed by an '*' (multiply symbol). 







Discussion:

Resolving patch:
diff --git a/pyomo/repn/plugins/gams_writer.py b/pyomo/repn/plugins/gams_writer.py
--- a/pyomo/repn/plugins/gams_writer.py
+++ b/pyomo/repn/plugins/gams_writer.py
@@ -244,7 +244,9 @@ def split_long_line(line):
                     "Found an 80,000+ character string with no spaces")
             i -= 1
         new_lines += line[:i] + '\n'
-        line = line[i + 1:]
+        # the space will be the first character in the next line,
+        # so that the line doesn't start with the comment character '*'
+        line = line[i:]
     new_lines += line
     return new_lines
 


Test patch:
diff --git a/pyomo/repn/tests/gams/test_gams.py b/pyomo/repn/tests/gams/test_gams.py
--- a/pyomo/repn/tests/gams/test_gams.py
+++ b/pyomo/repn/tests/gams/test_gams.py
@@ -227,10 +227,16 @@ def test_split_long_line(self):
         pat = "var1 + log(var2 / 9) - "
         line = (pat * 10000) + "x"
         self.assertEqual(split_long_line(line),
-                         pat * 3478 + "var1 +\nlog(var2 / 9) - " +
-                         pat * 3477 + "var1 +\nlog(var2 / 9) - " +
+                         pat * 3478 + "var1 +\n log(var2 / 9) - " +
+                         pat * 3477 + "var1 +\n log(var2 / 9) - " +
                          pat * 3043 + "x")
 
+    def test_split_long_line_no_comment(self):
+        pat = "1000 * 2000 * "
+        line = pat * 5715 + "x"
+        self.assertEqual(split_long_line(line),
+            pat * 5714 + "1000\n * 2000 * x")
+
     def test_solver_arg(self):
         m = ConcreteModel()
         m.x = Var()
Repository: Pyomo/pyomo

Issue:
Pyomo 5.6 CPLEX "Unallowed character (:) found in CPLEX log file" ValueError on Windows
Hi,

After installing Pyomo 5.6, my unit tests fail on Windows 7. Here are the details:

filename = 'C:\\Users\\<username>\\AppData\\Local\\Temp\\tmp9v95r1qz.cplex.log'
description = 'log'

    def _validate_file_name(cplex, filename, description):
        """Validate filenames against the set of allowable chaacters in CPLEX.

        Returns the filename, possibly enclosed in double-quotes, or raises
        a ValueError is unallowable characters are found.
        """
       ...
E           ValueError: Unallowed character (:) found in CPLEX log file path/name.
E               For portability reasons, only [a-zA-Z0-9 .-_] are allowed.

..\..\AppData\Local\Continuum\Anaconda3\lib\site-packages\pyomo\solvers\plugins\solvers\CPLEX.py:53: ValueError

It seems that "C:\\path..." is no longer valid in Pyomo 5.6, which is a bit difficult on Windows.

Others are experiencing the same problem:

https://stackoverflow.com/questions/54095681/why-am-i-getting-an-unallowed-character-error-in-pyomo-with-cplex

Can you fix this?

Thanks. 


Discussion:

Resolving patch:
diff --git a/pyomo/solvers/plugins/solvers/CPLEX.py b/pyomo/solvers/plugins/solvers/CPLEX.py
--- a/pyomo/solvers/plugins/solvers/CPLEX.py
+++ b/pyomo/solvers/plugins/solvers/CPLEX.py
@@ -62,7 +62,7 @@ def _validate_file_name(cplex, filename, description):
                 "CPLEX or remove the space from the %s path."
                 % (description, filename, description))
     return filename
-_validate_file_name.allowed_characters = r"a-zA-Z0-9 \.\-_\%s" % (os.path.sep,)
+_validate_file_name.allowed_characters = r"a-zA-Z0-9 :\.\-_\%s" % (os.path.sep,)
 _validate_file_name.illegal_characters = re.compile(
     '[^%s]' % (_validate_file_name.allowed_characters,))
 


Test patch:
Repository: Pyomo/pyomo

Issue:
NEOS error
Our current build is failing because of the error below, I speculate due to usually-untested code being triggered by a transient network failure.
```
======================================================================
ERROR: test_kestrel_plugin (pyomo.neos.tests.test_neos.TestKestrel)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/travis/build/Pyomo/pyomo/pyomo/neos/tests/test_neos.py", line 90, in test_kestrel_plugin
    results = solver_manager.solve(m, opt='cbc')
  File "/home/travis/build/Pyomo/pyomo/pyomo/opt/parallel/async_solver.py", line 28, in solve
    return self.execute(*args, **kwds)
  File "/home/travis/build/Pyomo/pyomo/pyomo/opt/parallel/manager.py", line 107, in execute
    ah = self.queue(*args, **kwds)
  File "/home/travis/build/Pyomo/pyomo/pyomo/opt/parallel/manager.py", line 122, in queue
    return self._perform_queue(ah, *args, **kwds)
  File "/home/travis/build/Pyomo/pyomo/pyomo/neos/plugins/kestrel_plugin.py", line 127, in _perform_queue
    raise ActionManagerError(
NameError: name 'ActionManagerError' is not defined
```
NEOS error
Our current build is failing because of the error below, I speculate due to usually-untested code being triggered by a transient network failure.
```
======================================================================
ERROR: test_kestrel_plugin (pyomo.neos.tests.test_neos.TestKestrel)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/travis/build/Pyomo/pyomo/pyomo/neos/tests/test_neos.py", line 90, in test_kestrel_plugin
    results = solver_manager.solve(m, opt='cbc')
  File "/home/travis/build/Pyomo/pyomo/pyomo/opt/parallel/async_solver.py", line 28, in solve
    return self.execute(*args, **kwds)
  File "/home/travis/build/Pyomo/pyomo/pyomo/opt/parallel/manager.py", line 107, in execute
    ah = self.queue(*args, **kwds)
  File "/home/travis/build/Pyomo/pyomo/pyomo/opt/parallel/manager.py", line 122, in queue
    return self._perform_queue(ah, *args, **kwds)
  File "/home/travis/build/Pyomo/pyomo/pyomo/neos/plugins/kestrel_plugin.py", line 127, in _perform_queue
    raise ActionManagerError(
NameError: name 'ActionManagerError' is not defined
```


Discussion:

Resolving patch:
diff --git a/pyomo/neos/plugins/kestrel_plugin.py b/pyomo/neos/plugins/kestrel_plugin.py
--- a/pyomo/neos/plugins/kestrel_plugin.py
+++ b/pyomo/neos/plugins/kestrel_plugin.py
@@ -16,6 +16,7 @@
 from six.moves.xmlrpc_client import ProtocolError
 
 from pyomo.opt import SolverFactory, SolverManagerFactory, OptSolver
+from pyomo.opt.parallel.manager import ActionManagerError
 from pyomo.opt.parallel.async_solver import (
     AsynchronousSolverManager, ActionStatus
 )


Test patch:
Repository: Pyomo/pyomo

Issue:
LinearExpression incompatible with identify_variables()
`LinearExpression` always returns `expr.nargs() = 0` and has no `args`. This causes the walker for `identify_variables()` to see it as a leaf node. Unfortunately, it is not a leaf node.

This issue probably also causes the effect observed in #709.


Discussion:

Resolving patch:
diff --git a/pyomo/core/expr/expr_pyomo5.py b/pyomo/core/expr/expr_pyomo5.py
--- a/pyomo/core/expr/expr_pyomo5.py
+++ b/pyomo/core/expr/expr_pyomo5.py
@@ -1249,6 +1249,19 @@ def visit(self, node):
             self.seen.add(id(node))
             return node
 
+        if node.is_expression_type() and isinstance(node, LinearExpression):
+            if id(node) in self.seen:
+                return
+            self.seen.add(id(node))
+
+            def unique_vars_generator():
+                for var in node.linear_vars:
+                    if id(var) in self.seen:
+                        continue
+                    self.seen.add(id(var))
+                    yield var
+            return tuple(v for v in unique_vars_generator())
+
 
 def identify_variables(expr, include_fixed=True):
     """
@@ -1267,11 +1280,20 @@ def identify_variables(expr, include_fixed=True):
     visitor = _VariableVisitor()
     if include_fixed:
         for v in visitor.xbfs_yield_leaves(expr):
-            yield v
+            if isinstance(v, tuple):
+                for v_i in v:
+                    yield v_i
+            else:
+                yield v
     else:
         for v in visitor.xbfs_yield_leaves(expr):
-            if not v.is_fixed():
-                yield v
+            if isinstance(v, tuple):
+                for v_i in v:
+                    if not v_i.is_fixed():
+                        yield v_i
+            else:
+                if not v.is_fixed():
+                    yield v
 
 
 # =====================================================


Test patch:
diff --git a/pyomo/core/tests/unit/test_expr_pyomo5.py b/pyomo/core/tests/unit/test_expr_pyomo5.py
--- a/pyomo/core/tests/unit/test_expr_pyomo5.py
+++ b/pyomo/core/tests/unit/test_expr_pyomo5.py
@@ -5149,6 +5149,12 @@ def test_identify_vars_vars(self):
         #self.assertEqual( list(EXPR.identify_variables(m.a**m.a + m.a, allow_duplicates=True)),
         #                  [ m.a, m.a, m.a,  ] )
 
+    def test_identify_vars_linear_expression(self):
+        m = ConcreteModel()
+        m.x = Var()
+        expr = quicksum([m.x, m.x], linear=True)
+        self.assertEqual(list(EXPR.identify_variables(expr, include_fixed=False)), [m.x])
+
 
 class TestIdentifyParams(unittest.TestCase):