From e55007991d8ec0cadc84bc12bbddfcb01225bdcb Mon Sep 17 00:00:00 2001 From: Eslick Date: Fri, 22 Mar 2024 14:47:31 -0400 Subject: [PATCH 1/9] Fix for duplicate add --- pyomo/solvers/plugins/solvers/ASL.py | 4 +++- pyomo/solvers/plugins/solvers/IPOPT.py | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pyomo/solvers/plugins/solvers/ASL.py b/pyomo/solvers/plugins/solvers/ASL.py index ae7ad82c870..3ebe5c3b422 100644 --- a/pyomo/solvers/plugins/solvers/ASL.py +++ b/pyomo/solvers/plugins/solvers/ASL.py @@ -160,7 +160,9 @@ def create_command_line(self, executable, problem_files): # if 'PYOMO_AMPLFUNC' in env: if 'AMPLFUNC' in env: - env['AMPLFUNC'] += "\n" + env['PYOMO_AMPLFUNC'] + for line in env['PYOMO_AMPLFUNC'].split('\n'): + if line not in env['AMPLFUNC']: + env['AMPLFUNC'] += "\n" + line else: env['AMPLFUNC'] = env['PYOMO_AMPLFUNC'] diff --git a/pyomo/solvers/plugins/solvers/IPOPT.py b/pyomo/solvers/plugins/solvers/IPOPT.py index 4ebbbc07d3b..17b68da6364 100644 --- a/pyomo/solvers/plugins/solvers/IPOPT.py +++ b/pyomo/solvers/plugins/solvers/IPOPT.py @@ -121,7 +121,9 @@ def create_command_line(self, executable, problem_files): # if 'PYOMO_AMPLFUNC' in env: if 'AMPLFUNC' in env: - env['AMPLFUNC'] += "\n" + env['PYOMO_AMPLFUNC'] + for line in env['PYOMO_AMPLFUNC'].split('\n'): + if line not in env['AMPLFUNC']: + env['AMPLFUNC'] += "\n" + line else: env['AMPLFUNC'] = env['PYOMO_AMPLFUNC'] From 53635a9bbb1817ef2d3e4881779736cdabd87345 Mon Sep 17 00:00:00 2001 From: Eslick Date: Tue, 26 Mar 2024 10:56:25 -0400 Subject: [PATCH 2/9] Fix black and substring issue --- pyomo/solvers/plugins/solvers/ASL.py | 3 ++- pyomo/solvers/plugins/solvers/IPOPT.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pyomo/solvers/plugins/solvers/ASL.py b/pyomo/solvers/plugins/solvers/ASL.py index 3ebe5c3b422..38a9fc1df58 100644 --- a/pyomo/solvers/plugins/solvers/ASL.py +++ b/pyomo/solvers/plugins/solvers/ASL.py @@ -160,8 +160,9 @@ def create_command_line(self, executable, problem_files): # if 'PYOMO_AMPLFUNC' in env: if 'AMPLFUNC' in env: + existing = set(env['AMPLFUNC'].split("\n")) for line in env['PYOMO_AMPLFUNC'].split('\n'): - if line not in env['AMPLFUNC']: + if line not in existing: env['AMPLFUNC'] += "\n" + line else: env['AMPLFUNC'] = env['PYOMO_AMPLFUNC'] diff --git a/pyomo/solvers/plugins/solvers/IPOPT.py b/pyomo/solvers/plugins/solvers/IPOPT.py index 17b68da6364..8f5190a4a07 100644 --- a/pyomo/solvers/plugins/solvers/IPOPT.py +++ b/pyomo/solvers/plugins/solvers/IPOPT.py @@ -121,8 +121,9 @@ def create_command_line(self, executable, problem_files): # if 'PYOMO_AMPLFUNC' in env: if 'AMPLFUNC' in env: + existing = set(env['AMPLFUNC'].split("\n")) for line in env['PYOMO_AMPLFUNC'].split('\n'): - if line not in env['AMPLFUNC']: + if line not in existing: env['AMPLFUNC'] += "\n" + line else: env['AMPLFUNC'] = env['PYOMO_AMPLFUNC'] From 6e1d351126e5e3f0b775d678b1778ceb38de5938 Mon Sep 17 00:00:00 2001 From: Eslick Date: Wed, 27 Mar 2024 08:18:07 -0400 Subject: [PATCH 3/9] Add Robbybp's patch --- pyomo/contrib/pynumero/interfaces/pyomo_nlp.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/pyomo/contrib/pynumero/interfaces/pyomo_nlp.py b/pyomo/contrib/pynumero/interfaces/pyomo_nlp.py index 51edd09311a..ce148f50ecf 100644 --- a/pyomo/contrib/pynumero/interfaces/pyomo_nlp.py +++ b/pyomo/contrib/pynumero/interfaces/pyomo_nlp.py @@ -92,15 +92,13 @@ def __init__(self, pyomo_model, nl_file_options=None): # The NL writer advertises the external function libraries # through the PYOMO_AMPLFUNC environment variable; merge it # with any preexisting AMPLFUNC definitions - amplfunc = "\n".join( - filter( - None, - ( - os.environ.get('AMPLFUNC', None), - os.environ.get('PYOMO_AMPLFUNC', None), - ), - ) - ) + amplfunc_lines = os.environ.get("AMPLFUNC", "").split("\n") + existing = set(amplfunc_lines) + for line in os.environ.get("PYOMO_AMPLFUNC", "").split("\n"): + # Skip (a) empty lines and (b) lines we already have + if line != "" and line not in existing: + amplfunc_lines.append(line) + amplfunc = "\n".join(amplfunc_lines) with CtypesEnviron(AMPLFUNC=amplfunc): super(PyomoNLP, self).__init__(nl_file) From ce34115f48d6610cda97e2438f91aae10886209a Mon Sep 17 00:00:00 2001 From: Eslick Date: Thu, 4 Apr 2024 08:32:43 -0400 Subject: [PATCH 4/9] Centralize function to merge AMPLFUNC and PYOMO_AMPLFUNC --- .../contrib/pynumero/interfaces/pyomo_nlp.py | 16 +++++++------- pyomo/solvers/amplfunc_merge.py | 21 +++++++++++++++++++ pyomo/solvers/plugins/solvers/ASL.py | 6 ++---- pyomo/solvers/plugins/solvers/IPOPT.py | 7 +++---- 4 files changed, 35 insertions(+), 15 deletions(-) create mode 100644 pyomo/solvers/amplfunc_merge.py diff --git a/pyomo/contrib/pynumero/interfaces/pyomo_nlp.py b/pyomo/contrib/pynumero/interfaces/pyomo_nlp.py index ce148f50ecf..bfd22ede86b 100644 --- a/pyomo/contrib/pynumero/interfaces/pyomo_nlp.py +++ b/pyomo/contrib/pynumero/interfaces/pyomo_nlp.py @@ -22,6 +22,7 @@ import pyomo.core.base as pyo from pyomo.common.collections import ComponentMap from pyomo.common.env import CtypesEnviron +from pyomo.solvers.amplfunc_merge import amplfunc_merge from ..sparse.block_matrix import BlockMatrix from pyomo.contrib.pynumero.interfaces.ampl_nlp import AslNLP from pyomo.contrib.pynumero.interfaces.nlp import NLP @@ -92,13 +93,14 @@ def __init__(self, pyomo_model, nl_file_options=None): # The NL writer advertises the external function libraries # through the PYOMO_AMPLFUNC environment variable; merge it # with any preexisting AMPLFUNC definitions - amplfunc_lines = os.environ.get("AMPLFUNC", "").split("\n") - existing = set(amplfunc_lines) - for line in os.environ.get("PYOMO_AMPLFUNC", "").split("\n"): - # Skip (a) empty lines and (b) lines we already have - if line != "" and line not in existing: - amplfunc_lines.append(line) - amplfunc = "\n".join(amplfunc_lines) + if 'PYOMO_AMPLFUNC' in os.environ: + if 'AMPLFUNC' in os.environ: + amplfunc = amplfunc_merge( + os.environ['AMPLFUNC'], os.environ['PYOMO_AMPLFUNC'] + ) + else: + amplfunc = os.environ['PYOMO_AMPLFUNC'] + with CtypesEnviron(AMPLFUNC=amplfunc): super(PyomoNLP, self).__init__(nl_file) diff --git a/pyomo/solvers/amplfunc_merge.py b/pyomo/solvers/amplfunc_merge.py new file mode 100644 index 00000000000..72c6587f552 --- /dev/null +++ b/pyomo/solvers/amplfunc_merge.py @@ -0,0 +1,21 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright (c) 2008-2024 +# National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + + +def amplfunc_merge(amplfunc, pyomo_amplfunc): + """Merge two AMPLFUNC variable strings eliminating duplicate lines""" + amplfunc_lines = amplfunc.split("\n") + existing = set(amplfunc_lines) + for line in pyomo_amplfunc.split("\n"): + # Skip lines we already have + if line not in existing: + amplfunc_lines.append(line) + return "\n".join(amplfunc_lines) diff --git a/pyomo/solvers/plugins/solvers/ASL.py b/pyomo/solvers/plugins/solvers/ASL.py index 38a9fc1df58..6d3e08af259 100644 --- a/pyomo/solvers/plugins/solvers/ASL.py +++ b/pyomo/solvers/plugins/solvers/ASL.py @@ -23,6 +23,7 @@ from pyomo.opt.solver import SystemCallSolver from pyomo.core.kernel.block import IBlock from pyomo.solvers.mockmip import MockMIP +from pyomo.solvers.amplfunc_merge import amplfunc_merge from pyomo.core import TransformationFactory import logging @@ -160,10 +161,7 @@ def create_command_line(self, executable, problem_files): # if 'PYOMO_AMPLFUNC' in env: if 'AMPLFUNC' in env: - existing = set(env['AMPLFUNC'].split("\n")) - for line in env['PYOMO_AMPLFUNC'].split('\n'): - if line not in existing: - env['AMPLFUNC'] += "\n" + line + env['AMPLFUNC'] = amplfunc_merge(env['AMPLFUNC'], env['PYOMO_AMPLFUNC']) else: env['AMPLFUNC'] = env['PYOMO_AMPLFUNC'] diff --git a/pyomo/solvers/plugins/solvers/IPOPT.py b/pyomo/solvers/plugins/solvers/IPOPT.py index 8f5190a4a07..a3c6b6beb28 100644 --- a/pyomo/solvers/plugins/solvers/IPOPT.py +++ b/pyomo/solvers/plugins/solvers/IPOPT.py @@ -21,6 +21,8 @@ from pyomo.opt.results import SolverStatus, SolverResults, TerminationCondition from pyomo.opt.solver import SystemCallSolver +from pyomo.solvers.amplfunc_merge import amplfunc_merge + import logging logger = logging.getLogger('pyomo.solvers') @@ -121,10 +123,7 @@ def create_command_line(self, executable, problem_files): # if 'PYOMO_AMPLFUNC' in env: if 'AMPLFUNC' in env: - existing = set(env['AMPLFUNC'].split("\n")) - for line in env['PYOMO_AMPLFUNC'].split('\n'): - if line not in existing: - env['AMPLFUNC'] += "\n" + line + env['AMPLFUNC'] = amplfunc_merge(env['AMPLFUNC'], env['PYOMO_AMPLFUNC']) else: env['AMPLFUNC'] = env['PYOMO_AMPLFUNC'] From 11d11e0672ef963631d9c75fb5946dc683e6592c Mon Sep 17 00:00:00 2001 From: Eslick Date: Thu, 4 Apr 2024 09:29:56 -0400 Subject: [PATCH 5/9] Add tests --- pyomo/solvers/amplfunc_merge.py | 6 ++ .../tests/checks/test_amplfunc_merge.py | 93 +++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 pyomo/solvers/tests/checks/test_amplfunc_merge.py diff --git a/pyomo/solvers/amplfunc_merge.py b/pyomo/solvers/amplfunc_merge.py index 72c6587f552..88babc2f43c 100644 --- a/pyomo/solvers/amplfunc_merge.py +++ b/pyomo/solvers/amplfunc_merge.py @@ -12,10 +12,16 @@ def amplfunc_merge(amplfunc, pyomo_amplfunc): """Merge two AMPLFUNC variable strings eliminating duplicate lines""" + # Assume that the strings amplfunc and pyomo_amplfunc don't contain duplicates + # Assume that the path separator is correct for the OS so we don't need to + # worry about comparing Unix and Windows paths. amplfunc_lines = amplfunc.split("\n") existing = set(amplfunc_lines) for line in pyomo_amplfunc.split("\n"): # Skip lines we already have if line not in existing: amplfunc_lines.append(line) + # Remove empty lines which could happen if one or both of the strings is + # empty or there are two new lines in a row for whatever reason. + amplfunc_lines = [s for s in amplfunc_lines if s != ""] return "\n".join(amplfunc_lines) diff --git a/pyomo/solvers/tests/checks/test_amplfunc_merge.py b/pyomo/solvers/tests/checks/test_amplfunc_merge.py new file mode 100644 index 00000000000..de31720010c --- /dev/null +++ b/pyomo/solvers/tests/checks/test_amplfunc_merge.py @@ -0,0 +1,93 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright (c) 2008-2024 +# National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + +import pyomo.common.unittest as unittest +from pyomo.solvers.amplfunc_merge import amplfunc_merge + + +class TestAMPLFUNCMerge(unittest.TestCase): + def test_merge_no_dup(self): + s1 = "my/place/l1.so\nanother/place/l1.so" + s2 = "my/place/l2.so" + sm = amplfunc_merge(s1, s2) + sm_list = sm.split("\n") + self.assertEqual(len(sm_list), 3) + # The order of lines should be maintained with the second string + # following the first + self.assertEqual(sm_list[0], "my/place/l1.so") + self.assertEqual(sm_list[1], "another/place/l1.so") + self.assertEqual(sm_list[2], "my/place/l2.so") + + def test_merge_empty1(self): + s1 = "" + s2 = "my/place/l2.so" + sm = amplfunc_merge(s1, s2) + sm_list = sm.split("\n") + self.assertEqual(len(sm_list), 1) + self.assertEqual(sm_list[0], "my/place/l2.so") + + def test_merge_empty2(self): + s1 = "my/place/l2.so" + s2 = "" + sm = amplfunc_merge(s1, s2) + sm_list = sm.split("\n") + self.assertEqual(len(sm_list), 1) + self.assertEqual(sm_list[0], "my/place/l2.so") + + def test_merge_empty_both(self): + s1 = "" + s2 = "" + sm = amplfunc_merge(s1, s2) + sm_list = sm.split("\n") + self.assertEqual(len(sm_list), 1) + self.assertEqual(sm_list[0], "") + + def test_merge_bad_type(self): + self.assertRaises(AttributeError, amplfunc_merge, "", 3) + self.assertRaises(AttributeError, amplfunc_merge, 3, "") + self.assertRaises(AttributeError, amplfunc_merge, 3, 3) + self.assertRaises(AttributeError, amplfunc_merge, None, "") + self.assertRaises(AttributeError, amplfunc_merge, "", None) + self.assertRaises(AttributeError, amplfunc_merge, 2.3, "") + self.assertRaises(AttributeError, amplfunc_merge, "", 2.3) + + def test_merge_duplicate1(self): + s1 = "my/place/l1.so\nanother/place/l1.so" + s2 = "my/place/l1.so\nanother/place/l1.so" + sm = amplfunc_merge(s1, s2) + sm_list = sm.split("\n") + self.assertEqual(len(sm_list), 2) + # The order of lines should be maintained with the second string + # following the first + self.assertEqual(sm_list[0], "my/place/l1.so") + self.assertEqual(sm_list[1], "another/place/l1.so") + + def test_merge_duplicate2(self): + s1 = "my/place/l1.so\nanother/place/l1.so" + s2 = "my/place/l1.so" + sm = amplfunc_merge(s1, s2) + sm_list = sm.split("\n") + self.assertEqual(len(sm_list), 2) + # The order of lines should be maintained with the second string + # following the first + self.assertEqual(sm_list[0], "my/place/l1.so") + self.assertEqual(sm_list[1], "another/place/l1.so") + + def test_merge_extra_linebreaks(self): + s1 = "\nmy/place/l1.so\nanother/place/l1.so\n" + s2 = "\nmy/place/l1.so\n\n" + sm = amplfunc_merge(s1, s2) + sm_list = sm.split("\n") + self.assertEqual(len(sm_list), 2) + # The order of lines should be maintained with the second string + # following the first + self.assertEqual(sm_list[0], "my/place/l1.so") + self.assertEqual(sm_list[1], "another/place/l1.so") From 30ffed7a4ee79435bd472f7846c0d95f67768712 Mon Sep 17 00:00:00 2001 From: Eslick Date: Thu, 4 Apr 2024 10:13:25 -0400 Subject: [PATCH 6/9] Fix error in pyomo_nlp missing undefined amplfunc --- .../contrib/pynumero/interfaces/pyomo_nlp.py | 8 +- pyomo/solvers/amplfunc_merge.py | 6 +- pyomo/solvers/plugins/solvers/ASL.py | 8 +- pyomo/solvers/plugins/solvers/IPOPT.py | 8 +- .../tests/checks/test_amplfunc_merge.py | 114 +++++++++++++++--- 5 files changed, 110 insertions(+), 34 deletions(-) diff --git a/pyomo/contrib/pynumero/interfaces/pyomo_nlp.py b/pyomo/contrib/pynumero/interfaces/pyomo_nlp.py index bfd22ede86b..e12d0cf568b 100644 --- a/pyomo/contrib/pynumero/interfaces/pyomo_nlp.py +++ b/pyomo/contrib/pynumero/interfaces/pyomo_nlp.py @@ -93,13 +93,7 @@ def __init__(self, pyomo_model, nl_file_options=None): # The NL writer advertises the external function libraries # through the PYOMO_AMPLFUNC environment variable; merge it # with any preexisting AMPLFUNC definitions - if 'PYOMO_AMPLFUNC' in os.environ: - if 'AMPLFUNC' in os.environ: - amplfunc = amplfunc_merge( - os.environ['AMPLFUNC'], os.environ['PYOMO_AMPLFUNC'] - ) - else: - amplfunc = os.environ['PYOMO_AMPLFUNC'] + amplfunc = amplfunc_merge(os.environ) with CtypesEnviron(AMPLFUNC=amplfunc): super(PyomoNLP, self).__init__(nl_file) diff --git a/pyomo/solvers/amplfunc_merge.py b/pyomo/solvers/amplfunc_merge.py index 88babc2f43c..4c77f080ca1 100644 --- a/pyomo/solvers/amplfunc_merge.py +++ b/pyomo/solvers/amplfunc_merge.py @@ -10,7 +10,7 @@ # ___________________________________________________________________________ -def amplfunc_merge(amplfunc, pyomo_amplfunc): +def amplfunc_string_merge(amplfunc, pyomo_amplfunc): """Merge two AMPLFUNC variable strings eliminating duplicate lines""" # Assume that the strings amplfunc and pyomo_amplfunc don't contain duplicates # Assume that the path separator is correct for the OS so we don't need to @@ -25,3 +25,7 @@ def amplfunc_merge(amplfunc, pyomo_amplfunc): # empty or there are two new lines in a row for whatever reason. amplfunc_lines = [s for s in amplfunc_lines if s != ""] return "\n".join(amplfunc_lines) + +def amplfunc_merge(env): + """Merge AMPLFUNC and PYOMO_AMPLFuNC in an environment var dict""" + return amplfunc_string_merge(env.get("AMPLFUNC", ""), env.get("PYOMO_AMPLFUNC", "")) \ No newline at end of file diff --git a/pyomo/solvers/plugins/solvers/ASL.py b/pyomo/solvers/plugins/solvers/ASL.py index 6d3e08af259..bb8174a013e 100644 --- a/pyomo/solvers/plugins/solvers/ASL.py +++ b/pyomo/solvers/plugins/solvers/ASL.py @@ -159,11 +159,9 @@ def create_command_line(self, executable, problem_files): # Pyomo/Pyomo) with any user-specified external function # libraries # - if 'PYOMO_AMPLFUNC' in env: - if 'AMPLFUNC' in env: - env['AMPLFUNC'] = amplfunc_merge(env['AMPLFUNC'], env['PYOMO_AMPLFUNC']) - else: - env['AMPLFUNC'] = env['PYOMO_AMPLFUNC'] + amplfunc = amplfunc_merge(env) + if amplfunc: + env['AMPLFUNC'] = amplfunc cmd = [executable, problem_files[0], '-AMPL'] if self._timer: diff --git a/pyomo/solvers/plugins/solvers/IPOPT.py b/pyomo/solvers/plugins/solvers/IPOPT.py index a3c6b6beb28..21045cb7b4f 100644 --- a/pyomo/solvers/plugins/solvers/IPOPT.py +++ b/pyomo/solvers/plugins/solvers/IPOPT.py @@ -121,11 +121,9 @@ def create_command_line(self, executable, problem_files): # Pyomo/Pyomo) with any user-specified external function # libraries # - if 'PYOMO_AMPLFUNC' in env: - if 'AMPLFUNC' in env: - env['AMPLFUNC'] = amplfunc_merge(env['AMPLFUNC'], env['PYOMO_AMPLFUNC']) - else: - env['AMPLFUNC'] = env['PYOMO_AMPLFUNC'] + amplfunc = amplfunc_merge(env) + if amplfunc: + env['AMPLFUNC'] = amplfunc cmd = [executable, problem_files[0], '-AMPL'] if self._timer: diff --git a/pyomo/solvers/tests/checks/test_amplfunc_merge.py b/pyomo/solvers/tests/checks/test_amplfunc_merge.py index de31720010c..fb7701e9282 100644 --- a/pyomo/solvers/tests/checks/test_amplfunc_merge.py +++ b/pyomo/solvers/tests/checks/test_amplfunc_merge.py @@ -10,14 +10,14 @@ # ___________________________________________________________________________ import pyomo.common.unittest as unittest -from pyomo.solvers.amplfunc_merge import amplfunc_merge +from pyomo.solvers.amplfunc_merge import amplfunc_string_merge, amplfunc_merge -class TestAMPLFUNCMerge(unittest.TestCase): +class TestAMPLFUNCStringMerge(unittest.TestCase): def test_merge_no_dup(self): s1 = "my/place/l1.so\nanother/place/l1.so" s2 = "my/place/l2.so" - sm = amplfunc_merge(s1, s2) + sm = amplfunc_string_merge(s1, s2) sm_list = sm.split("\n") self.assertEqual(len(sm_list), 3) # The order of lines should be maintained with the second string @@ -29,7 +29,7 @@ def test_merge_no_dup(self): def test_merge_empty1(self): s1 = "" s2 = "my/place/l2.so" - sm = amplfunc_merge(s1, s2) + sm = amplfunc_string_merge(s1, s2) sm_list = sm.split("\n") self.assertEqual(len(sm_list), 1) self.assertEqual(sm_list[0], "my/place/l2.so") @@ -37,7 +37,7 @@ def test_merge_empty1(self): def test_merge_empty2(self): s1 = "my/place/l2.so" s2 = "" - sm = amplfunc_merge(s1, s2) + sm = amplfunc_string_merge(s1, s2) sm_list = sm.split("\n") self.assertEqual(len(sm_list), 1) self.assertEqual(sm_list[0], "my/place/l2.so") @@ -45,24 +45,24 @@ def test_merge_empty2(self): def test_merge_empty_both(self): s1 = "" s2 = "" - sm = amplfunc_merge(s1, s2) + sm = amplfunc_string_merge(s1, s2) sm_list = sm.split("\n") self.assertEqual(len(sm_list), 1) self.assertEqual(sm_list[0], "") def test_merge_bad_type(self): - self.assertRaises(AttributeError, amplfunc_merge, "", 3) - self.assertRaises(AttributeError, amplfunc_merge, 3, "") - self.assertRaises(AttributeError, amplfunc_merge, 3, 3) - self.assertRaises(AttributeError, amplfunc_merge, None, "") - self.assertRaises(AttributeError, amplfunc_merge, "", None) - self.assertRaises(AttributeError, amplfunc_merge, 2.3, "") - self.assertRaises(AttributeError, amplfunc_merge, "", 2.3) + self.assertRaises(AttributeError, amplfunc_string_merge, "", 3) + self.assertRaises(AttributeError, amplfunc_string_merge, 3, "") + self.assertRaises(AttributeError, amplfunc_string_merge, 3, 3) + self.assertRaises(AttributeError, amplfunc_string_merge, None, "") + self.assertRaises(AttributeError, amplfunc_string_merge, "", None) + self.assertRaises(AttributeError, amplfunc_string_merge, 2.3, "") + self.assertRaises(AttributeError, amplfunc_string_merge, "", 2.3) def test_merge_duplicate1(self): s1 = "my/place/l1.so\nanother/place/l1.so" s2 = "my/place/l1.so\nanother/place/l1.so" - sm = amplfunc_merge(s1, s2) + sm = amplfunc_string_merge(s1, s2) sm_list = sm.split("\n") self.assertEqual(len(sm_list), 2) # The order of lines should be maintained with the second string @@ -73,7 +73,7 @@ def test_merge_duplicate1(self): def test_merge_duplicate2(self): s1 = "my/place/l1.so\nanother/place/l1.so" s2 = "my/place/l1.so" - sm = amplfunc_merge(s1, s2) + sm = amplfunc_string_merge(s1, s2) sm_list = sm.split("\n") self.assertEqual(len(sm_list), 2) # The order of lines should be maintained with the second string @@ -84,10 +84,92 @@ def test_merge_duplicate2(self): def test_merge_extra_linebreaks(self): s1 = "\nmy/place/l1.so\nanother/place/l1.so\n" s2 = "\nmy/place/l1.so\n\n" - sm = amplfunc_merge(s1, s2) + sm = amplfunc_string_merge(s1, s2) sm_list = sm.split("\n") self.assertEqual(len(sm_list), 2) # The order of lines should be maintained with the second string # following the first self.assertEqual(sm_list[0], "my/place/l1.so") self.assertEqual(sm_list[1], "another/place/l1.so") + +class TestAMPLFUNCMerge(unittest.TestCase): + def test_merge_no_dup(self): + env = { + "AMPLFUNC": "my/place/l1.so\nanother/place/l1.so", + "PYOMO_AMPLFUNC": "my/place/l2.so", + } + sm = amplfunc_merge(env) + sm_list = sm.split("\n") + self.assertEqual(len(sm_list), 3) + self.assertEqual(sm_list[0], "my/place/l1.so") + self.assertEqual(sm_list[1], "another/place/l1.so") + self.assertEqual(sm_list[2], "my/place/l2.so") + + def test_merge_empty1(self): + env = { + "AMPLFUNC": "", + "PYOMO_AMPLFUNC": "my/place/l2.so", + } + sm = amplfunc_merge(env) + sm_list = sm.split("\n") + self.assertEqual(len(sm_list), 1) + self.assertEqual(sm_list[0], "my/place/l2.so") + + def test_merge_empty2(self): + env = { + "AMPLFUNC": "my/place/l2.so", + "PYOMO_AMPLFUNC": "", + } + sm = amplfunc_merge(env) + sm_list = sm.split("\n") + self.assertEqual(len(sm_list), 1) + self.assertEqual(sm_list[0], "my/place/l2.so") + + def test_merge_empty_both(self): + env = { + "AMPLFUNC": "", + "PYOMO_AMPLFUNC": "", + } + sm = amplfunc_merge(env) + sm_list = sm.split("\n") + self.assertEqual(len(sm_list), 1) + self.assertEqual(sm_list[0], "") + + def test_merge_duplicate1(self): + env = { + "AMPLFUNC": "my/place/l1.so\nanother/place/l1.so", + "PYOMO_AMPLFUNC": "my/place/l1.so\nanother/place/l1.so", + } + sm = amplfunc_merge(env) + sm_list = sm.split("\n") + self.assertEqual(len(sm_list), 2) + self.assertEqual(sm_list[0], "my/place/l1.so") + self.assertEqual(sm_list[1], "another/place/l1.so") + + def test_merge_no_pyomo(self): + env = { + "AMPLFUNC": "my/place/l1.so\nanother/place/l1.so", + } + sm = amplfunc_merge(env) + sm_list = sm.split("\n") + self.assertEqual(len(sm_list), 2) + self.assertEqual(sm_list[0], "my/place/l1.so") + self.assertEqual(sm_list[1], "another/place/l1.so") + + def test_merge_no_user(self): + env = { + "PYOMO_AMPLFUNC": "my/place/l1.so\nanother/place/l1.so", + } + sm = amplfunc_merge(env) + sm_list = sm.split("\n") + self.assertEqual(len(sm_list), 2) + self.assertEqual(sm_list[0], "my/place/l1.so") + self.assertEqual(sm_list[1], "another/place/l1.so") + + def test_merge_nothing(self): + env = {} + sm = amplfunc_merge(env) + sm_list = sm.split("\n") + self.assertEqual(len(sm_list), 1) + self.assertEqual(sm_list[0], "") + From d4c9ddf35c28dbd2ce20f81e8fff485d3fec13b5 Mon Sep 17 00:00:00 2001 From: Eslick Date: Thu, 4 Apr 2024 10:20:44 -0400 Subject: [PATCH 7/9] Run black --- pyomo/solvers/amplfunc_merge.py | 3 ++- pyomo/solvers/tests/checks/test_amplfunc_merge.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pyomo/solvers/amplfunc_merge.py b/pyomo/solvers/amplfunc_merge.py index 4c77f080ca1..9d127a94396 100644 --- a/pyomo/solvers/amplfunc_merge.py +++ b/pyomo/solvers/amplfunc_merge.py @@ -26,6 +26,7 @@ def amplfunc_string_merge(amplfunc, pyomo_amplfunc): amplfunc_lines = [s for s in amplfunc_lines if s != ""] return "\n".join(amplfunc_lines) + def amplfunc_merge(env): """Merge AMPLFUNC and PYOMO_AMPLFuNC in an environment var dict""" - return amplfunc_string_merge(env.get("AMPLFUNC", ""), env.get("PYOMO_AMPLFUNC", "")) \ No newline at end of file + return amplfunc_string_merge(env.get("AMPLFUNC", ""), env.get("PYOMO_AMPLFUNC", "")) diff --git a/pyomo/solvers/tests/checks/test_amplfunc_merge.py b/pyomo/solvers/tests/checks/test_amplfunc_merge.py index fb7701e9282..00885feb5a4 100644 --- a/pyomo/solvers/tests/checks/test_amplfunc_merge.py +++ b/pyomo/solvers/tests/checks/test_amplfunc_merge.py @@ -92,6 +92,7 @@ def test_merge_extra_linebreaks(self): self.assertEqual(sm_list[0], "my/place/l1.so") self.assertEqual(sm_list[1], "another/place/l1.so") + class TestAMPLFUNCMerge(unittest.TestCase): def test_merge_no_dup(self): env = { @@ -172,4 +173,3 @@ def test_merge_nothing(self): sm_list = sm.split("\n") self.assertEqual(len(sm_list), 1) self.assertEqual(sm_list[0], "") - From 4849ec515cee9d7ea1fd48747dc5f087a56752cb Mon Sep 17 00:00:00 2001 From: Eslick Date: Thu, 4 Apr 2024 10:30:58 -0400 Subject: [PATCH 8/9] Run black again --- .../tests/checks/test_amplfunc_merge.py | 23 ++++--------------- 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/pyomo/solvers/tests/checks/test_amplfunc_merge.py b/pyomo/solvers/tests/checks/test_amplfunc_merge.py index 00885feb5a4..2c819404d2f 100644 --- a/pyomo/solvers/tests/checks/test_amplfunc_merge.py +++ b/pyomo/solvers/tests/checks/test_amplfunc_merge.py @@ -107,30 +107,21 @@ def test_merge_no_dup(self): self.assertEqual(sm_list[2], "my/place/l2.so") def test_merge_empty1(self): - env = { - "AMPLFUNC": "", - "PYOMO_AMPLFUNC": "my/place/l2.so", - } + env = {"AMPLFUNC": "", "PYOMO_AMPLFUNC": "my/place/l2.so"} sm = amplfunc_merge(env) sm_list = sm.split("\n") self.assertEqual(len(sm_list), 1) self.assertEqual(sm_list[0], "my/place/l2.so") def test_merge_empty2(self): - env = { - "AMPLFUNC": "my/place/l2.so", - "PYOMO_AMPLFUNC": "", - } + env = {"AMPLFUNC": "my/place/l2.so", "PYOMO_AMPLFUNC": ""} sm = amplfunc_merge(env) sm_list = sm.split("\n") self.assertEqual(len(sm_list), 1) self.assertEqual(sm_list[0], "my/place/l2.so") def test_merge_empty_both(self): - env = { - "AMPLFUNC": "", - "PYOMO_AMPLFUNC": "", - } + env = {"AMPLFUNC": "", "PYOMO_AMPLFUNC": ""} sm = amplfunc_merge(env) sm_list = sm.split("\n") self.assertEqual(len(sm_list), 1) @@ -148,9 +139,7 @@ def test_merge_duplicate1(self): self.assertEqual(sm_list[1], "another/place/l1.so") def test_merge_no_pyomo(self): - env = { - "AMPLFUNC": "my/place/l1.so\nanother/place/l1.so", - } + env = {"AMPLFUNC": "my/place/l1.so\nanother/place/l1.so"} sm = amplfunc_merge(env) sm_list = sm.split("\n") self.assertEqual(len(sm_list), 2) @@ -158,9 +147,7 @@ def test_merge_no_pyomo(self): self.assertEqual(sm_list[1], "another/place/l1.so") def test_merge_no_user(self): - env = { - "PYOMO_AMPLFUNC": "my/place/l1.so\nanother/place/l1.so", - } + env = {"PYOMO_AMPLFUNC": "my/place/l1.so\nanother/place/l1.so"} sm = amplfunc_merge(env) sm_list = sm.split("\n") self.assertEqual(len(sm_list), 2) From 942df952e5c79e9ddffe53a2fe98c36fce96132b Mon Sep 17 00:00:00 2001 From: Bethany Nicholson Date: Thu, 4 Apr 2024 13:40:45 -0600 Subject: [PATCH 9/9] NFC: Typo fix in amplfunc_merge.py --- pyomo/solvers/amplfunc_merge.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyomo/solvers/amplfunc_merge.py b/pyomo/solvers/amplfunc_merge.py index 9d127a94396..e49fd20e20f 100644 --- a/pyomo/solvers/amplfunc_merge.py +++ b/pyomo/solvers/amplfunc_merge.py @@ -28,5 +28,5 @@ def amplfunc_string_merge(amplfunc, pyomo_amplfunc): def amplfunc_merge(env): - """Merge AMPLFUNC and PYOMO_AMPLFuNC in an environment var dict""" + """Merge AMPLFUNC and PYOMO_AMPLFUNC in an environment var dict""" return amplfunc_string_merge(env.get("AMPLFUNC", ""), env.get("PYOMO_AMPLFUNC", ""))