From 7102497c843e0687c4817b78f3939b1e1fff18d1 Mon Sep 17 00:00:00 2001 From: Miranda Mundt Date: Mon, 13 Feb 2023 08:22:55 -0700 Subject: [PATCH 1/2] Apply black to pyomo/solvers directory py files --- pyomo/solvers/__init__.py | 1 - pyomo/solvers/mockmip.py | 21 +- pyomo/solvers/plugins/__init__.py | 1 + pyomo/solvers/plugins/converter/ampl.py | 35 +- pyomo/solvers/plugins/converter/glpsol.py | 64 +- pyomo/solvers/plugins/converter/model.py | 167 ++--- pyomo/solvers/plugins/converter/pico.py | 49 +- pyomo/solvers/plugins/solvers/ASL.py | 101 ++- pyomo/solvers/plugins/solvers/BARON.py | 219 +++---- pyomo/solvers/plugins/solvers/CBCplugin.py | 605 ++++++++++++------ pyomo/solvers/plugins/solvers/CONOPT.py | 51 +- pyomo/solvers/plugins/solvers/CPLEX.py | 557 +++++++++------- pyomo/solvers/plugins/solvers/GAMS.py | 423 +++++++----- pyomo/solvers/plugins/solvers/GLPK.py | 192 +++--- pyomo/solvers/plugins/solvers/GUROBI.py | 209 +++--- pyomo/solvers/plugins/solvers/GUROBI_RUN.py | 96 +-- pyomo/solvers/plugins/solvers/IPOPT.py | 68 +- pyomo/solvers/plugins/solvers/SCIPAMPL.py | 432 ++++++------- pyomo/solvers/plugins/solvers/XPRESS.py | 9 +- pyomo/solvers/plugins/solvers/cplex_direct.py | 296 ++++++--- .../plugins/solvers/cplex_persistent.py | 26 +- .../solvers/direct_or_persistent_solver.py | 111 ++-- .../solvers/plugins/solvers/direct_solver.py | 80 ++- .../solvers/plugins/solvers/gurobi_direct.py | 322 +++++++--- .../plugins/solvers/gurobi_persistent.py | 141 ++-- pyomo/solvers/plugins/solvers/mosek_direct.py | 483 ++++++++------ .../plugins/solvers/mosek_persistent.py | 85 +-- .../plugins/solvers/persistent_solver.py | 172 +++-- pyomo/solvers/plugins/solvers/pywrapper.py | 12 +- .../solvers/plugins/solvers/xpress_direct.py | 305 ++++++--- .../plugins/solvers/xpress_persistent.py | 37 +- pyomo/solvers/tests/checks/test_BARON.py | 54 +- pyomo/solvers/tests/checks/test_CBCplugin.py | 207 ++++-- .../solvers/tests/checks/test_CPLEXDirect.py | 141 ++-- .../tests/checks/test_CPLEXPersistent.py | 13 +- pyomo/solvers/tests/checks/test_GAMS.py | 278 ++++---- .../solvers/tests/checks/test_MOSEKDirect.py | 153 ++--- .../tests/checks/test_MOSEKPersistent.py | 25 +- pyomo/solvers/tests/checks/test_cbc.py | 38 +- pyomo/solvers/tests/checks/test_cplex.py | 97 ++- pyomo/solvers/tests/checks/test_gurobi.py | 13 +- .../tests/checks/test_gurobi_persistent.py | 20 +- .../tests/checks/test_no_solution_behavior.py | 27 +- pyomo/solvers/tests/checks/test_pickle.py | 98 +-- pyomo/solvers/tests/checks/test_writers.py | 73 ++- .../tests/checks/test_xpress_persistent.py | 27 +- pyomo/solvers/tests/mip/model.py | 5 +- pyomo/solvers/tests/mip/test_asl.py | 72 ++- pyomo/solvers/tests/mip/test_convert.py | 251 +++++--- pyomo/solvers/tests/mip/test_factory.py | 53 +- pyomo/solvers/tests/mip/test_ipopt.py | 123 ++-- pyomo/solvers/tests/mip/test_mip.py | 3 +- pyomo/solvers/tests/mip/test_scip.py | 34 +- pyomo/solvers/tests/mip/test_scip_log_data.py | 324 +++++----- pyomo/solvers/tests/mip/test_scip_version.py | 52 +- pyomo/solvers/tests/mip/test_solver.py | 5 +- pyomo/solvers/tests/models/LP_block.py | 35 +- pyomo/solvers/tests/models/LP_compiled.py | 201 +++--- .../tests/models/LP_constant_objective1.py | 8 +- .../tests/models/LP_constant_objective2.py | 9 +- .../solvers/tests/models/LP_duals_maximize.py | 84 +-- .../solvers/tests/models/LP_duals_minimize.py | 77 ++- .../solvers/tests/models/LP_inactive_index.py | 63 +- pyomo/solvers/tests/models/LP_infeasible1.py | 29 +- pyomo/solvers/tests/models/LP_infeasible2.py | 29 +- pyomo/solvers/tests/models/LP_piecewise.py | 19 +- pyomo/solvers/tests/models/LP_simple.py | 39 +- .../tests/models/LP_trivial_constraints.py | 19 +- pyomo/solvers/tests/models/LP_unbounded.py | 26 +- pyomo/solvers/tests/models/LP_unique_duals.py | 35 +- pyomo/solvers/tests/models/LP_unused_vars.py | 71 +- .../tests/models/MILP_discrete_var_bounds.py | 35 +- .../solvers/tests/models/MILP_infeasible1.py | 37 +- pyomo/solvers/tests/models/MILP_simple.py | 23 +- pyomo/solvers/tests/models/MILP_unbounded.py | 26 +- .../solvers/tests/models/MILP_unused_vars.py | 77 ++- pyomo/solvers/tests/models/MIQCP_simple.py | 17 +- pyomo/solvers/tests/models/MIQP_simple.py | 37 +- pyomo/solvers/tests/models/QCP_simple.py | 53 +- .../tests/models/QP_constant_objective.py | 9 +- pyomo/solvers/tests/models/QP_simple.py | 38 +- pyomo/solvers/tests/models/SOS1_simple.py | 28 +- pyomo/solvers/tests/models/SOS2_simple.py | 56 +- pyomo/solvers/tests/models/__init__.py | 3 +- pyomo/solvers/tests/models/base.py | 389 ++++++----- .../kernel_problems/concave_var.py | 25 +- .../kernel_problems/convex_var.py | 25 +- .../kernel_problems/piecewise_var.py | 25 +- .../kernel_problems/step_var.py | 31 +- .../problems/concave_multi_vararray1.py | 75 ++- .../problems/concave_multi_vararray2.py | 72 ++- .../piecewise_linear/problems/concave_var.py | 107 ++-- .../problems/concave_vararray.py | 60 +- .../problems/convex_multi_vararray1.py | 74 ++- .../problems/convex_multi_vararray2.py | 76 ++- .../piecewise_linear/problems/convex_var.py | 107 ++-- .../problems/convex_vararray.py | 57 +- .../problems/piecewise_multi_vararray.py | 61 +- .../problems/piecewise_var.py | 87 +-- .../problems/piecewise_vararray.py | 47 +- .../piecewise_linear/problems/step_var.py | 85 ++- .../problems/step_vararray.py | 64 +- .../tests/piecewise_linear/problems/tester.py | 6 +- .../tests/piecewise_linear/test_examples.py | 86 +-- .../piecewise_linear/test_piecewise_linear.py | 183 ++++-- .../test_piecewise_linear_kernel.py | 133 ++-- pyomo/solvers/tests/solvers.py | 249 ++++--- pyomo/solvers/tests/testcases.py | 351 ++++++---- pyomo/solvers/wrappers.py | 6 +- 109 files changed, 6686 insertions(+), 4534 deletions(-) diff --git a/pyomo/solvers/__init__.py b/pyomo/solvers/__init__.py index 9320e403e95..d93cfd77b3c 100644 --- a/pyomo/solvers/__init__.py +++ b/pyomo/solvers/__init__.py @@ -8,4 +8,3 @@ # rights in this software. # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ - diff --git a/pyomo/solvers/mockmip.py b/pyomo/solvers/mockmip.py index 8b1289e546e..9497a6dff9d 100644 --- a/pyomo/solvers/mockmip.py +++ b/pyomo/solvers/mockmip.py @@ -16,25 +16,26 @@ from pyomo.opt.base.solvers import _extract_version + class MockMIP(object): - """Methods used to create a mock MIP solver used for testing - """ + """Methods used to create a mock MIP solver used for testing""" def __init__(self, mockdir): - self.mock_subdir=mockdir + self.mock_subdir = mockdir - def create_command_line(self,executable,problem_files): + def create_command_line(self, executable, problem_files): self._mock_problem = basename(problem_files[0]).split('.')[0] self._mock_dir = dirname(problem_files[0]) def _default_executable(self): return "mock" + executable = _default_executable def version(self): return _extract_version('') - def _execute_command(self,cmd): + def _execute_command(self, cmd): mock_basename = join(self._mock_dir, self.mock_subdir, self._mock_problem) if self._soln_file is not None: # prefer .sol over .soln @@ -48,12 +49,12 @@ def _execute_command(self,cmd): for file in glob.glob(mock_basename + "*"): if file.split(".")[-1] != "out": shutil.copyfile(file, join(self._mock_dir, basename(file))) - log="" + log = "" fname = mock_basename + ".out" if not isfile(fname): - raise ValueError("Missing mock data file: "+fname) - INPUT=open(mock_basename + ".out") + raise ValueError("Missing mock data file: " + fname) + INPUT = open(mock_basename + ".out") for line in INPUT: - log = log+line + log = log + line INPUT.close() - return [0,log] + return [0, log] diff --git a/pyomo/solvers/plugins/__init__.py b/pyomo/solvers/plugins/__init__.py index bb071a7d4ee..797ed5036bd 100644 --- a/pyomo/solvers/plugins/__init__.py +++ b/pyomo/solvers/plugins/__init__.py @@ -9,6 +9,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ + def load(): import pyomo.solvers.plugins.converter import pyomo.solvers.plugins.solvers diff --git a/pyomo/solvers/plugins/converter/ampl.py b/pyomo/solvers/plugins/converter/ampl.py index 5d346c704f0..b718faf2d21 100644 --- a/pyomo/solvers/plugins/converter/ampl.py +++ b/pyomo/solvers/plugins/converter/ampl.py @@ -21,7 +21,6 @@ @ProblemConverterFactory.register('ampl') class AmplMIPConverter(object): - def can_convert(self, from_type, to_type): """Returns true if this object supports the specified conversion""" # @@ -45,12 +44,12 @@ def apply(self, *args, **kwargs): _exec = pyomo.common.Executable("ampl") if not _exec: raise ConverterError("The 'ampl' executable cannot be found") - script_filename = TempfileManager.create_tempfile(suffix = '.ampl') + script_filename = TempfileManager.create_tempfile(suffix='.ampl') if args[1] == ProblemFormat.nl: - output_filename = TempfileManager.create_tempfile(suffix = '.nl') + output_filename = TempfileManager.create_tempfile(suffix='.nl') else: - output_filename = TempfileManager.create_tempfile(suffix = '.mps') + output_filename = TempfileManager.create_tempfile(suffix='.mps') cmd = [_exec.path(), script_filename] # @@ -61,22 +60,28 @@ def apply(self, *args, **kwargs): OUTPUT.write("# AMPL script for converting the following files\n") OUTPUT.write("#\n") if len(args[2:]) == 1: - OUTPUT.write('model '+args[2]+";\n") + OUTPUT.write('model ' + args[2] + ";\n") else: - OUTPUT.write('model '+args[2]+";\n") - OUTPUT.write('data '+args[3]+";\n") + OUTPUT.write('model ' + args[2] + ";\n") + OUTPUT.write('data ' + args[3] + ";\n") abs_ofile = os.path.abspath(output_filename) if args[1] == ProblemFormat.nl: - OUTPUT.write('write g'+abs_ofile[:-3]+";\n") + OUTPUT.write('write g' + abs_ofile[:-3] + ";\n") else: - OUTPUT.write('write m'+abs_ofile[:-4]+";\n") + OUTPUT.write('write m' + abs_ofile[:-4] + ";\n") OUTPUT.close() # # Execute command and cleanup # - output = subprocess.run(cmd, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - universal_newlines=True) - if not os.path.exists(output_filename): #pragma:nocover - raise ApplicationError("Problem launching 'ampl' to create '%s': %s" % (output_filename, output.stdout)) - return (output_filename,),None # empty variable map + output = subprocess.run( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True, + ) + if not os.path.exists(output_filename): # pragma:nocover + raise ApplicationError( + "Problem launching 'ampl' to create '%s': %s" + % (output_filename, output.stdout) + ) + return (output_filename,), None # empty variable map diff --git a/pyomo/solvers/plugins/converter/glpsol.py b/pyomo/solvers/plugins/converter/glpsol.py index 841c75828f0..a38892e3cf5 100644 --- a/pyomo/solvers/plugins/converter/glpsol.py +++ b/pyomo/solvers/plugins/converter/glpsol.py @@ -21,7 +21,6 @@ @ProblemConverterFactory.register('glpsol') class GlpsolMIPConverter(object): - def can_convert(self, from_type, to_type): """Returns true if this object supports the specified conversion""" # @@ -54,21 +53,29 @@ def apply(self, *args, **kwargs): # MPS->LP conversion is ignored in coverage because it's not being # used; instead, we're using pico_convert for this conversion # - modfile='' - if args[1] == ProblemFormat.mps: #pragma:nocover - ofile = TempfileManager.create_tempfile(suffix = '.glpsol.mps') - cmd.extend([ - "--check", - "--name", "MPS model derived from "+os.path.basename(args[2]), - "--wfreemps", ofile - ]) + modfile = '' + if args[1] == ProblemFormat.mps: # pragma:nocover + ofile = TempfileManager.create_tempfile(suffix='.glpsol.mps') + cmd.extend( + [ + "--check", + "--name", + "MPS model derived from " + os.path.basename(args[2]), + "--wfreemps", + ofile, + ] + ) elif args[1] == ProblemFormat.cpxlp: - ofile = TempfileManager.create_tempfile(suffix = '.glpsol.lp') - cmd.extend([ - "--check", - "--name","MPS model derived from "+os.path.basename(args[2]), - "--wcpxlp", ofile - ]) + ofile = TempfileManager.create_tempfile(suffix='.glpsol.lp') + cmd.extend( + [ + "--check", + "--name", + "MPS model derived from " + os.path.basename(args[2]), + "--wcpxlp", + ofile, + ] + ) if len(args[2:]) == 1: cmd.append(args[2]) else: @@ -76,38 +83,39 @@ def apply(self, *args, **kwargs): # Create a temporary model file, since GLPSOL can only # handle one input file # - modfile = TempfileManager.create_tempfile(suffix = '.glpsol.mod') - OUTPUT=open(modfile,"w") - flag=False + modfile = TempfileManager.create_tempfile(suffix='.glpsol.mod') + OUTPUT = open(modfile, "w") + flag = False # # Read the model file # - INPUT= open(args[2]) + INPUT = open(args[2]) for line in INPUT: line = line.strip() if line == "data;": - raise ConverterError("Problem composing mathprog model and data files - mathprog file already has data in it!") + raise ConverterError( + "Problem composing mathprog model and data files - mathprog file already has data in it!" + ) if line != "end;": - OUTPUT.write(line+'\n') + OUTPUT.write(line + '\n') INPUT.close() OUTPUT.write("data;\n") # # Read the data files # for file in args[3:]: - INPUT= open(file) + INPUT = open(file) for line in INPUT: line = line.strip() if line != "end;" and line != "data;": - OUTPUT.write(line+'\n') + OUTPUT.write(line + '\n') INPUT.close() OUTPUT.write("end;\n") OUTPUT.close() cmd.append(modfile) - subprocess.run(cmd, stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL) - if not os.path.exists(ofile): #pragma:nocover - raise ApplicationError("Problem launching 'glpsol' to create "+ofile) + subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + if not os.path.exists(ofile): # pragma:nocover + raise ApplicationError("Problem launching 'glpsol' to create " + ofile) if os.path.exists(modfile): os.remove(modfile) - return (ofile,),None # empty variable map + return (ofile,), None # empty variable map diff --git a/pyomo/solvers/plugins/converter/model.py b/pyomo/solvers/plugins/converter/model.py index 9c8dba7d0a0..8a3ea2ae5a5 100644 --- a/pyomo/solvers/plugins/converter/model.py +++ b/pyomo/solvers/plugins/converter/model.py @@ -31,11 +31,13 @@ def can_convert(self, from_type, to_type): # # Return True for specific from/to pairs # - if to_type in (ProblemFormat.nl, - ProblemFormat.cpxlp, - ProblemFormat.osil, - ProblemFormat.bar, - ProblemFormat.mps): + if to_type in ( + ProblemFormat.nl, + ProblemFormat.cpxlp, + ProblemFormat.osil, + ProblemFormat.bar, + ProblemFormat.mps, + ): return True return False @@ -63,8 +65,7 @@ def apply(self, *args, **kwds): instance = args[2] if args[1] == ProblemFormat.cpxlp: - problem_filename = TempfileManager.\ - create_tempfile(suffix = '.pyomo.lp') + problem_filename = TempfileManager.create_tempfile(suffix='.pyomo.lp') if instance is not None: if isinstance(instance, IBlock): symbol_map_id = instance.write( @@ -72,14 +73,15 @@ def apply(self, *args, **kwds): format=ProblemFormat.cpxlp, _solver_capability=capabilities, _called_by_solver=True, - **io_options) + **io_options + ) else: - (problem_filename, symbol_map_id) = \ - instance.write( - filename=problem_filename, - format=ProblemFormat.cpxlp, - solver_capability=capabilities, - io_options=io_options) + (problem_filename, symbol_map_id) = instance.write( + filename=problem_filename, + format=ProblemFormat.cpxlp, + solver_capability=capabilities, + io_options=io_options, + ) return (problem_filename,), symbol_map_id else: @@ -92,25 +94,27 @@ def apply(self, *args, **kwds): if len(io_options): raise ValueError( "The following io_options will be ignored " - "(please create a bug report):\n\t" + - "\n\t".join("%s = %s" % (k,v) - for k,v in io_options.items())) + "(please create a bug report):\n\t" + + "\n\t".join("%s = %s" % (k, v) for k, v in io_options.items()) + ) - ans = pyomo.scripting.convert.\ - pyomo2lp(['--output',problem_filename,args[2]]) + ans = pyomo.scripting.convert.pyomo2lp( + ['--output', problem_filename, args[2]] + ) if ans.errorcode: - raise RuntimeError("pyomo2lp conversion " - "returned nonzero error code " - "(%s)" % ans.errorcode) + raise RuntimeError( + "pyomo2lp conversion " + "returned nonzero error code " + "(%s)" % ans.errorcode + ) model = ans.retval problem_filename = model.filename symbol_map = model.symbol_map - return (problem_filename,),symbol_map + return (problem_filename,), symbol_map elif args[1] == ProblemFormat.bar: - problem_filename = TempfileManager.\ - create_tempfile(suffix = '.pyomo.bar') + problem_filename = TempfileManager.create_tempfile(suffix='.pyomo.bar') if instance is not None: if isinstance(instance, IBlock): symbol_map_id = instance.write( @@ -118,14 +122,15 @@ def apply(self, *args, **kwds): format=ProblemFormat.bar, _solver_capability=capabilities, _called_by_solver=True, - **io_options) + **io_options + ) else: - (problem_filename, symbol_map_id) = \ - instance.write( - filename=problem_filename, - format=ProblemFormat.bar, - solver_capability=capabilities, - io_options=io_options) + (problem_filename, symbol_map_id) = instance.write( + filename=problem_filename, + format=ProblemFormat.bar, + solver_capability=capabilities, + io_options=io_options, + ) return (problem_filename,), symbol_map_id else: @@ -138,36 +143,37 @@ def apply(self, *args, **kwds): if len(io_options): raise ValueError( "The following io_options will be ignored " - "(please create a bug report):\n\t" + - "\n\t".join("%s = %s" % (k,v) - for k,v in io_options.items())) + "(please create a bug report):\n\t" + + "\n\t".join("%s = %s" % (k, v) for k, v in io_options.items()) + ) - ans = pyomo.scripting.convert.\ - pyomo2bar(['--output',problem_filename,args[2]]) + ans = pyomo.scripting.convert.pyomo2bar( + ['--output', problem_filename, args[2]] + ) if ans.errorcode: - raise RuntimeError("pyomo2bar conversion " - "returned nonzero error code " - "(%s)" % ans.errorcode) + raise RuntimeError( + "pyomo2bar conversion " + "returned nonzero error code " + "(%s)" % ans.errorcode + ) model = ans.retval problem_filename = model.filename symbol_map = model.symbol_map - return (problem_filename,),symbol_map + return (problem_filename,), symbol_map elif args[1] in [ProblemFormat.mps, ProblemFormat.nl]: if args[1] == ProblemFormat.nl: - problem_filename = TempfileManager.\ - create_tempfile(suffix = '.pyomo.nl') + problem_filename = TempfileManager.create_tempfile(suffix='.pyomo.nl') if io_options.get("symbolic_solver_labels", False): TempfileManager.add_tempfile( - problem_filename[:-3]+".row", - exists=False) + problem_filename[:-3] + ".row", exists=False + ) TempfileManager.add_tempfile( - problem_filename[:-3]+".col", - exists=False) + problem_filename[:-3] + ".col", exists=False + ) else: assert args[1] == ProblemFormat.mps - problem_filename = TempfileManager.\ - create_tempfile(suffix = '.pyomo.mps') + problem_filename = TempfileManager.create_tempfile(suffix='.pyomo.mps') if instance is not None: if isinstance(instance, IBlock): symbol_map_id = instance.write( @@ -175,14 +181,15 @@ def apply(self, *args, **kwds): format=args[1], _solver_capability=capabilities, _called_by_solver=True, - **io_options) + **io_options + ) else: - (problem_filename, symbol_map_id) = \ - instance.write( - filename=problem_filename, - format=args[1], - solver_capability=capabilities, - io_options=io_options) + (problem_filename, symbol_map_id) = instance.write( + filename=problem_filename, + format=args[1], + solver_capability=capabilities, + io_options=io_options, + ) return (problem_filename,), symbol_map_id else: @@ -195,22 +202,25 @@ def apply(self, *args, **kwds): if len(io_options): raise ValueError( "The following io_options will be ignored " - "(please create a bug report):\n\t" + - "\n\t".join("%s = %s" % (k,v) - for k,v in io_options.items())) + "(please create a bug report):\n\t" + + "\n\t".join("%s = %s" % (k, v) for k, v in io_options.items()) + ) - ans = pyomo.scripting.convert.\ - pyomo2nl(['--output',problem_filename,args[2]]) + ans = pyomo.scripting.convert.pyomo2nl( + ['--output', problem_filename, args[2]] + ) if ans.errorcode: - raise RuntimeError("pyomo2nl conversion " - "returned nonzero error " - "code (%s)" % ans.errorcode) + raise RuntimeError( + "pyomo2nl conversion " + "returned nonzero error " + "code (%s)" % ans.errorcode + ) model = ans.retval problem_filename = model.filename symbol_map = model.symbol_map if args[1] == ProblemFormat.nl: - return (problem_filename,),symbol_map + return (problem_filename,), symbol_map # # Convert from NL to MPS # @@ -221,16 +231,15 @@ def apply(self, *args, **kwds): # NOTE: we should generalize this so it doesn't strictly # depend on the PICO converter utility. # - ans = self.pico_converter.apply(ProblemFormat.nl, - ProblemFormat.mps, - problem_filename) + ans = self.pico_converter.apply( + ProblemFormat.nl, ProblemFormat.mps, problem_filename + ) os.remove(problem_filename) return ans elif args[1] == ProblemFormat.osil: if False: - problem_filename = TempfileManager.\ - create_tempfile(suffix='pyomo.osil') + problem_filename = TempfileManager.create_tempfile(suffix='pyomo.osil') if instance: if isinstance(instance, IBlock): symbol_map_id = instance.write( @@ -238,17 +247,19 @@ def apply(self, *args, **kwds): format=ProblemFormat.osil, _solver_capability=capabilities, _called_by_solver=True, - **io_options) + **io_options + ) else: - (problem_filename, symbol_map_id) = \ - instance.write( - filename=problem_filename, - format=ProblemFormat.osil, - solver_capability=capabilities, - io_options=io_options) + (problem_filename, symbol_map_id) = instance.write( + filename=problem_filename, + format=ProblemFormat.osil, + solver_capability=capabilities, + io_options=io_options, + ) return (problem_filename,), None else: raise NotImplementedError( "There is currently no " "script conversion available from " - "Pyomo to OSiL format.") + "Pyomo to OSiL format." + ) diff --git a/pyomo/solvers/plugins/converter/pico.py b/pyomo/solvers/plugins/converter/pico.py index 2715dd028aa..7fd0d11222b 100644 --- a/pyomo/solvers/plugins/converter/pico.py +++ b/pyomo/solvers/plugins/converter/pico.py @@ -20,7 +20,6 @@ class PicoMIPConverter(object): - def can_convert(self, from_type, to_type): """Returns true if this object supports the specified conversion""" # @@ -49,31 +48,45 @@ def apply(self, *args, **kwargs): Run the external pico_convert utility """ if len(args) != 3: - raise ConverterError("Cannot apply pico_convert with more than one filename or model") + raise ConverterError( + "Cannot apply pico_convert with more than one filename or model" + ) _exe = pyomo.common.Executable("pico_convert") if not _exe: raise ConverterError("The 'pico_convert' application cannot be found") pico_convert_cmd = _exe.path() - target=str(args[1]) - if target=="cpxlp": - target="lp" + target = str(args[1]) + if target == "cpxlp": + target = "lp" # NOTE: if you have an extra "." in the suffix, the pico_convert program fails to output to the correct filename. - output_filename = TempfileManager.create_tempfile(suffix = 'pico_convert.' + target) + output_filename = TempfileManager.create_tempfile( + suffix='pico_convert.' + target + ) if not isinstance(args[2], str): - fname= TempfileManager.create_tempfile(suffix= 'pico_convert.' +str(args[0])) + fname = TempfileManager.create_tempfile( + suffix='pico_convert.' + str(args[0]) + ) args[2].write(filename=fname, format=args[1]) - cmd = pico_convert_cmd +" --output="+output_filename+" "+target+" "+fname + cmd = ( + pico_convert_cmd + + " --output=" + + output_filename + + " " + + target + + " " + + fname + ) else: - cmd = pico_convert_cmd +" --output="+output_filename+" "+target + cmd = pico_convert_cmd + " --output=" + output_filename + " " + target for item in args[2:]: if not os.path.exists(item): - raise ConverterError("File "+item+" does not exist!") - cmd = cmd + " "+item - print("Running command: "+cmd) - subprocess.run(cmd, stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL) - if not os.path.exists(output_filename): #pragma:nocover - raise ApplicationError(\ - "Problem launching 'pico_convert' to create "+output_filename) - return (output_filename,),None # no variable map at the moment + raise ConverterError("File " + item + " does not exist!") + cmd = cmd + " " + item + print("Running command: " + cmd) + subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + if not os.path.exists(output_filename): # pragma:nocover + raise ApplicationError( + "Problem launching 'pico_convert' to create " + output_filename + ) + return (output_filename,), None # no variable map at the moment diff --git a/pyomo/solvers/plugins/solvers/ASL.py b/pyomo/solvers/plugins/solvers/ASL.py index 198e04f2277..cb7d33cabba 100644 --- a/pyomo/solvers/plugins/solvers/ASL.py +++ b/pyomo/solvers/plugins/solvers/ASL.py @@ -26,14 +26,15 @@ from pyomo.core import TransformationFactory import logging + logger = logging.getLogger('pyomo.solvers') -@SolverFactory.register('asl', doc='Interface for solvers using the AMPL Solver Library') +@SolverFactory.register( + 'asl', doc='Interface for solvers using the AMPL Solver Library' +) class ASL(SystemCallSolver): - """A generic optimizer that uses the AMPL Solver Library to interface with applications. - """ - + """A generic optimizer that uses the AMPL Solver Library to interface with applications.""" def __init__(self, **kwds): # @@ -47,7 +48,7 @@ def __init__(self, **kwds): # Setup valid problem formats, and valid results for each problem format. # Also set the default problem and results formats. # - self._valid_problem_formats=[ProblemFormat.nl] + self._valid_problem_formats = [ProblemFormat.nl] self._valid_result_formats = {} self._valid_result_formats[ProblemFormat.nl] = [ResultsFormat.sol] self.set_problem_format(ProblemFormat.nl) @@ -74,14 +75,14 @@ def _default_executable(self): logger.warning("No solver option specified for ASL solver interface") return None if not self.options.solver: - logger.warning( - "No solver option specified for ASL solver interface") + logger.warning("No solver option specified for ASL solver interface") return None executable = Executable(self.options.solver) if not executable: logger.warning( "Could not locate the '%s' executable, which is required " - "for solver %s" % (self.options.solver, self.name)) + "for solver %s" % (self.options.solver, self.name) + ) self.enable = False return None return executable.path() @@ -94,11 +95,13 @@ def _get_version(self): if solver_exec is None: return _extract_version('') try: - results = subprocess.run([solver_exec, "-v"], - timeout=5, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - universal_newlines=True) + results = subprocess.run( + [solver_exec, "-v"], + timeout=5, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True, + ) ver = _extract_version(results.stdout) if ver is None: # Some ASL solvers do not export a version number @@ -116,28 +119,30 @@ def available(self, exception_flag=True): return self.version() is not None def create_command_line(self, executable, problem_files): - assert(self._problem_format == ProblemFormat.nl) - assert(self._results_format == ResultsFormat.sol) + assert self._problem_format == ProblemFormat.nl + assert self._results_format == ResultsFormat.sol # # Define log file # solver_name = os.path.basename(self.options.solver) if self._log_file is None: - self._log_file = TempfileManager.\ - create_tempfile(suffix="_%s.log" % solver_name) + self._log_file = TempfileManager.create_tempfile( + suffix="_%s.log" % solver_name + ) # # Define solution file # if self._soln_file is not None: # the solution file can not be redefined - logger.warning("The 'soln_file' keyword will be ignored " - "for solver="+self.type) + logger.warning( + "The 'soln_file' keyword will be ignored " "for solver=" + self.type + ) fname = problem_files[0] if '.' in fname: tmp = fname.split('.') fname = '.'.join(tmp[:-1]) - self._soln_file = fname+".sol" + self._soln_file = fname + ".sol" # # Define results file (since an external parser is used) @@ -147,7 +152,7 @@ def create_command_line(self, executable, problem_files): # # Define command line # - env=os.environ.copy() + env = os.environ.copy() # # Merge the PYOMO_AMPLFUNC (externals defined within # Pyomo/Pyomo) with any user-specified external function @@ -172,20 +177,19 @@ def create_command_line(self, executable, problem_files): # Because of this, I think the only reliable way to pass options for any # solver is by using the command line # - opt=[] + opt = [] for key in self.options: if key == 'solver': continue - if isinstance(self.options[key], str) and \ - (' ' in self.options[key]): - opt.append(key+"=\""+str(self.options[key])+"\"") - cmd.append(str(key)+"="+str(self.options[key])) + if isinstance(self.options[key], str) and (' ' in self.options[key]): + opt.append(key + "=\"" + str(self.options[key]) + "\"") + cmd.append(str(key) + "=" + str(self.options[key])) elif key == 'subsolver': - opt.append("solver="+str(self.options[key])) - cmd.append(str(key)+"="+str(self.options[key])) + opt.append("solver=" + str(self.options[key])) + cmd.append(str(key) + "=" + str(self.options[key])) else: - opt.append(key+"="+str(self.options[key])) - cmd.append(str(key)+"="+str(self.options[key])) + opt.append(key + "=" + str(self.options[key])) + cmd.append(str(key) + "=" + str(self.options[key])) envstr = "%s_options" % self.options.solver # Merge with any options coming in through the environment @@ -194,8 +198,7 @@ def create_command_line(self, executable, problem_files): return Bunch(cmd=cmd, log_file=self._log_file, env=env) def _presolve(self, *args, **kwds): - if (not isinstance(args[0], str)) and \ - (not isinstance(args[0], IBlock)): + if (not isinstance(args[0], str)) and (not isinstance(args[0], IBlock)): self._instance = args[0] xfrm = TransformationFactory('mpec.nl') xfrm.apply_to(self._instance) @@ -214,11 +217,12 @@ def _postsolve(self): # # Reclassify complementarity components # - mpec=False + mpec = False if not self._instance is None: from pyomo.mpec import Complementarity + for cuid in self._instance._transformation_data['mpec.nl'].compl_cuids: - mpec=True + mpec = True cobj = cuid.find_component_on(self._instance) cobj.parent_block().reclassify_component_type(cobj, Complementarity) # @@ -227,32 +231,27 @@ def _postsolve(self): @SolverFactory.register('_mock_asl') -class MockASL(ASL,MockMIP): - """A Mock ASL solver used for testing - """ +class MockASL(ASL, MockMIP): + """A Mock ASL solver used for testing""" def __init__(self, **kwds): try: - ASL.__init__(self,**kwds) - except ApplicationError: #pragma:nocover - pass #pragma:nocover - MockMIP.__init__(self,"asl") + ASL.__init__(self, **kwds) + except ApplicationError: # pragma:nocover + pass # pragma:nocover + MockMIP.__init__(self, "asl") self._assert_available = True def available(self, exception_flag=True): - return ASL.available(self,exception_flag) + return ASL.available(self, exception_flag) - def create_command_line(self,executable, problem_files): - command = ASL.create_command_line(self, - executable, - problem_files) - MockMIP.create_command_line(self, - executable, - problem_files) + def create_command_line(self, executable, problem_files): + command = ASL.create_command_line(self, executable, problem_files) + MockMIP.create_command_line(self, executable, problem_files) return command def executable(self): return MockMIP.executable(self) - def _execute_command(self,cmd): - return MockMIP._execute_command(self,cmd) + def _execute_command(self, cmd): + return MockMIP._execute_command(self, cmd) diff --git a/pyomo/solvers/plugins/solvers/BARON.py b/pyomo/solvers/plugins/solvers/BARON.py index 3f3d7f31219..1caa7c7f5f7 100644 --- a/pyomo/solvers/plugins/solvers/BARON.py +++ b/pyomo/solvers/plugins/solvers/BARON.py @@ -22,17 +22,21 @@ from pyomo.opt.base import ProblemFormat, ResultsFormat, OptSolver from pyomo.opt.base.solvers import _extract_version, SolverFactory from pyomo.opt.results import ( - SolverResults, Solution, SolverStatus, TerminationCondition, + SolverResults, + Solution, + SolverStatus, + TerminationCondition, SolutionStatus, ) from pyomo.opt.solver import SystemCallSolver logger = logging.getLogger('pyomo.solvers') -@SolverFactory.register('baron', doc='The BARON MINLP solver') + +@SolverFactory.register('baron', doc='The BARON MINLP solver') class BARONSHELL(SystemCallSolver): - """The BARON MINLP solver - """ + """The BARON MINLP solver""" + _solver_info_cache = {} def __init__(self, **kwds): @@ -44,7 +48,7 @@ def __init__(self, **kwds): self._tim_file = None - self._valid_problem_formats=[ProblemFormat.bar] + self._valid_problem_formats = [ProblemFormat.bar] self._valid_result_formats = {} self._valid_result_formats[ProblemFormat.bar] = [ResultsFormat.soln] self.set_problem_format(ProblemFormat.bar) @@ -57,7 +61,6 @@ def __init__(self, **kwds): self._capabilities.sos1 = False self._capabilities.sos2 = False - # CLH: Coppied from cpxlp.py, the cplex file writer. # Keven Hunter made a nice point about using %.16g in his attachment # to ticket #4319. I am adjusting this to %.17g as this mocks the @@ -73,37 +76,35 @@ def __init__(self, **kwds): self._precision_string = '.17g' def _get_dummy_input_files(self, check_license=False): - with tempfile.NamedTemporaryFile(mode='w', - delete=False) as f: + with tempfile.NamedTemporaryFile(mode='w', delete=False) as f: # For some reason, if results: 0 is added to the options # section, it causes a file named fort.71 to appear. # So point the ResName option to a temporary file that # we will delete - with tempfile.NamedTemporaryFile(mode='w', - delete=False) as fr: + with tempfile.NamedTemporaryFile(mode='w', delete=False) as fr: pass # Doing this for the remaining output files as well. # Can't seem to reliably control the files created by # Baron otherwise. - with tempfile.NamedTemporaryFile(mode='w', - delete=False) as fs: + with tempfile.NamedTemporaryFile(mode='w', delete=False) as fs: pass - with tempfile.NamedTemporaryFile(mode='w', - delete=False) as ft: + with tempfile.NamedTemporaryFile(mode='w', delete=False) as ft: pass - f.write("//This is a dummy .bar file created to " - "return the baron version//\n" - "OPTIONS {\n" - "results: 1;\n" - "ResName: \""+fr.name+"\";\n" - "summary: 1;\n" - "SumName: \""+fs.name+"\";\n" - "times: 1;\n" - "TimName: \""+ft.name+"\";\n" - "}\n") + f.write( + "//This is a dummy .bar file created to " + "return the baron version//\n" + "OPTIONS {\n" + "results: 1;\n" + "ResName: \"" + fr.name + "\";\n" + "summary: 1;\n" + "SumName: \"" + fs.name + "\";\n" + "times: 1;\n" + "TimName: \"" + ft.name + "\";\n" + "}\n" + ) f.write("POSITIVE_VARIABLES ") if check_license: - f.write(", ".join("x"+str(i) for i in range(11))) + f.write(", ".join("x" + str(i) for i in range(11))) else: f.write("x1") f.write(";\n") @@ -130,11 +131,13 @@ def license_is_valid(self): if not solver_exec: licensed = False else: - fnames= self._get_dummy_input_files(check_license=True) + fnames = self._get_dummy_input_files(check_license=True) try: - process = subprocess.Popen([solver_exec, fnames[0]], - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) + process = subprocess.Popen( + [solver_exec, fnames[0]], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) stdout, stderr = process.communicate() assert stderr is None rc = 0 @@ -156,8 +159,10 @@ def license_is_valid(self): def _default_executable(self): executable = Executable("baron") if not executable: - logger.warning("Could not locate the 'baron' executable, " - "which is required for solver %s" % self.name) + logger.warning( + "Could not locate the 'baron' executable, " + "which is required for solver %s" % self.name + ) self.enable = False return None return executable.path() @@ -175,10 +180,12 @@ def _get_version(self): else: fnames = self._get_dummy_input_files(check_license=False) try: - results = subprocess.run([solver_exec, fnames[0]], - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - universal_newlines=True) + results = subprocess.run( + [solver_exec, fnames[0]], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True, + ) ver = _extract_version(results.stdout) finally: self._remove_dummy_input_files(fnames) @@ -191,15 +198,12 @@ def create_command_line(self, executable, problem_files): # The solution file is created in the _convert_problem function. # The bar file needs the solution filename in the OPTIONS section, but # this function is executed after the bar problem file writing. - #self._soln_file = pyomo.common.tempfiles.TempfileManager.create_tempfile(suffix = '.baron.sol') - + # self._soln_file = pyomo.common.tempfiles.TempfileManager.create_tempfile(suffix = '.baron.sol') cmd = [executable, problem_files[0]] if self._timer: cmd.insert(0, self._timer) - return Bunch( cmd=cmd, - log_file=self._log_file, - env=None ) + return Bunch(cmd=cmd, log_file=self._log_file, env=None) # # Assuming the variable values stored in the model will @@ -209,11 +213,7 @@ def warm_start_capable(self): return False - def _convert_problem(self, - args, - problem_format, - valid_problem_formats, - **kwds): + def _convert_problem(self, args, problem_format, valid_problem_formats, **kwds): # Baron needs all solver options and file redirections # inside the input file, so we need to input those @@ -223,18 +223,15 @@ def _convert_problem(self, # Define log file # if self._log_file is None: - self._log_file = TempfileManager.\ - create_tempfile(suffix = '.baron.log') + self._log_file = TempfileManager.create_tempfile(suffix='.baron.log') # # Define solution file # if self._soln_file is None: - self._soln_file = TempfileManager.\ - create_tempfile(suffix = '.baron.soln') + self._soln_file = TempfileManager.create_tempfile(suffix='.baron.soln') - self._tim_file = TempfileManager.\ - create_tempfile(suffix = '.baron.tim') + self._tim_file = TempfileManager.create_tempfile(suffix='.baron.tim') # # Create options to send through as io_options @@ -250,12 +247,13 @@ def _convert_problem(self, 'Ignoring user-specified option "%s=%s". This ' 'option is set to %s, and can be overridden using ' 'the "solnfile" argument to the solve() method.' - % (key, self.options[key], self._soln_file)) + % (key, self.options[key], self._soln_file) + ) elif lower_key == 'timname': logger.warning( 'Ignoring user-specified option "%s=%s". This ' - 'option is set to %s.' - % (key, self.options[key], self._tim_file)) + 'option is set to %s.' % (key, self.options[key], self._tim_file) + ) else: solver_options[key] = self.options[key] @@ -265,19 +263,19 @@ def _convert_problem(self, break if 'solver_options' in kwds: - raise ValueError("Baron solver options should be set " - "using the options object on this " - "solver plugin. The solver_options " - "I/O options dict for the Baron writer " - "will be populated by this plugin's " - "options object") + raise ValueError( + "Baron solver options should be set " + "using the options object on this " + "solver plugin. The solver_options " + "I/O options dict for the Baron writer " + "will be populated by this plugin's " + "options object" + ) kwds['solver_options'] = solver_options - return OptSolver._convert_problem(self, - args, - problem_format, - valid_problem_formats, - **kwds) + return OptSolver._convert_problem( + self, args, problem_format, valid_problem_formats, **kwds + ) def process_logfile(self): @@ -286,8 +284,7 @@ def process_logfile(self): # # Process logfile # - cuts = ['Bilinear', 'LD-Envelopes', 'Multilinears', - 'Convexity', 'Integrality'] + cuts = ['Bilinear', 'LD-Envelopes', 'Multilinears', 'Convexity', 'Integrality'] # Collect cut-generation statistics from the log file with open(self._log_file) as OUTPUT: @@ -295,14 +292,14 @@ def process_logfile(self): for field in cuts: if field in line: try: - results.solver.statistics[field+'_cuts'] = int( - line.split()[1]) + results.solver.statistics[field + '_cuts'] = int( + line.split()[1] + ) except: pass return results - def process_soln_file(self, results): # check for existence of the solution and time file. Not sure why we # just return - would think that we would want to indicate @@ -315,7 +312,7 @@ def process_soln_file(self, results): return with open(self._tim_file, "r") as TimFile: - with open(self._soln_file,"r") as INPUT: + with open(self._soln_file, "r") as INPUT: self._process_soln_file(results, TimFile, INPUT) def _process_soln_file(self, results, TimFile, INPUT): @@ -341,15 +338,17 @@ def _process_soln_file(self, results, TimFile, INPUT): extract_price = False for suffix in self._suffixes: flag = False - if re.match(suffix, "rc"): #baron_marginal + if re.match(suffix, "rc"): # baron_marginal extract_marginals = True flag = True - if re.match(suffix, "dual"): #baron_price + if re.match(suffix, "dual"): # baron_price extract_price = True flag = True if not flag: - raise RuntimeError("***The BARON solver plugin cannot" - "extract solution suffix="+suffix) + raise RuntimeError( + "***The BARON solver plugin cannot" + "extract solution suffix=" + suffix + ) soln = Solution() @@ -402,71 +401,64 @@ def _process_soln_file(self, results, TimFile, INPUT): soln.objective[objective_label] = {'Value': None} results.problem.number_of_objectives = 1 if objective is not None: - results.problem.sense = \ + results.problem.sense = ( 'minimizing' if objective.is_minimizing() else 'maximizing' + ) if solver_status == '1': results.solver.status = SolverStatus.ok elif solver_status == '2': results.solver.status = SolverStatus.error results.solver.termination_condition = TerminationCondition.error - #CLH: I wasn't sure if this was double reporting errors. I + # CLH: I wasn't sure if this was double reporting errors. I # just filled in one termination_message for now - results.solver.termination_message = \ - ("Insufficient memory to store the number of nodes required " - "for this seach tree. Increase physical memory or change " - "algorithmic options") + results.solver.termination_message = ( + "Insufficient memory to store the number of nodes required " + "for this seach tree. Increase physical memory or change " + "algorithmic options" + ) elif solver_status == '3': results.solver.status = SolverStatus.ok - results.solver.termination_condition = \ - TerminationCondition.maxIterations + results.solver.termination_condition = TerminationCondition.maxIterations elif solver_status == '4': results.solver.status = SolverStatus.ok - results.solver.termination_condition = \ - TerminationCondition.maxTimeLimit + results.solver.termination_condition = TerminationCondition.maxTimeLimit elif solver_status == '5': results.solver.status = SolverStatus.warning - results.solver.termination_condition = \ - TerminationCondition.other + results.solver.termination_condition = TerminationCondition.other elif solver_status == '6': results.solver.status = SolverStatus.aborted - results.solver.termination_condition = \ - TerminationCondition.userInterrupt + results.solver.termination_condition = TerminationCondition.userInterrupt elif solver_status == '7': results.solver.status = SolverStatus.error - results.solver.termination_condition = \ - TerminationCondition.error + results.solver.termination_condition = TerminationCondition.error elif solver_status == '8': results.solver.status = SolverStatus.unknown - results.solver.termination_condition = \ - TerminationCondition.unknown + results.solver.termination_condition = TerminationCondition.unknown elif solver_status == '9': results.solver.status = SolverStatus.error - results.solver.termination_condition = \ - TerminationCondition.solverFailure + results.solver.termination_condition = TerminationCondition.solverFailure elif solver_status == '10': results.solver.status = SolverStatus.error - results.solver.termination_condition = \ - TerminationCondition.error + results.solver.termination_condition = TerminationCondition.error elif solver_status == '11': results.solver.status = SolverStatus.aborted - results.solver.termination_condition = \ + results.solver.termination_condition = ( TerminationCondition.licensingProblems - results.solver.termination_message = \ + ) + results.solver.termination_message = ( 'Run terminated because of a licensing error.' + ) if model_status == '1': soln.status = SolutionStatus.optimal - results.solver.termination_condition = \ - TerminationCondition.optimal + results.solver.termination_condition = TerminationCondition.optimal elif model_status == '2': soln.status = SolutionStatus.infeasible - results.solver.termination_condition = \ - TerminationCondition.infeasible + results.solver.termination_condition = TerminationCondition.infeasible elif model_status == '3': soln.status = SolutionStatus.unbounded - results.solver.termination_condition = \ - TerminationCondition.unbounded + results.solver.termination_condition = TerminationCondition.unbounded elif model_status == '4': soln.status = SolutionStatus.feasible elif model_status == '5': @@ -478,8 +470,7 @@ def _process_soln_file(self, results, TimFile, INPUT): # Solutions that were preprocessed infeasible, were aborted, # or gave error will not have filled in res.lst files - if results.solver.status not in [SolverStatus.error, - SolverStatus.aborted]: + if results.solver.status not in [SolverStatus.error, SolverStatus.aborted]: # # Extract the solution vector and objective value from BARON # @@ -505,12 +496,13 @@ def _process_soln_file(self, results, TimFile, INPUT): objective_value = float(INPUT.readline().split()[4]) except IndexError: # No objective value, so no solution to return - if solver_status == '1' and model_status in ('1','4'): + if solver_status == '1' and model_status in ('1', '4'): logger.error( -"""Failed to process BARON solution file: could not extract the final + """Failed to process BARON solution file: could not extract the final objective value, but BARON completed normally. This is indicative of a bug in Pyomo's BARON solution parser. Please report this (along with -the Pyomo model and BARON version) to the Pyomo Developers.""") +the Pyomo model and BARON version) to the Pyomo Developers.""" + ) return INPUT.readline() INPUT.readline() @@ -588,14 +580,15 @@ def _process_soln_file(self, results, TimFile, INPUT): # for i, price_val in enumerate(con_price, 1): # use the alias made by the Baron writer - con_label = ".c"+str(i) + con_label = ".c" + str(i) soln_constraint[con_label] = {"dual": price_val} # This check is necessary because solutions that are # preprocessed infeasible have ok solver status, but no # objective value located in the res.lst file - if not (SolvedDuringPreprocessing and \ - soln.status == SolutionStatus.infeasible): + if not ( + SolvedDuringPreprocessing and soln.status == SolutionStatus.infeasible + ): soln.objective[objective_label] = {'Value': objective_value} # Fill the solution for most cases, except errors diff --git a/pyomo/solvers/plugins/solvers/CBCplugin.py b/pyomo/solvers/plugins/solvers/CBCplugin.py index e169adf1993..064e979de05 100644 --- a/pyomo/solvers/plugins/solvers/CBCplugin.py +++ b/pyomo/solvers/plugins/solvers/CBCplugin.py @@ -26,7 +26,14 @@ from pyomo.core import Var from pyomo.opt.base import ProblemFormat, ResultsFormat, OptSolver from pyomo.opt.base.solvers import _extract_version, SolverFactory -from pyomo.opt.results import SolverResults, SolverStatus, TerminationCondition, SolutionStatus, ProblemSense, Solution +from pyomo.opt.results import ( + SolverResults, + SolverStatus, + TerminationCondition, + SolutionStatus, + ProblemSense, + Solution, +) from pyomo.opt.solver import SystemCallSolver from pyomo.solvers.mockmip import MockMIP @@ -35,13 +42,12 @@ @SolverFactory.register('cbc', doc='The CBC LP/MIP solver') class CBC(OptSolver): - """The CBC LP/MIP solver - """ + """The CBC LP/MIP solver""" def __new__(cls, *args, **kwds): mode = kwds.pop('solver_io', 'lp') - if mode == 'lp' or mode is None: + if mode == 'lp' or mode is None: opt = SolverFactory('_cbc_shell', **kwds) opt.set_problem_format(ProblemFormat.cpxlp) return opt @@ -60,7 +66,7 @@ def __new__(cls, *args, **kwds): # options (-s in particular, which is required for # streaming output of all asl solvers). Therefore we need # to send it through the cbc_shell instead of ASL - opt = SolverFactory('_cbc_shell',**kwds) + opt = SolverFactory('_cbc_shell', **kwds) opt.set_problem_format(ProblemFormat.nl) return opt elif mode == 'os': @@ -72,11 +78,9 @@ def __new__(cls, *args, **kwds): return - -@SolverFactory.register('_cbc_shell', doc='Shell interface to the CBC LP/MIP solver') +@SolverFactory.register('_cbc_shell', doc='Shell interface to the CBC LP/MIP solver') class CBCSHELL(SystemCallSolver): - """Shell interface to the CBC LP/MIP solver - """ + """Shell interface to the CBC LP/MIP solver""" def __init__(self, **kwds): # @@ -99,15 +103,15 @@ def __init__(self, **kwds): # Set up valid problem formats and valid results for each # problem format # - self._valid_problem_formats=[ + self._valid_problem_formats = [ ProblemFormat.cpxlp, ProblemFormat.nl, - #ProblemFormat.mps, + # ProblemFormat.mps, ] - self._valid_result_formats={ + self._valid_result_formats = { ProblemFormat.cpxlp: [ResultsFormat.soln], ProblemFormat.nl: [ResultsFormat.sol], - #ProblemFormat.mps: [ResultsFormat.soln], + # ProblemFormat.mps: [ResultsFormat.soln], } # Note: Undefined capabilities default to 'None' @@ -129,7 +133,7 @@ def __init__(self, **kwds): self.set_problem_format(ProblemFormat.cpxlp) def set_problem_format(self, format): - super(CBCSHELL,self).set_problem_format(format) + super(CBCSHELL, self).set_problem_format(format) if self._problem_format == ProblemFormat.cpxlp: self._capabilities.sos1 = False self._capabilities.sos2 = False @@ -144,22 +148,24 @@ def set_problem_format(self, format): if _ver is None: _ver_str = "" else: - _ver_str ='.'.join(str(i) for i in _ver) + _ver_str = '.'.join(str(i) for i in _ver) logger.warning( f"found CBC version {_ver_str} < 2.7; " "ASL support disabled (falling back on LP interface)." ) - logger.warning("Upgrade CBC to activate ASL " - "support in this plugin") + logger.warning( + "Upgrade CBC to activate ASL " "support in this plugin" + ) # Fall back on LP self.set_problem_format(ProblemFormat.cpxlp) else: - logger.warning("CBC solver is not compiled with ASL " - "interface (falling back on LP interface).") + logger.warning( + "CBC solver is not compiled with ASL " + "interface (falling back on LP interface)." + ) # Fall back on LP self.set_problem_format(ProblemFormat.cpxlp) - def _default_results_format(self, prob_format): if prob_format == ProblemFormat.nl: return ResultsFormat.sol @@ -169,7 +175,7 @@ def warm_start_capable(self): if self._problem_format != ProblemFormat.cpxlp: return False _ver = self.version() - return _ver and _ver >= (2,8,0,0) + return _ver and _ver >= (2, 8, 0, 0) def _write_soln_file(self, instance, filename): @@ -186,15 +192,13 @@ def _write_soln_file(self, instance, filename): for var in instance.component_data_objects(Var): # Cbc only expects integer variables with non-zero # values for mipstart. - if var.value \ - and (var.is_integer() or var.is_binary()) \ - and (id(var) in byObject): + if ( + var.value + and (var.is_integer() or var.is_binary()) + and (id(var) in byObject) + ): name = byObject[id(var)] - solnfile.write( - '{} {} {}\n'.format( - column_index, name, var.value - ) - ) + solnfile.write('{} {} {}\n'.format(column_index, name, var.value)) # Cbc ignores column indexes, so the value does not matter. column_index += 1 @@ -238,7 +242,8 @@ def _presolve(self, *args, **kwds): if self._warm_start_file_name is None: assert not user_warmstart self._warm_start_file_name = TempfileManager.create_tempfile( - suffix = '.cbc.soln') + suffix='.cbc.soln' + ) # CBC does not cleanly handle windows-style drive names in the # MIPSTART file name (though at least 2.10.5). @@ -255,7 +260,8 @@ def _presolve(self, *args, **kwds): logger.warning( "warmstart_file points to a file on a drive " "different from the current working directory. " - "CBC is likely to (silently) ignore the warmstart.") + "CBC is likely to (silently) ignore the warmstart." + ) # let the base class handle any remaining keywords/actions. # let the base class handle any remaining keywords/actions. @@ -269,7 +275,8 @@ def _presolve(self, *args, **kwds): if len(args) != 1: raise ValueError( "CBCplugin _presolve method can only handle a single " - "problem instance - %s were supplied" % (len(args),)) + "problem instance - %s were supplied" % (len(args),) + ) # write the warm-start file - currently only supports MIPs. # we only know how to deal with a single problem instance. @@ -279,15 +286,17 @@ def _presolve(self, *args, **kwds): self._warm_start(args[0]) end_time = time.time() if self._report_timing is True: - print("Warm start write time=%.2f seconds" % (end_time-start_time)) - + print( + "Warm start write time=%.2f seconds" % (end_time - start_time) + ) def _default_executable(self): executable = Executable("cbc") if not executable: logger.warning( "Could not locate the 'cbc' executable, which is " - "required for solver %s" % self.name) + "required for solver %s" % self.name + ) self.enable = False return None return executable.path() @@ -301,7 +310,7 @@ def _get_version(self): timeout=5, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, - universal_newlines=True + universal_newlines=True, ) _version = _extract_version(results.stdout) if _version is None: @@ -314,7 +323,7 @@ def _compiled_with_asl(self): timeout=5, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, - universal_newlines=True + universal_newlines=True, ) return 'No match for AMPL'.lower() not in results.stdout.lower() @@ -340,9 +349,9 @@ def create_command_line(self, executable, problem_files): else: problem_filename_prefix = tmp[0] if self._results_format is ResultsFormat.sol: - self._soln_file = problem_filename_prefix+".sol" + self._soln_file = problem_filename_prefix + ".sol" else: - self._soln_file = problem_filename_prefix+".soln" + self._soln_file = problem_filename_prefix + ".soln" # # Define the results file (if the sol external parser is used) @@ -368,14 +377,16 @@ def _check_and_escape_options(options): tmp_v = '"' + tmp_v + '"' if _bad: - raise ValueError("Unable to properly escape solver option:" - "\n\t%s=%s" % (key, val) ) + raise ValueError( + "Unable to properly escape solver option:" + "\n\t%s=%s" % (key, val) + ) yield (tmp_k, tmp_v) # # Define command line # - cmd = [ executable ] + cmd = [executable] if self._timer: cmd.insert(0, self._timer) if self._problem_format == ProblemFormat.nl: @@ -386,35 +397,32 @@ def _check_and_escape_options(options): cmd.extend(['-sec', str(self._timelimit)]) cmd.extend(['-timeMode', "elapsed"]) if "debug" in self.options: - cmd.extend(["-log","5"]) + cmd.extend(["-log", "5"]) for key, val in _check_and_escape_options(self.options): if key == 'solver': continue - cmd.append(key+"="+val) - os.environ['cbc_options']="printingOptions=all" - #cmd.extend(["-printingOptions=all", - #"-stat"]) + cmd.append(key + "=" + val) + os.environ['cbc_options'] = "printingOptions=all" + # cmd.extend(["-printingOptions=all", + # "-stat"]) else: if self._timelimit is not None and self._timelimit > 0.0: cmd.extend(['-sec', str(self._timelimit)]) cmd.extend(['-timeMode', "elapsed"]) if "debug" in self.options: - cmd.extend(["-log","5"]) + cmd.extend(["-log", "5"]) # these must go after options that take a value action_options = [] for key, val in _check_and_escape_options(self.options): if val.strip() != '': - cmd.extend(['-'+key, val]) + cmd.extend(['-' + key, val]) else: - action_options.append('-'+key) - cmd.extend(["-printingOptions", "all", - "-import", problem_files[0]]) + action_options.append('-' + key) + cmd.extend(["-printingOptions", "all", "-import", problem_files[0]]) cmd.extend(action_options) if self._warm_start_solve: - cmd.extend(["-mipstart",self._warm_start_file_name]) - cmd.extend(["-stat=1", - "-solve", - "-solu", self._soln_file]) + cmd.extend(["-mipstart", self._warm_start_file_name]) + cmd.extend(["-stat=1", "-solve", "-solu", self._soln_file]) return Bunch(cmd=cmd, log_file=self._log_file, env=None) @@ -457,48 +465,78 @@ def process_logfile(self): n_tokens = len(tokens) if n_tokens > 1: # https://projects.coin-or.org/Cbc/browser/trunk/Cbc/src/CbcSolver.cpp?rev=2497#L3769 - if n_tokens > 4 and tokens[:4] == ('Continuous', 'objective', 'value', 'is'): + if n_tokens > 4 and tokens[:4] == ( + 'Continuous', + 'objective', + 'value', + 'is', + ): lower_bound = _float(tokens[4]) # Search completed - best objective %g, took %d iterations and %d nodes - elif n_tokens > 12 and tokens[1:3] == ('Search', 'completed') \ - and tokens[4:6] == ('best', 'objective') and tokens[9] == 'iterations' \ - and tokens[12] == 'nodes': + elif ( + n_tokens > 12 + and tokens[1:3] == ('Search', 'completed') + and tokens[4:6] == ('best', 'objective') + and tokens[9] == 'iterations' + and tokens[12] == 'nodes' + ): optim_value = _float(tokens[6][:-1]) - results.solver.statistics.black_box.number_of_iterations = int(tokens[8]) + results.solver.statistics.black_box.number_of_iterations = int( + tokens[8] + ) nodes = int(tokens[11]) elif tokens[1] == 'Exiting' and n_tokens > 4: if tokens[2:4] == ('on', 'maximum'): - results.solver.termination_condition = {'nodes': TerminationCondition.maxEvaluations, - 'time': TerminationCondition.maxTimeLimit, - 'solutions': TerminationCondition.other, - 'iterations': TerminationCondition.maxIterations - }.get(tokens[4], TerminationCondition.other) + results.solver.termination_condition = { + 'nodes': TerminationCondition.maxEvaluations, + 'time': TerminationCondition.maxTimeLimit, + 'solutions': TerminationCondition.other, + 'iterations': TerminationCondition.maxIterations, + }.get(tokens[4], TerminationCondition.other) # elif tokens[2:5] == ('as', 'integer', 'gap'): # # We might want to handle this case # Integer solution of %g found... elif n_tokens >= 4 and tokens[1:4] == ('Integer', 'solution', 'of'): optim_value = _float(tokens[4]) try: - results.solver.statistics.black_box.number_of_iterations = \ - int(tokens[tokens.index('iterations') - 1]) + results.solver.statistics.black_box.number_of_iterations = int( + tokens[tokens.index('iterations') - 1] + ) nodes = int(tokens[tokens.index('nodes') - 1]) except ValueError: pass # Partial search - best objective %g (best possible %g), took %d iterations and %d nodes - elif n_tokens > 15 and tokens[1:3] == ('Partial', 'search') \ - and tokens[4:6] == ('best', 'objective') and tokens[7:9] == ('(best', 'possible') \ - and tokens[12] == 'iterations' and tokens[15] == 'nodes': + elif ( + n_tokens > 15 + and tokens[1:3] == ('Partial', 'search') + and tokens[4:6] == ('best', 'objective') + and tokens[7:9] == ('(best', 'possible') + and tokens[12] == 'iterations' + and tokens[15] == 'nodes' + ): optim_value = _float(tokens[6]) lower_bound = _float(tokens[9][:-2]) - results.solver.statistics.black_box.number_of_iterations = int(tokens[11]) + results.solver.statistics.black_box.number_of_iterations = int( + tokens[11] + ) nodes = int(tokens[14]) - elif n_tokens > 12 and tokens[1] == 'After' and tokens[3] == 'nodes,' \ - and tokens[8:10] == ('best', 'solution,') and tokens[10:12] == ('best', 'possible'): + elif ( + n_tokens > 12 + and tokens[1] == 'After' + and tokens[3] == 'nodes,' + and tokens[8:10] == ('best', 'solution,') + and tokens[10:12] == ('best', 'possible') + ): nodes = int(tokens[2]) optim_value = _float(tokens[7]) lower_bound = _float(tokens[12]) - elif tokens[0] == "Current" and n_tokens == 10 and tokens[1] == "default" and tokens[2] == "(if" \ - and results.problem.name is None: + elif ( + tokens[0] == "Current" + and n_tokens == 10 + and tokens[1] == "default" + and tokens[2] == "(if" + and results.problem.name is None + ): results.problem.name = tokens[-1] if '.' in results.problem.name: parts = results.problem.name.split('.') @@ -513,48 +551,73 @@ def process_logfile(self): # https://projects.coin-or.org/Cbc/browser/trunk/Cbc/src/CbcSolver.cpp?rev=2497#L10840 elif tokens[0] == 'Presolve': if n_tokens > 9 and tokens[3] == 'rows,' and tokens[6] == 'columns': - results.problem.number_of_variables = int(tokens[4]) - int(tokens[5][1:-1]) - results.problem.number_of_constraints = int(tokens[1]) - int(tokens[2][1:-1]) + results.problem.number_of_variables = int(tokens[4]) - int( + tokens[5][1:-1] + ) + results.problem.number_of_constraints = int(tokens[1]) - int( + tokens[2][1:-1] + ) results.problem.number_of_objectives = 1 elif n_tokens > 6 and tokens[6] == 'infeasible': soln.status = SolutionStatus.infeasible # https://projects.coin-or.org/Cbc/browser/trunk/Cbc/src/CbcSolver.cpp?rev=2497#L11105 - elif n_tokens > 11 and tokens[:2] == ('Problem', 'has') and tokens[3] == 'rows,' and \ - tokens[5] == 'columns' and tokens[7:9] == ('with', 'objective)'): + elif ( + n_tokens > 11 + and tokens[:2] == ('Problem', 'has') + and tokens[3] == 'rows,' + and tokens[5] == 'columns' + and tokens[7:9] == ('with', 'objective)') + ): results.problem.number_of_variables = int(tokens[4]) results.problem.number_of_constraints = int(tokens[2]) results.problem.number_of_nonzeros = int(tokens[6][1:]) results.problem.number_of_objectives = 1 # https://projects.coin-or.org/Cbc/browser/trunk/Cbc/src/CbcSolver.cpp?rev=2497#L10814 - elif n_tokens > 8 and tokens[:3] == ('Original', 'problem', 'has') and tokens[4] == 'integers' \ - and tokens[6:9] == ('of', 'which', 'binary)'): + elif ( + n_tokens > 8 + and tokens[:3] == ('Original', 'problem', 'has') + and tokens[4] == 'integers' + and tokens[6:9] == ('of', 'which', 'binary)') + ): results.problem.number_of_integer_variables = int(tokens[3]) results.problem.number_of_binary_variables = int(tokens[5][1:]) elif n_tokens == 5 and tokens[3] == "NAME": results.problem.name = tokens[4] - elif 'CoinLpIO::readLp(): Maximization problem reformulated as minimization' in ' '.join(tokens): + elif ( + 'CoinLpIO::readLp(): Maximization problem reformulated as minimization' + in ' '.join(tokens) + ): results.problem.sense = ProblemSense.maximize # https://projects.coin-or.org/Cbc/browser/trunk/Cbc/src/CbcSolver.cpp?rev=2497#L3047 elif n_tokens > 3 and tokens[:2] == ('Result', '-'): if tokens[2:4] in [('Run', 'abandoned'), ('User', 'ctrl-c')]: - results.solver.termination_condition = TerminationCondition.userInterrupt + results.solver.termination_condition = ( + TerminationCondition.userInterrupt + ) if n_tokens > 4: if tokens[2:5] == ('Optimal', 'solution', 'found'): # parser for log file generetated with discrete variable soln.status = SolutionStatus.optimal # if n_tokens > 7 and tokens[5:8] == ('(within', 'gap', 'tolerance)'): # # We might want to handle this case - elif tokens[2:5] in [('Linear', 'relaxation', 'infeasible'), - ('Problem', 'proven', 'infeasible')]: + elif tokens[2:5] in [ + ('Linear', 'relaxation', 'infeasible'), + ('Problem', 'proven', 'infeasible'), + ]: soln.status = SolutionStatus.infeasible elif tokens[2:5] == ('Linear', 'relaxation', 'unbounded'): soln.status = SolutionStatus.unbounded - elif n_tokens > 5 and tokens[2:4] == ('Stopped', 'on') and tokens[5] == 'limit': - results.solver.termination_condition = {'node': TerminationCondition.maxEvaluations, - 'time': TerminationCondition.maxTimeLimit, - 'solution': TerminationCondition.other, - 'iterations': TerminationCondition.maxIterations - }.get(tokens[4], TerminationCondition.other) + elif ( + n_tokens > 5 + and tokens[2:4] == ('Stopped', 'on') + and tokens[5] == 'limit' + ): + results.solver.termination_condition = { + 'node': TerminationCondition.maxEvaluations, + 'time': TerminationCondition.maxTimeLimit, + 'solution': TerminationCondition.other, + 'iterations': TerminationCondition.maxIterations, + }.get(tokens[4], TerminationCondition.other) # perhaps from https://projects.coin-or.org/Cbc/browser/trunk/Cbc/src/CbcSolver.cpp?rev=2497#L12318 elif n_tokens > 3 and tokens[2] == "Finished": soln.status = SolutionStatus.optimal @@ -564,10 +627,17 @@ def process_logfile(self): # parser for log file generetated with discrete variable optim_value = _float(tokens[2]) # https://projects.coin-or.org/Cbc/browser/trunk/Cbc/src/CbcSolver.cpp?rev=2497#L7904 - elif n_tokens >= 4 and tokens[:4] == ('No', 'feasible', 'solution', 'found'): + elif n_tokens >= 4 and tokens[:4] == ( + 'No', + 'feasible', + 'solution', + 'found', + ): soln.status = SolutionStatus.infeasible elif n_tokens > 2 and tokens[:2] == ('Lower', 'bound:'): - if lower_bound is None: # Only use if not already found since this is to less decimal places + if ( + lower_bound is None + ): # Only use if not already found since this is to less decimal places results.problem.lower_bound = _float(tokens[2]) # https://projects.coin-or.org/Cbc/browser/trunk/Cbc/src/CbcSolver.cpp?rev=2497#L7918 elif tokens[0] == 'Gap:': @@ -578,7 +648,9 @@ def process_logfile(self): nodes = int(tokens[2]) # https://projects.coin-or.org/Cbc/browser/trunk/Cbc/src/CbcSolver.cpp?rev=2497#L7926 elif n_tokens > 2 and tokens[:2] == ('Total', 'iterations:'): - results.solver.statistics.black_box.number_of_iterations = int(tokens[2]) + results.solver.statistics.black_box.number_of_iterations = int( + tokens[2] + ) # https://projects.coin-or.org/Cbc/browser/trunk/Cbc/src/CbcSolver.cpp?rev=2497#L7930 elif n_tokens > 3 and tokens[:3] == ('Time', '(CPU', 'seconds):'): results.solver.system_time = _float(tokens[3]) @@ -586,7 +658,12 @@ def process_logfile(self): elif n_tokens > 3 and tokens[:3] == ('Time', '(Wallclock', 'Seconds):'): results.solver.wallclock_time = _float(tokens[3]) # https://projects.coin-or.org/Cbc/browser/trunk/Cbc/src/CbcSolver.cpp?rev=2497#L10477 - elif n_tokens > 4 and tokens[:4] == ('Total', 'time', '(CPU', 'seconds):'): + elif n_tokens > 4 and tokens[:4] == ( + 'Total', + 'time', + '(CPU', + 'seconds):', + ): results.solver.system_time = _float(tokens[4]) if n_tokens > 7 and tokens[5:7] == ('(Wallclock', 'seconds):'): results.solver.wallclock_time = _float(tokens[7]) @@ -597,16 +674,27 @@ def process_logfile(self): # complementarity gap both smallish and small steps" soln.status = SolutionStatus.optimal optim_value = _float(tokens[4]) - elif n_tokens > 5 and tokens[1] == 'objective' and tokens[5] == 'iterations': + elif ( + n_tokens > 5 + and tokens[1] == 'objective' + and tokens[5] == 'iterations' + ): soln.status = SolutionStatus.optimal optim_value = _float(tokens[2]) - results.solver.statistics.black_box.number_of_iterations = int(tokens[4]) + results.solver.statistics.black_box.number_of_iterations = int( + tokens[4] + ) elif tokens[0] == "sys" and n_tokens == 2: results.solver.system_time = _float(tokens[1]) elif tokens[0] == "user" and n_tokens == 2: results.solver.user_time = _float(tokens[1]) - elif n_tokens == 10 and "Presolve" in tokens and \ - "iterations" in tokens and tokens[0] == "Optimal" and "objective" == tokens[1]: + elif ( + n_tokens == 10 + and "Presolve" in tokens + and "iterations" in tokens + and tokens[0] == "Optimal" + and "objective" == tokens[1] + ): soln.status = SolutionStatus.optimal optim_value = _float(tokens[2]) results.solver.user_time = -1.0 # Why is this set to -1? @@ -615,8 +703,10 @@ def process_logfile(self): results.problem.name = 'unknown' if soln.status is SolutionStatus.optimal: - results.solver.termination_message = "Model was solved to optimality (subject to tolerances), and an " \ - "optimal solution is available." + results.solver.termination_message = ( + "Model was solved to optimality (subject to tolerances), and an " + "optimal solution is available." + ) results.solver.termination_condition = TerminationCondition.optimal results.solver.status = SolverStatus.ok if gap is None: @@ -630,28 +720,46 @@ def process_logfile(self): results.solver.termination_message = "Model was proven to be unbounded." results.solver.termination_condition = TerminationCondition.unbounded results.solver.status = SolverStatus.warning - elif results.solver.termination_condition in [TerminationCondition.maxTimeLimit, - TerminationCondition.maxEvaluations, - TerminationCondition.other, - TerminationCondition.maxIterations]: + elif results.solver.termination_condition in [ + TerminationCondition.maxTimeLimit, + TerminationCondition.maxEvaluations, + TerminationCondition.other, + TerminationCondition.maxIterations, + ]: results.solver.status = SolverStatus.aborted soln.status = SolutionStatus.stoppedByLimit - if results.solver.termination_condition == TerminationCondition.maxTimeLimit: - results.solver.termination_message = "Optimization terminated because the time expended " \ - "exceeded the value specified in the seconds " \ - "parameter." - elif results.solver.termination_condition == TerminationCondition.maxEvaluations: - results.solver.termination_message = \ - "Optimization terminated because the total number of branch-and-cut nodes explored " \ + if ( + results.solver.termination_condition + == TerminationCondition.maxTimeLimit + ): + results.solver.termination_message = ( + "Optimization terminated because the time expended " + "exceeded the value specified in the seconds " + "parameter." + ) + elif ( + results.solver.termination_condition + == TerminationCondition.maxEvaluations + ): + results.solver.termination_message = ( + "Optimization terminated because the total number of branch-and-cut nodes explored " "exceeded the value specified in the maxNodes parameter" + ) elif results.solver.termination_condition == TerminationCondition.other: - results.solver.termination_message = "Optimization terminated because the number of " \ - "solutions found reached the value specified in the " \ - "maxSolutions parameter." - elif results.solver.termination_condition == TerminationCondition.maxIterations: - results.solver.termination_message = "Optimization terminated because the total number of simplex " \ - "iterations performed exceeded the value specified in the " \ - "maxIterations parameter." + results.solver.termination_message = ( + "Optimization terminated because the number of " + "solutions found reached the value specified in the " + "maxSolutions parameter." + ) + elif ( + results.solver.termination_condition + == TerminationCondition.maxIterations + ): + results.solver.termination_message = ( + "Optimization terminated because the total number of simplex " + "iterations performed exceeded the value specified in the " + "maxIterations parameter." + ) soln.gap = gap if results.problem.sense == ProblemSense.minimize: upper_bound = optim_value @@ -670,10 +778,12 @@ def process_logfile(self): results.solver.statistics.branch_and_bound.number_of_bounded_subproblems = nodes results.solver.statistics.branch_and_bound.number_of_created_subproblems = nodes - if soln.status in [SolutionStatus.optimal, - SolutionStatus.stoppedByLimit, - SolutionStatus.unknown, - SolutionStatus.other]: + if soln.status in [ + SolutionStatus.optimal, + SolutionStatus.stoppedByLimit, + SolutionStatus.unknown, + SolutionStatus.other, + ]: results.solution.insert(soln) return results @@ -686,15 +796,17 @@ def process_soln_file(self, results): extract_duals = False extract_reduced_costs = False for suffix in self._suffixes: - flag=False + flag = False if re.match(suffix, "dual"): extract_duals = True - flag=True + flag = True if re.match(suffix, "rc"): extract_reduced_costs = True - flag=True + flag = True if not flag: - raise RuntimeError("***CBC solver plugin cannot extract solution suffix="+suffix) + raise RuntimeError( + "***CBC solver plugin cannot extract solution suffix=" + suffix + ) # if dealing with SOL format files, we've already read # this via the base class reader functionality. @@ -709,24 +821,24 @@ def process_soln_file(self, results): results.problem.number_of_objectives = 1 - processing_constraints = None # None means header, True means constraints, False means variables. + processing_constraints = ( + None # None means header, True means constraints, False means variables. + ) header_processed = False optim_value = None try: - INPUT = open(self._soln_file,"r") + INPUT = open(self._soln_file, "r") except IOError: INPUT = [] _ver = self.version() - invert_objective_sense = ( - results.problem.sense == ProblemSense.maximize - and ( _ver and _ver[:3] < (2, 10, 2) ) + invert_objective_sense = results.problem.sense == ProblemSense.maximize and ( + _ver and _ver[:3] < (2, 10, 2) ) - for line in INPUT: - tokens = tuple(re.split('[ \t]+',line.strip())) + tokens = tuple(re.split('[ \t]+', line.strip())) n_tokens = len(tokens) # # These are the only header entries CBC will generate (identified via browsing CbcSolver.cpp) @@ -739,23 +851,40 @@ def process_soln_file(self, results): if tokens[0] == 'Optimal': results.solver.termination_condition = TerminationCondition.optimal results.solver.status = SolverStatus.ok - results.solver.termination_message = "Model was solved to optimality (subject to tolerances), " \ - "and an optimal solution is available." + results.solver.termination_message = ( + "Model was solved to optimality (subject to tolerances), " + "and an optimal solution is available." + ) solution.status = SolutionStatus.optimal optim_value = _float(tokens[-1]) elif tokens[0] in ('Infeasible', 'PrimalInfeasible') or ( - n_tokens > 1 and tokens[0:2] == ('Integer', 'infeasible')): - results.solver.termination_message = "Model was proven to be infeasible." - results.solver.termination_condition = TerminationCondition.infeasible + n_tokens > 1 and tokens[0:2] == ('Integer', 'infeasible') + ): + results.solver.termination_message = ( + "Model was proven to be infeasible." + ) + results.solver.termination_condition = ( + TerminationCondition.infeasible + ) results.solver.status = SolverStatus.warning solution.status = SolutionStatus.infeasible INPUT.close() return - elif tokens[0] == 'Unbounded' or ( - n_tokens > 2 and tokens[0] == 'Problem' and tokens[2] == 'unbounded') or ( - n_tokens > 1 and tokens[0:2] == ('Dual', 'infeasible')): - results.solver.termination_message = "Model was proven to be unbounded." - results.solver.termination_condition = TerminationCondition.unbounded + elif ( + tokens[0] == 'Unbounded' + or ( + n_tokens > 2 + and tokens[0] == 'Problem' + and tokens[2] == 'unbounded' + ) + or (n_tokens > 1 and tokens[0:2] == ('Dual', 'infeasible')) + ): + results.solver.termination_message = ( + "Model was proven to be unbounded." + ) + results.solver.termination_condition = ( + TerminationCondition.unbounded + ) results.solver.status = SolverStatus.warning solution.status = SolutionStatus.unbounded INPUT.close() @@ -766,62 +895,110 @@ def process_soln_file(self, results): results.solver.status = SolverStatus.aborted solution.status = SolutionStatus.stoppedByLimit if tokens[2] == 'time': - results.solver.termination_message = "Optimization terminated because the time expended " \ - "exceeded the value specified in the seconds " \ - "parameter." - results.solver.termination_condition = TerminationCondition.maxTimeLimit + results.solver.termination_message = ( + "Optimization terminated because the time expended " + "exceeded the value specified in the seconds " + "parameter." + ) + results.solver.termination_condition = ( + TerminationCondition.maxTimeLimit + ) elif tokens[2] == 'iterations': # Only add extra info if not already obtained from logs (which give a better description) - if results.solver.termination_condition not in [TerminationCondition.maxEvaluations, - TerminationCondition.other, - TerminationCondition.maxIterations]: - results.solver.termination_message = "Optimization terminated because a limit was hit" - results.solver.termination_condition = TerminationCondition.maxIterations + if results.solver.termination_condition not in [ + TerminationCondition.maxEvaluations, + TerminationCondition.other, + TerminationCondition.maxIterations, + ]: + results.solver.termination_message = ( + "Optimization terminated because a limit was hit" + ) + results.solver.termination_condition = ( + TerminationCondition.maxIterations + ) elif tokens[2] == 'difficulties': - results.solver.termination_condition = TerminationCondition.solverFailure + results.solver.termination_condition = ( + TerminationCondition.solverFailure + ) results.solver.status = SolverStatus.error solution.status = SolutionStatus.error elif tokens[2] == 'ctrl-c': - results.solver.termination_message = "Optimization was terminated by the user." - results.solver.termination_condition = TerminationCondition.userInterrupt + results.solver.termination_message = ( + "Optimization was terminated by the user." + ) + results.solver.termination_condition = ( + TerminationCondition.userInterrupt + ) solution.status = SolutionStatus.unknown else: - results.solver.termination_condition = TerminationCondition.unknown + results.solver.termination_condition = ( + TerminationCondition.unknown + ) results.solver.status = SolverStatus.unknown solution.status = SolutionStatus.unknown results.solver.termination_message = ' '.join(tokens) - print('***WARNING: CBC plugin currently not processing solution status=Stopped correctly. Full ' - 'status line is: {}'.format(line.strip())) - if n_tokens > 8 and tokens[3:9] == ('(no', 'integer', 'solution', '-', 'continuous', 'used)'): - results.solver.termination_message = "Optimization terminated because a limit was hit, " \ - "however it had not found an integer solution yet." - results.solver.termination_condition = TerminationCondition.intermediateNonInteger + print( + '***WARNING: CBC plugin currently not processing solution status=Stopped correctly. Full ' + 'status line is: {}'.format(line.strip()) + ) + if n_tokens > 8 and tokens[3:9] == ( + '(no', + 'integer', + 'solution', + '-', + 'continuous', + 'used)', + ): + results.solver.termination_message = ( + "Optimization terminated because a limit was hit, " + "however it had not found an integer solution yet." + ) + results.solver.termination_condition = ( + TerminationCondition.intermediateNonInteger + ) solution.status = SolutionStatus.other else: results.solver.termination_condition = TerminationCondition.unknown results.solver.status = SolverStatus.unknown solution.status = SolutionStatus.unknown results.solver.termination_message = ' '.join(tokens) - print('***WARNING: CBC plugin currently not processing solution status={} correctly. Full status ' - 'line is: {}'.format(tokens[0], line.strip())) + print( + '***WARNING: CBC plugin currently not processing solution status={} correctly. Full status ' + 'line is: {}'.format(tokens[0], line.strip()) + ) # most of the first tokens should be integers # if it's not an integer, only then check the list of results try: - row_number = int( tokens[0]) - if row_number == 0: # indicates section start. + row_number = int(tokens[0]) + if row_number == 0: # indicates section start. if processing_constraints is None: processing_constraints = True elif processing_constraints is True: processing_constraints = False else: - raise RuntimeError("CBC plugin encountered unexpected line=("+line.strip()+") in solution file="+self._soln_file+"; constraint and variable sections already processed!") + raise RuntimeError( + "CBC plugin encountered unexpected line=(" + + line.strip() + + ") in solution file=" + + self._soln_file + + "; constraint and variable sections already processed!" + ) except ValueError: - if tokens[0] in ("Optimal", "Infeasible", "Unbounded", "Stopped", "Integer", "Status"): + if tokens[0] in ( + "Optimal", + "Infeasible", + "Unbounded", + "Stopped", + "Integer", + "Status", + ): if optim_value is not None: if invert_objective_sense: optim_value *= -1 - solution.objective['__default_objective__'] = {'Value': optim_value} + solution.objective['__default_objective__'] = { + 'Value': optim_value + } header_processed = True if (processing_constraints is True) and (extract_duals is True): @@ -830,29 +1007,40 @@ def process_soln_file(self, results): elif (n_tokens == 5) and tokens[0] == "**": tokens = tokens[1:] else: - raise RuntimeError("Unexpected line format encountered in CBC solution file - line="+line) + raise RuntimeError( + "Unexpected line format encountered in CBC solution file - line=" + + line + ) constraint = tokens[1] - constraint_ax = _float(tokens[2]) # CBC reports the constraint row times the solution vector - not the slack. + constraint_ax = _float( + tokens[2] + ) # CBC reports the constraint row times the solution vector - not the slack. constraint_dual = _float(tokens[3]) if invert_objective_sense: constraint_dual *= -1 if constraint[:2] == 'c_': - solution.constraint[constraint] = {"Dual" : constraint_dual} + solution.constraint[constraint] = {"Dual": constraint_dual} elif constraint[:2] == 'r_': # For the range constraints, supply only the dual with the largest # magnitude (at least one should always be numerically zero) - existing_constraint_dual_dict = solution.constraint.get( 'r_l_' + constraint[4:], None ) + existing_constraint_dual_dict = solution.constraint.get( + 'r_l_' + constraint[4:], None + ) if existing_constraint_dual_dict: # if a constraint dual is already saved, then update it if its # magnitude is larger than existing; this avoids checking vs # zero (avoiding problems with solver tolerances) existing_constraint_dual = existing_constraint_dual_dict["Dual"] - if abs( constraint_dual) > abs(existing_constraint_dual): - solution.constraint[ 'r_l_' + constraint[4:] ] = {"Dual": constraint_dual} + if abs(constraint_dual) > abs(existing_constraint_dual): + solution.constraint['r_l_' + constraint[4:]] = { + "Dual": constraint_dual + } else: # if no constraint with that name yet, just save it in the solution constraint dictionary - solution.constraint[ 'r_l_' + constraint[4:] ] = {"Dual": constraint_dual} + solution.constraint['r_l_' + constraint[4:]] = { + "Dual": constraint_dual + } elif processing_constraints is False: if n_tokens == 4: @@ -860,14 +1048,16 @@ def process_soln_file(self, results): elif (n_tokens == 5) and tokens[0] == "**": tokens = tokens[1:] else: - raise RuntimeError("Unexpected line format encountered " - "in CBC solution file - line="+line) + raise RuntimeError( + "Unexpected line format encountered " + "in CBC solution file - line=" + line + ) variable_name = tokens[1] variable_value = _float(tokens[2]) - variable = solution.variable[variable_name] = {"Value" : variable_value} + variable = solution.variable[variable_name] = {"Value": variable_value} if extract_reduced_costs is True: - variable_reduced_cost = _float(tokens[3]) # currently ignored. + variable_reduced_cost = _float(tokens[3]) # currently ignored. if invert_objective_sense: variable_reduced_cost *= -1 variable["Rc"] = variable_reduced_cost @@ -876,21 +1066,27 @@ def process_soln_file(self, results): pass else: - raise RuntimeError("CBC plugin encountered unexpected " - "line=("+line.strip()+") in solution file=" - +self._soln_file+"; expecting header, but " - "found data!") + raise RuntimeError( + "CBC plugin encountered unexpected " + "line=(" + + line.strip() + + ") in solution file=" + + self._soln_file + + "; expecting header, but " + "found data!" + ) if not type(INPUT) is list: INPUT.close() - if len(results.solution) == 0 and solution.status in [SolutionStatus.optimal, - SolutionStatus.stoppedByLimit, - SolutionStatus.unknown, - SolutionStatus.other]: + if len(results.solution) == 0 and solution.status in [ + SolutionStatus.optimal, + SolutionStatus.stoppedByLimit, + SolutionStatus.unknown, + SolutionStatus.other, + ]: results.solution.insert(solution) - def _postsolve(self): # let the base class deal with returning results. @@ -910,32 +1106,31 @@ def _float(x): @SolverFactory.register('_mock_cbc') -class MockCBC(CBCSHELL,MockMIP): - """A Mock CBC solver used for testing - """ +class MockCBC(CBCSHELL, MockMIP): + """A Mock CBC solver used for testing""" def __init__(self, **kwds): try: - CBCSHELL.__init__(self,**kwds) - except ApplicationError: #pragma:nocover - pass #pragma:nocover - MockMIP.__init__(self,"cbc") + CBCSHELL.__init__(self, **kwds) + except ApplicationError: # pragma:nocover + pass # pragma:nocover + MockMIP.__init__(self, "cbc") def available(self, exception_flag=True): - return CBCSHELL.available(self,exception_flag) + return CBCSHELL.available(self, exception_flag) - def create_command_line(self,executable,problem_files): - command = CBCSHELL.create_command_line(self,executable,problem_files) - MockMIP.create_command_line(self,executable,problem_files) + def create_command_line(self, executable, problem_files): + command = CBCSHELL.create_command_line(self, executable, problem_files) + MockMIP.create_command_line(self, executable, problem_files) return command def executable(self): return MockMIP.executable(self) - def _execute_command(self,cmd): - return MockMIP._execute_command(self,cmd) + def _execute_command(self, cmd): + return MockMIP._execute_command(self, cmd) - def _convert_problem(self,args,pformat,valid_pformats): + def _convert_problem(self, args, pformat, valid_pformats): if pformat in [ProblemFormat.mps, ProblemFormat.cpxlp, ProblemFormat.nl]: return (args, pformat, None) else: diff --git a/pyomo/solvers/plugins/solvers/CONOPT.py b/pyomo/solvers/plugins/solvers/CONOPT.py index ccfddc2fb6e..e11cae942d3 100644 --- a/pyomo/solvers/plugins/solvers/CONOPT.py +++ b/pyomo/solvers/plugins/solvers/CONOPT.py @@ -19,9 +19,10 @@ from pyomo.opt.base import ProblemFormat, ResultsFormat from pyomo.opt.base.solvers import _extract_version, SolverFactory from pyomo.opt.results import SolverStatus -from pyomo.opt.solver import SystemCallSolver +from pyomo.opt.solver import SystemCallSolver import logging + logger = logging.getLogger('pyomo.solvers') @@ -31,7 +32,6 @@ class CONOPT(SystemCallSolver): An interface to the CONOPT optimizer that uses the AMPL Solver Library. """ - def __init__(self, **kwds): # # Call base constructor @@ -42,7 +42,7 @@ def __init__(self, **kwds): # Setup valid problem formats, and valid results for each problem format # Also set the default problem and results formats. # - self._valid_problem_formats=[ProblemFormat.nl] + self._valid_problem_formats = [ProblemFormat.nl] self._valid_result_formats = {} self._valid_result_formats[ProblemFormat.nl] = [ResultsFormat.sol] self.set_problem_format(ProblemFormat.nl) @@ -62,8 +62,10 @@ def _default_results_format(self, prob_format): def _default_executable(self): executable = Executable("conopt") if not executable: - logger.warning("Could not locate the 'conopt' executable, " - "which is required for solver %s" % self.name) + logger.warning( + "Could not locate the 'conopt' executable, " + "which is required for solver %s" % self.name + ) self.enable = False return None return executable.path() @@ -75,23 +77,25 @@ def _get_version(self): solver_exec = self.executable() if solver_exec is None: return _extract_version('') - results = subprocess.run( [solver_exec], timeout=1, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - universal_newlines=True) + results = subprocess.run( + [solver_exec], + timeout=1, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True, + ) return _extract_version(results.stdout) def create_command_line(self, executable, problem_files): - assert(self._problem_format == ProblemFormat.nl) - assert(self._results_format == ResultsFormat.sol) + assert self._problem_format == ProblemFormat.nl + assert self._results_format == ResultsFormat.sol # # Define log file # if self._log_file is None: - self._log_file = TempfileManager.\ - create_tempfile(suffix="_conopt.log") + self._log_file = TempfileManager.create_tempfile(suffix="_conopt.log") fname = problem_files[0] if '.' in fname: @@ -100,7 +104,7 @@ def create_command_line(self, executable, problem_files): fname = '.'.join(tmp[:-1]) else: fname = tmp[0] - self._soln_file = fname+".sol" + self._soln_file = fname + ".sol" # # Define results file (since an external parser is used) @@ -110,7 +114,7 @@ def create_command_line(self, executable, problem_files): # # Define command line # - env=os.environ.copy() + env = os.environ.copy() # # Merge the PYOMO_AMPLFUNC (externals defined within # Pyomo/Pyomo) with any user-specified external function @@ -130,19 +134,19 @@ def create_command_line(self, executable, problem_files): # to the command line. I'm not sure what solvers this method of passing options # through the envstr variable works for, but it does not seem to work for cplex # or gurobi - opt=[] + opt = [] for key in self.options: if key == 'solver': continue if isinstance(self.options[key], str) and ' ' in self.options[key]: - opt.append(key+"=\""+str(self.options[key])+"\"") - cmd.append(str(key)+"="+str(self.options[key])) + opt.append(key + "=\"" + str(self.options[key]) + "\"") + cmd.append(str(key) + "=" + str(self.options[key])) elif key == 'subsolver': - opt.append("solver="+str(self.options[key])) - cmd.append(str(key)+"="+str(self.options[key])) + opt.append("solver=" + str(self.options[key])) + cmd.append(str(key) + "=" + str(self.options[key])) else: - opt.append(key+"="+str(self.options[key])) - cmd.append(str(key)+"="+str(self.options[key])) + opt.append(key + "=" + str(self.options[key])) + cmd.append(str(key) + "=" + str(self.options[key])) envstr = "%s_options" % self.options.solver # Merge with any options coming in through the environment @@ -157,7 +161,6 @@ def _postsolve(self): # For some reason it sets the solver_results_num to # 100 in this case, which is reserved for cases # where "optimal solution indicated, but error likely". - if results.solver.id == 100 and \ - 'Locally optimal' in results.solver.message: + if results.solver.id == 100 and 'Locally optimal' in results.solver.message: results.solver.status = SolverStatus.ok return results diff --git a/pyomo/solvers/plugins/solvers/CPLEX.py b/pyomo/solvers/plugins/solvers/CPLEX.py index ea33fabf2fd..d7078905192 100644 --- a/pyomo/solvers/plugins/solvers/CPLEX.py +++ b/pyomo/solvers/plugins/solvers/CPLEX.py @@ -21,13 +21,15 @@ from pyomo.common.tempfiles import TempfileManager from pyomo.common.collections import ComponentMap, Bunch -from pyomo.opt.base import ( - ProblemFormat, ResultsFormat, OptSolver, BranchDirection, -) +from pyomo.opt.base import ProblemFormat, ResultsFormat, OptSolver, BranchDirection from pyomo.opt.base.solvers import _extract_version, SolverFactory from pyomo.opt.results import ( - SolverResults, SolverStatus, TerminationCondition, SolutionStatus, - ProblemSense, Solution, + SolverResults, + SolverStatus, + TerminationCondition, + SolutionStatus, + ProblemSense, + Solution, ) from pyomo.opt.solver import ILMLicensedSystemCallSolver from pyomo.solvers.mockmip import MockMIP @@ -53,31 +55,39 @@ def _validate_file_name(cplex, filename, description): raise ValueError( "Unallowed character (%s) found in CPLEX %s file path/name.\n\t" "For portability reasons, only [%s] are allowed." - % (matches.group(), description, - _validate_file_name.allowed_characters.replace("\\",''))) + % ( + matches.group(), + description, + _validate_file_name.allowed_characters.replace("\\", ''), + ) + ) # CPLEX only supports quoting spaces starting in v12.8. if ' ' in filename: - if cplex.version()[:2] >= (12,8): - filename = '"'+filename+'"' + if cplex.version()[:2] >= (12, 8): + filename = '"' + filename + '"' else: raise ValueError( "Space detected in CPLEX %s file path/name\n\t%s\nand " "CPLEX older than version 12.8. Please either upgrade " "CPLEX or remove the space from the %s path." - % (description, filename, description)) + % (description, filename, description) + ) return filename + + # The full list of allowed characters, per IBM, is: - # (a-z, A-Z, 0-9) or ! " # $ % & ( ) / , . ; ? @ _ ` ' { } | ~ -_validate_file_name.allowed_characters = \ +# (a-z, A-Z, 0-9) or ! " # $ % & ( ) / , . ; ? @ _ ` ' { } | ~ +_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,)) + '[^%s]' % (_validate_file_name.allowed_characters,) +) @SolverFactory.register('cplex', doc='The CPLEX LP/MIP solver') class CPLEX(OptSolver): - """The CPLEX LP/MIP solver - """ + """The CPLEX LP/MIP solver""" def __new__(cls, *args, **kwds): try: @@ -88,7 +98,7 @@ def __new__(cls, *args, **kwds): except KeyError: mode = 'lp' # - if mode == 'lp': + if mode == 'lp': return SolverFactory('_cplex_shell', **kwds) if mode == 'mps': opt = SolverFactory('_cplex_shell', **kwds) @@ -97,13 +107,17 @@ def __new__(cls, *args, **kwds): if mode in ['python', 'direct']: opt = SolverFactory('cplex_direct', **kwds) if opt is None: - logging.getLogger('pyomo.solvers').error('Python API for CPLEX is not installed') + logging.getLogger('pyomo.solvers').error( + 'Python API for CPLEX is not installed' + ) return return opt if mode == 'persistent': opt = SolverFactory('cplex_persistent', **kwds) if opt is None: - logging.getLogger('pyomo.solvers').error('Python API for CPLEX is not installed') + logging.getLogger('pyomo.solvers').error( + 'Python API for CPLEX is not installed' + ) return return opt # @@ -124,11 +138,7 @@ class ORDFileSchema(object): @classmethod def ROW(cls, name, priority, branch_direction=None): - return " %s %s %s\n" % ( - cls._direction_to_str(branch_direction), - name, - priority, - ) + return " %s %s %s\n" % (cls._direction_to_str(branch_direction), name, priority) @staticmethod def _direction_to_str(branch_direction): @@ -140,10 +150,11 @@ def _direction_to_str(branch_direction): return "" -@SolverFactory.register('_cplex_shell', doc='Shell interface to the CPLEX LP/MIP solver') +@SolverFactory.register( + '_cplex_shell', doc='Shell interface to the CPLEX LP/MIP solver' +) class CPLEXSHELL(ILMLicensedSystemCallSolver): - """Shell interface to the CPLEX LP/MIP solver - """ + """Shell interface to the CPLEX LP/MIP solver""" def __init__(self, **kwds): # @@ -162,8 +173,8 @@ def __init__(self, **kwds): # # Define valid problem formats and associated results formats # - self._valid_problem_formats=[ProblemFormat.cpxlp, ProblemFormat.mps] - self._valid_result_formats={} + self._valid_problem_formats = [ProblemFormat.cpxlp, ProblemFormat.mps] + self._valid_result_formats = {} self._valid_result_formats[ProblemFormat.cpxlp] = [ResultsFormat.soln] self._valid_result_formats[ProblemFormat.mps] = [ResultsFormat.soln] self.set_problem_format(ProblemFormat.cpxlp) @@ -198,8 +209,7 @@ def _warm_start(self, instance): # contains only references to the variables encountered in constraints output_index = 0 if isinstance(instance, IBlock): - smap = getattr(instance,"._symbol_maps")\ - [self._smap_id] + smap = getattr(instance, "._symbol_maps")[self._smap_id] else: smap = instance.solutions.symbol_map[self._smap_id] byObject = smap.byObject @@ -215,12 +225,13 @@ def _warm_start(self, instance): mst_file.write("\n") mst_file.write("\n") for var in instance.component_data_objects(Var): - if (var.value is not None) and \ - (id(var) in byObject): + if (var.value is not None) and (id(var) in byObject): name = byObject[id(var)] - mst_file.write("\n" - % (output_index, name, var.value)) + mst_file.write( + "\n" + % (output_index, name, var.value) + ) output_index = output_index + 1 mst_file.write("\n") @@ -231,7 +242,7 @@ def _warm_start(self, instance): SUFFIX_DIRECTION_NAME = "direction" def _write_priorities_file(self, instance): - """ Write a variable priorities file in the CPLEX ORD format. """ + """Write a variable priorities file in the CPLEX ORD format.""" priorities, directions = self._get_suffixes(instance) rows = self._convert_priorities_to_rows(instance, priorities, directions) self._write_priority_rows(rows) @@ -304,26 +315,26 @@ def _presolve(self, *args, **kwds): # to a file. self._warm_start_solve = kwds.pop('warmstart', False) self._warm_start_file_name = _validate_file_name( - self, kwds.pop('warmstart_file', None), "warm start") + self, kwds.pop('warmstart_file', None), "warm start" + ) user_warmstart = self._warm_start_file_name is not None # the input argument can currently be one of two things: an instance or a filename. # if a filename is provided and a warm-start is indicated, we go ahead and # create the temporary file - assuming that the user has already, via some external # mechanism, invoked warm_start() with a instance to create the warm start file. - if self._warm_start_solve and \ - isinstance(args[0], str): + if self._warm_start_solve and isinstance(args[0], str): # we assume the user knows what they are doing... pass - elif self._warm_start_solve and \ - (not isinstance(args[0], str)): + elif self._warm_start_solve and (not isinstance(args[0], str)): # assign the name of the warm start file *before* calling the base class # presolve - the base class method ends up creating the command line, # and the warm start file-name is (obviously) needed there. if self._warm_start_file_name is None: assert not user_warmstart - self._warm_start_file_name = TempfileManager.\ - create_tempfile(suffix = '.cplex.mst') + self._warm_start_file_name = TempfileManager.create_tempfile( + suffix='.cplex.mst' + ) self._priorities_solve = kwds.pop("priorities", False) self._priorities_file_name = _validate_file_name( @@ -351,8 +362,8 @@ def _presolve(self, *args, **kwds): if len(args) != 1: raise ValueError( "CPLEX _presolve method can only handle a " - "single problem instance - %s were supplied" - % (len(args),)) + "single problem instance - %s were supplied" % (len(args),) + ) # write the warm-start file - currently only supports MIPs. # we only know how to deal with a single problem instance. @@ -362,8 +373,9 @@ def _presolve(self, *args, **kwds): self._warm_start(args[0]) end_time = time.time() if self._report_timing: - print("Warm start write time= %.2f seconds" - % (end_time-start_time)) + print( + "Warm start write time= %.2f seconds" % (end_time - start_time) + ) if self._priorities_solve and (not user_priorities): start_time = time.time() @@ -378,9 +390,10 @@ def _presolve(self, *args, **kwds): def _default_executable(self): executable = Executable("cplex") if not executable: - logger.warning("Could not locate the 'cplex' executable" - ", which is required for solver %s" - % self.name) + logger.warning( + "Could not locate the 'cplex' executable" + ", which is required for solver %s" % self.name + ) self.enable = False return None return executable.path() @@ -392,10 +405,13 @@ def _get_version(self): solver_exec = self.executable() if solver_exec is None: return _extract_version('') - results = subprocess.run( [solver_exec,'-c','quit'], timeout=1, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - universal_newlines=True) + results = subprocess.run( + [solver_exec, '-c', 'quit'], + timeout=1, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True, + ) return _extract_version(results.stdout) def create_command_line(self, executable, problem_files): @@ -405,8 +421,7 @@ def create_command_line(self, executable, problem_files): # The log file in CPLEX contains the solution trace, but the solver status can be found in the solution file. # if self._log_file is None: - self._log_file = TempfileManager.\ - create_tempfile(suffix = '.cplex.log') + self._log_file = TempfileManager.create_tempfile(suffix='.cplex.log') self._log_file = _validate_file_name(self, self._log_file, "log") # @@ -414,8 +429,7 @@ def create_command_line(self, executable, problem_files): # As indicated above, contains (in XML) both the solution and solver status. # if self._soln_file is None: - self._soln_file = TempfileManager.\ - create_tempfile(suffix = '.cplex.sol') + self._soln_file = TempfileManager.create_tempfile(suffix='.cplex.sol') self._soln_file = _validate_file_name(self, self._soln_file, "solution") # @@ -423,28 +437,24 @@ def create_command_line(self, executable, problem_files): # script = 'set logfile %s\n' % (self._log_file,) if self._timelimit is not None and self._timelimit > 0.0: - script += 'set timelimit %s\n' % ( self._timelimit, ) + script += 'set timelimit %s\n' % (self._timelimit,) - if (self.options.mipgap is not None) and \ - (float(self.options.mipgap) > 0.0): - script += ('set mip tolerances mipgap %s\n' - % (self.options.mipgap,)) + if (self.options.mipgap is not None) and (float(self.options.mipgap) > 0.0): + script += 'set mip tolerances mipgap %s\n' % (self.options.mipgap,) for key in self.options: if key == 'relax_integrality' or key == 'mipgap': continue - elif isinstance(self.options[key], str) and \ - (' ' in self.options[key]): - opt = ' '.join(key.split('_'))+' '+str(self.options[key]) + elif isinstance(self.options[key], str) and (' ' in self.options[key]): + opt = ' '.join(key.split('_')) + ' ' + str(self.options[key]) else: - opt = ' '.join(key.split('_'))+' '+str(self.options[key]) - script += 'set %s\n' % ( opt, ) + opt = ' '.join(key.split('_')) + ' ' + str(self.options[key]) + script += 'set %s\n' % (opt,) _lp_file = _validate_file_name(self, problem_files[0], "LP") - script += 'read %s\n' % ( _lp_file, ) + script += 'read %s\n' % (_lp_file,) # if we're dealing with an LP, the MST file will be empty. - if self._warm_start_solve and \ - (self._warm_start_file_name is not None): + if self._warm_start_solve and (self._warm_start_file_name is not None): script += 'read %s\n' % (self._warm_start_file_name,) if self._priorities_solve and self._priorities_file_name is not None: @@ -461,17 +471,14 @@ def create_command_line(self, executable, problem_files): # dump the script and warm-start file names for the # user if we're keeping files around. if self._keepfiles: - script_fname = TempfileManager.\ - create_tempfile(suffix = '.cplex.script') - tmp = open(script_fname,'w') + script_fname = TempfileManager.create_tempfile(suffix='.cplex.script') + tmp = open(script_fname, 'w') tmp.write(script) tmp.close() print("Solver script file=" + script_fname) - if self._warm_start_solve and \ - (self._warm_start_file_name is not None): - print("Solver warm-start file=" - +self._warm_start_file_name) + if self._warm_start_solve and (self._warm_start_file_name is not None): + print("Solver warm-start file=" + self._warm_start_file_name) if self._priorities_solve and self._priorities_file_name is not None: print("Solver priorities file=" + self._priorities_file_name) @@ -482,8 +489,7 @@ def create_command_line(self, executable, problem_files): cmd = [executable] if self._timer: cmd.insert(0, self._timer) - return Bunch(cmd=cmd, script=script, - log_file=self._log_file, env=None) + return Bunch(cmd=cmd, script=script, log_file=self._log_file, env=None) def process_logfile(self): """ @@ -513,17 +519,19 @@ def process_logfile(self): self._gap = None for line in output.split("\n"): - tokens = re.split('[ \t]+',line.strip()) + tokens = re.split('[ \t]+', line.strip()) if len(tokens) > 3 and tokens[0] == "CPLEX" and tokens[1] == "Error": - # IMPT: See below - cplex can generate an error line and then terminate fine, e.g., in CPLEX 12.1. - # To handle these cases, we should be specifying some kind of termination criterion always - # in the course of parsing a log file (we aren't doing so currently - just in some conditions). - results.solver.status=SolverStatus.error + # IMPT: See below - cplex can generate an error line and then terminate fine, e.g., in CPLEX 12.1. + # To handle these cases, we should be specifying some kind of termination criterion always + # in the course of parsing a log file (we aren't doing so currently - just in some conditions). + results.solver.status = SolverStatus.error results.solver.error = " ".join(tokens) elif len(tokens) >= 3 and tokens[0] == "ILOG" and tokens[1] == "CPLEX": cplex_version = tokens[2].rstrip(',') elif len(tokens) >= 3 and tokens[0] == "Variables": - if results.problem.number_of_variables is None: # CPLEX 11.2 and subsequent versions have two Variables sections in the log file output. + if ( + results.problem.number_of_variables is None + ): # CPLEX 11.2 and subsequent versions have two Variables sections in the log file output. results.problem.number_of_variables = int(tokens[2]) # In CPLEX 11 (and presumably before), there was only a single line output to # indicate the constriant count, e.g., "Linear constraints : 16 [Less: 7, Greater: 6, Equal: 3]". @@ -531,32 +539,62 @@ def process_logfile(self): # in that detail), there is another instance of this line prefix in the min/max problem statistics # block - which we don't care about. In this case, the line looks like: "Linear constraints :" and # that's all. - elif len(tokens) >= 4 and tokens[0] == "Linear" and tokens[1] == "constraints": + elif ( + len(tokens) >= 4 + and tokens[0] == "Linear" + and tokens[1] == "constraints" + ): results.problem.number_of_constraints = int(tokens[3]) elif len(tokens) >= 3 and tokens[0] == "Nonzeros": - if results.problem.number_of_nonzeros is None: # CPLEX 11.2 and subsequent has two Nonzeros sections. + if ( + results.problem.number_of_nonzeros is None + ): # CPLEX 11.2 and subsequent has two Nonzeros sections. results.problem.number_of_nonzeros = int(tokens[2]) elif len(tokens) >= 5 and tokens[4] == "MINIMIZE": results.problem.sense = ProblemSense.minimize elif len(tokens) >= 5 and tokens[4] == "MAXIMIZE": results.problem.sense = ProblemSense.maximize - elif len(tokens) >= 4 and tokens[0] == "Solution" and tokens[1] == "time" and tokens[2] == "=": + elif ( + len(tokens) >= 4 + and tokens[0] == "Solution" + and tokens[1] == "time" + and tokens[2] == "=" + ): # technically, I'm not sure if this is CPLEX user time or user+system - CPLEX doesn't appear # to differentiate, and I'm not sure we can always provide a break-down. results.solver.user_time = float(tokens[3]) - elif len(tokens) >= 4 and tokens[0] == "Primal" and tokens[1] == "simplex" and tokens[3] == "Optimal:": + elif ( + len(tokens) >= 4 + and tokens[0] == "Primal" + and tokens[1] == "simplex" + and tokens[3] == "Optimal:" + ): results.solver.termination_condition = TerminationCondition.optimal results.solver.termination_message = ' '.join(tokens) - elif len(tokens) >= 4 and tokens[0] == "Dual" and tokens[1] == "simplex" and tokens[3] == "Optimal:": + elif ( + len(tokens) >= 4 + and tokens[0] == "Dual" + and tokens[1] == "simplex" + and tokens[3] == "Optimal:" + ): results.solver.termination_condition = TerminationCondition.optimal results.solver.termination_message = ' '.join(tokens) - elif len(tokens) >= 4 and tokens[0] == "Barrier" and tokens[2] == "Optimal:": + elif ( + len(tokens) >= 4 and tokens[0] == "Barrier" and tokens[2] == "Optimal:" + ): results.solver.termination_condition = TerminationCondition.optimal results.solver.termination_message = ' '.join(tokens) - elif len(tokens) >= 4 and tokens[0] == "Dual" and tokens[3] == "Infeasible:": + elif ( + len(tokens) >= 4 and tokens[0] == "Dual" and tokens[3] == "Infeasible:" + ): results.solver.termination_condition = TerminationCondition.infeasible results.solver.termination_message = ' '.join(tokens) - elif len(tokens) >= 4 and tokens[0] == "MIP" and tokens[2] == "Integer" and tokens[3] == "infeasible.": + elif ( + len(tokens) >= 4 + and tokens[0] == "MIP" + and tokens[2] == "Integer" + and tokens[3] == "infeasible." + ): # if CPLEX has previously printed an error message, reduce it to a warning - # there is a strong indication it recovered, but we can't be sure. if results.solver.status == SolverStatus.error: @@ -565,18 +603,39 @@ def process_logfile(self): results.solver.status = SolverStatus.ok results.solver.termination_condition = TerminationCondition.infeasible results.solver.termination_message = ' '.join(tokens) - elif len(tokens) >= 10 and tokens[0] == "MIP" and tokens[2] == "Time" and tokens[3] == "limit" and tokens[6] == "feasible:": + elif ( + len(tokens) >= 10 + and tokens[0] == "MIP" + and tokens[2] == "Time" + and tokens[3] == "limit" + and tokens[6] == "feasible:" + ): # handle processing when the time limit has been exceeded, and we have a feasible solution. results.solver.status = SolverStatus.ok results.solver.termination_condition = TerminationCondition.maxTimeLimit results.solver.termination_message = ' '.join(tokens) - elif len(tokens) >= 10 and tokens[0] == "Current" and tokens[1] == "MIP" and tokens[2] == "best" and tokens[3] == "bound": + elif ( + len(tokens) >= 10 + and tokens[0] == "Current" + and tokens[1] == "MIP" + and tokens[2] == "best" + and tokens[3] == "bound" + ): self._best_bound = float(tokens[5]) self._gap = float(tokens[8].rstrip(',')) # for the case below, CPLEX sometimes reports "true" optimal (the first case) # and other times within-tolerance optimal (the second case). - elif (len(tokens) >= 4 and tokens[0] == "MIP" and tokens[2] == "Integer" and tokens[3] == "optimal") or \ - (len(tokens) >= 4 and tokens[0] == "MIP" and tokens[2] == "Integer" and tokens[3] == "optimal,"): + elif ( + len(tokens) >= 4 + and tokens[0] == "MIP" + and tokens[2] == "Integer" + and tokens[3] == "optimal" + ) or ( + len(tokens) >= 4 + and tokens[0] == "MIP" + and tokens[2] == "Integer" + and tokens[3] == "optimal," + ): # if CPLEX has previously printed an error message, reduce it to a warning - # there is a strong indication it recovered, but we can't be sure. if results.solver.status == SolverStatus.error: @@ -585,7 +644,11 @@ def process_logfile(self): results.solver.status = SolverStatus.ok results.solver.termination_condition = TerminationCondition.optimal results.solver.termination_message = ' '.join(tokens) - elif len(tokens) >= 3 and tokens[0] == "Presolve" and tokens[2] == "Infeasible.": + elif ( + len(tokens) >= 3 + and tokens[0] == "Presolve" + and tokens[2] == "Infeasible." + ): # if CPLEX has previously printed an error message, reduce it to a warning - # there is a strong indication it recovered, but we can't be sure. if results.solver.status == SolverStatus.error: @@ -594,19 +657,27 @@ def process_logfile(self): results.solver.status = SolverStatus.ok results.solver.termination_condition = TerminationCondition.infeasible results.solver.termination_message = ' '.join(tokens) - elif ((len(tokens) == 6) and \ - (tokens[2] == "Integer") and \ - (tokens[3] == "infeasible") and \ - (tokens[5] == "unbounded.")) or \ - ((len(tokens) >= 4) and \ - (tokens[0] == "MIP") and \ - (tokens[1] == "-") and \ - (tokens[2] == "Integer") and \ - (tokens[3] == "unbounded:")) or \ - ((len(tokens) >= 5) and \ - (tokens[0] == "Presolve") and \ - (tokens[2] == "Unbounded") and \ - (tokens[4] == "infeasible.")): + elif ( + ( + (len(tokens) == 6) + and (tokens[2] == "Integer") + and (tokens[3] == "infeasible") + and (tokens[5] == "unbounded.") + ) + or ( + (len(tokens) >= 4) + and (tokens[0] == "MIP") + and (tokens[1] == "-") + and (tokens[2] == "Integer") + and (tokens[3] == "unbounded:") + ) + or ( + (len(tokens) >= 5) + and (tokens[0] == "Presolve") + and (tokens[2] == "Unbounded") + and (tokens[4] == "infeasible.") + ) + ): # if CPLEX has previously printed an error message, reduce it to a warning - # there is a strong indication it recovered, but we can't be sure. if results.solver.status == SolverStatus.error: @@ -620,12 +691,14 @@ def process_logfile(self): try: if isinstance(results.solver.termination_message, str): - results.solver.termination_message = results.solver.termination_message.replace(':', '\\x3a') + results.solver.termination_message = ( + results.solver.termination_message.replace(':', '\\x3a') + ) except: pass return results - def process_soln_file(self,results): + def process_soln_file(self, results): # the only suffixes that we extract from CPLEX are # constraint duals, constraint slacks, and variable @@ -639,27 +712,30 @@ def process_soln_file(self,results): extract_lrc = False extract_urc = False for suffix in self._suffixes: - flag=False - if re.match(suffix,"dual"): + flag = False + if re.match(suffix, "dual"): extract_duals = True - flag=True - if re.match(suffix,"slack"): + flag = True + if re.match(suffix, "slack"): extract_slacks = True - flag=True - if re.match(suffix,"rc"): + flag = True + if re.match(suffix, "rc"): extract_reduced_costs = True extract_rc = True - flag=True - if re.match(suffix,"lrc"): + flag = True + if re.match(suffix, "lrc"): extract_reduced_costs = True extract_lrc = True - flag=True - if re.match(suffix,"urc"): + flag = True + if re.match(suffix, "urc"): extract_reduced_costs = True extract_urc = True - flag=True + flag = True if not flag: - raise RuntimeError("***The CPLEX solver plugin cannot extract solution suffix="+suffix) + raise RuntimeError( + "***The CPLEX solver plugin cannot extract solution suffix=" + + suffix + ) # check for existence of the solution file # not sure why we just return - would think that we @@ -670,43 +746,49 @@ def process_soln_file(self,results): range_duals = {} range_slacks = {} soln = Solution() - soln.objective['__default_objective__'] = {'Value':None} + soln.objective['__default_objective__'] = {'Value': None} # caching for efficiency soln_variables = soln.variable soln_constraints = soln.constraint INPUT = open(self._soln_file, "r") - results.problem.number_of_objectives=1 + results.problem.number_of_objectives = 1 time_limit_exceeded = False - mip_problem=False + mip_problem = False for line in INPUT: line = line.strip() line = line.lstrip('?') - tokens=line.split(' ') + tokens = line.split(' ') if tokens[0] == "variable": variable_name = None variable_value = None variable_reduced_cost = None variable_status = None - for i in range(1,len(tokens)): - field_name = tokens[i].split('=')[0] + for i in range(1, len(tokens)): + field_name = tokens[i].split('=')[0] field_value = tokens[i].split('=')[1].lstrip("\"").rstrip("\"") if field_name == "name": variable_name = field_value elif field_name == "value": variable_value = field_value - elif (extract_reduced_costs is True) and (field_name == "reducedCost"): + elif (extract_reduced_costs is True) and ( + field_name == "reducedCost" + ): variable_reduced_cost = field_value elif (extract_reduced_costs is True) and (field_name == "status"): variable_status = field_value # skip the "constant-one" variable, used to capture/retain objective offsets in the CPLEX LP format. if variable_name != "ONE_VAR_CONSTANT": - variable = soln_variables[variable_name] = {"Value" : float(variable_value)} - if (variable_reduced_cost is not None) and (extract_reduced_costs is True): + variable = soln_variables[variable_name] = { + "Value": float(variable_value) + } + if (variable_reduced_cost is not None) and ( + extract_reduced_costs is True + ): try: if extract_rc is True: variable["Rc"] = float(variable_reduced_cost) @@ -722,13 +804,20 @@ def process_soln_file(self,results): else: variable["Urc"] = 0.0 except: - raise ValueError("Unexpected reduced-cost value="+str(variable_reduced_cost)+" encountered for variable="+variable_name) - elif (tokens[0] == "constraint") and ((extract_duals is True) or (extract_slacks is True)): + raise ValueError( + "Unexpected reduced-cost value=" + + str(variable_reduced_cost) + + " encountered for variable=" + + variable_name + ) + elif (tokens[0] == "constraint") and ( + (extract_duals is True) or (extract_slacks is True) + ): is_range = False rlabel = None rkey = None - for i in range(1,len(tokens)): - field_name = tokens[i].split('=')[0] + for i in range(1, len(tokens)): + field_name = tokens[i].split('=')[0] field_value = tokens[i].split('=')[1].lstrip("\"").rstrip("\"") if field_name == "name": if field_value.startswith('c_'): @@ -741,80 +830,105 @@ def process_soln_file(self,results): is_range = True rlabel = field_value[4:] rkey = 1 - elif (extract_duals is True) and (field_name == "dual"): # for LPs + elif (extract_duals is True) and (field_name == "dual"): # for LPs if is_range is False: constraint["Dual"] = float(field_value) else: - range_duals.setdefault(rlabel,[0,0])[rkey] = float(field_value) - elif (extract_slacks is True) and (field_name == "slack"): # for MIPs + range_duals.setdefault(rlabel, [0, 0])[rkey] = float( + field_value + ) + elif (extract_slacks is True) and ( + field_name == "slack" + ): # for MIPs if is_range is False: constraint["Slack"] = float(field_value) else: - range_slacks.setdefault(rlabel,[0,0])[rkey] = float(field_value) + range_slacks.setdefault(rlabel, [0, 0])[rkey] = float( + field_value + ) elif tokens[0].startswith("problemName"): filename = (tokens[0].split('=')[1].strip()).lstrip("\"").rstrip("\"") results.problem.name = os.path.basename(filename) if '.' in results.problem.name: results.problem.name = results.problem.name.split('.')[0] - tINPUT=open(filename,"r") + tINPUT = open(filename, "r") for tline in tINPUT: tline = tline.strip() if tline == "": continue - tokens = re.split('[\t ]+',tline) + tokens = re.split('[\t ]+', tline) if tokens[0][0] in ['\\', '*']: continue elif tokens[0] == "NAME": results.problem.name = tokens[1] else: sense = tokens[0].lower() - if sense in ['max','maximize']: + if sense in ['max', 'maximize']: results.problem.sense = ProblemSense.maximize - if sense in ['min','minimize']: + if sense in ['min', 'minimize']: results.problem.sense = ProblemSense.minimize break tINPUT.close() - elif tokens[0].startswith("objectiveValue") and tokens[0] != 'objectiveValues': + elif ( + tokens[0].startswith("objectiveValue") + and tokens[0] != 'objectiveValues' + ): # prior to 12.10.0, the objective value came back as an # attribute on the
tag - objective_value = (tokens[0].split('=')[1].strip()).lstrip("\"").rstrip("\"") - soln.objective['__default_objective__']['Value'] = float(objective_value) + objective_value = ( + (tokens[0].split('=')[1].strip()).lstrip("\"").rstrip("\"") + ) + soln.objective['__default_objective__']['Value'] = float( + objective_value + ) elif tokens[0] == "objective": # beginning in 12.10.0, CPLEX supports multiple # objectives in an tag fields = {} for field in tokens[1:]: - k,v = field.split('=') + k, v = field.split('=') fields[k] = v.strip('"') - soln.objective.setdefault(fields['name'], {})['Value'] = float(fields['value']) + soln.objective.setdefault(fields['name'], {})['Value'] = float( + fields['value'] + ) elif tokens[0].startswith("solutionStatusValue"): - pieces = tokens[0].split("=") - solution_status = eval(pieces[1]) - # solution status = 1 => optimal - # solution status = 3 => infeasible - if soln.status == SolutionStatus.unknown: - if solution_status == 1: - soln.status = SolutionStatus.optimal - elif solution_status == 3: - soln.status = SolutionStatus.infeasible - soln.gap = None - else: - # we are flagging anything with a solution status >= 4 as an error, to possibly - # be over-ridden as we learn more about the status (e.g., due to time limit exceeded). - soln.status = SolutionStatus.error - soln.gap = None + pieces = tokens[0].split("=") + solution_status = eval(pieces[1]) + # solution status = 1 => optimal + # solution status = 3 => infeasible + if soln.status == SolutionStatus.unknown: + if solution_status == 1: + soln.status = SolutionStatus.optimal + elif solution_status == 3: + soln.status = SolutionStatus.infeasible + soln.gap = None + else: + # we are flagging anything with a solution status >= 4 as an error, to possibly + # be over-ridden as we learn more about the status (e.g., due to time limit exceeded). + soln.status = SolutionStatus.error + soln.gap = None elif tokens[0].startswith("solutionStatusString"): - solution_status = ((" ".join(tokens).split('=')[1]).strip()).lstrip("\"").rstrip("\"") - if solution_status in ["optimal", "integer optimal solution", "integer optimal, tolerance"]: + solution_status = ( + ((" ".join(tokens).split('=')[1]).strip()).lstrip("\"").rstrip("\"") + ) + if solution_status in [ + "optimal", + "integer optimal solution", + "integer optimal, tolerance", + ]: soln.status = SolutionStatus.optimal soln.gap = 0.0 - results.problem.lower_bound = soln.objective['__default_objective__']['Value'] - results.problem.upper_bound = soln.objective['__default_objective__']['Value'] + results.problem.lower_bound = soln.objective[ + '__default_objective__' + ]['Value'] + results.problem.upper_bound = soln.objective[ + '__default_objective__' + ]['Value'] if "integer" in solution_status: - mip_problem=True + mip_problem = True elif solution_status in ["infeasible"]: soln.status = SolutionStatus.infeasible soln.gap = None @@ -824,21 +938,36 @@ def process_soln_file(self,results): time_limit_exceeded = True elif tokens[0].startswith("MIPNodes"): if mip_problem: - n = eval(eval((" ".join(tokens).split('=')[1]).strip()).lstrip("\"").rstrip("\"")) - results.solver.statistics.branch_and_bound.number_of_created_subproblems=n - results.solver.statistics.branch_and_bound.number_of_bounded_subproblems=n - elif tokens[0].startswith("primalFeasible") and (time_limit_exceeded is True): - primal_feasible = int(((" ".join(tokens).split('=')[1]).strip()).lstrip("\"").rstrip("\"")) + n = eval( + eval((" ".join(tokens).split('=')[1]).strip()) + .lstrip("\"") + .rstrip("\"") + ) + results.solver.statistics.branch_and_bound.number_of_created_subproblems = ( + n + ) + results.solver.statistics.branch_and_bound.number_of_bounded_subproblems = ( + n + ) + elif tokens[0].startswith("primalFeasible") and ( + time_limit_exceeded is True + ): + primal_feasible = int( + ((" ".join(tokens).split('=')[1]).strip()).lstrip("\"").rstrip("\"") + ) if primal_feasible == 1: soln.status = SolutionStatus.feasible - if (results.problem.sense == ProblemSense.minimize): - results.problem.upper_bound = soln.objective['__default_objective__']['Value'] + if results.problem.sense == ProblemSense.minimize: + results.problem.upper_bound = soln.objective[ + '__default_objective__' + ]['Value'] else: - results.problem.lower_bound = soln.objective['__default_objective__']['Value'] + results.problem.lower_bound = soln.objective[ + '__default_objective__' + ]['Value'] else: soln.status = SolutionStatus.infeasible - if self._best_bound is not None: if results.problem.sense == ProblemSense.minimize: results.problem.lower_bound = self._best_bound @@ -849,32 +978,37 @@ def process_soln_file(self,results): # For the range constraints, supply only the dual with the largest # magnitude (at least one should always be numerically zero) - for key,(ld,ud) in range_duals.items(): + for key, (ld, ud) in range_duals.items(): if abs(ld) > abs(ud): - soln_constraints['r_l_'+key] = {"Dual" : ld} + soln_constraints['r_l_' + key] = {"Dual": ld} else: - soln_constraints['r_l_'+key] = {"Dual" : ud} # Use the same key + soln_constraints['r_l_' + key] = {"Dual": ud} # Use the same key # slacks - for key,(ls,us) in range_slacks.items(): + for key, (ls, us) in range_slacks.items(): if abs(ls) > abs(us): - soln_constraints.setdefault('r_l_'+key,{})["Slack"] = ls + soln_constraints.setdefault('r_l_' + key, {})["Slack"] = ls else: - soln_constraints.setdefault('r_l_'+key,{})["Slack"] = us # Use the same key + soln_constraints.setdefault('r_l_' + key, {})[ + "Slack" + ] = us # Use the same key if not results.solver.status is SolverStatus.error: - if results.solver.termination_condition in [TerminationCondition.unknown, - #TerminationCondition.maxIterations, - #TerminationCondition.minFunctionValue, - #TerminationCondition.minStepLength, - TerminationCondition.globallyOptimal, - TerminationCondition.locallyOptimal, - TerminationCondition.optimal, - #TerminationCondition.maxEvaluations, - TerminationCondition.other]: + if results.solver.termination_condition in [ + TerminationCondition.unknown, + # TerminationCondition.maxIterations, + # TerminationCondition.minFunctionValue, + # TerminationCondition.minStepLength, + TerminationCondition.globallyOptimal, + TerminationCondition.locallyOptimal, + TerminationCondition.optimal, + # TerminationCondition.maxEvaluations, + TerminationCondition.other, + ]: results.solution.insert(soln) - elif (results.solver.termination_condition is \ - TerminationCondition.maxTimeLimit) and \ - (soln.status is not SolutionStatus.infeasible): + elif ( + results.solver.termination_condition + is TerminationCondition.maxTimeLimit + ) and (soln.status is not SolutionStatus.infeasible): results.solution.insert(soln) INPUT.close() @@ -894,7 +1028,7 @@ def _postsolve(self): # these logs don't matter anyway (we redirect everything), # and are largely an annoyance. try: - if re.match(r'cplex\.log', filename) != None: + if re.match(r'cplex\.log', filename) != None: os.remove(filename) elif re.match(r'clone\d+\.log', filename) != None: os.remove(filename) @@ -914,27 +1048,22 @@ def _postsolve(self): @SolverFactory.register('_mock_cplex') -class MockCPLEX(CPLEXSHELL,MockMIP): - """A Mock CPLEX solver used for testing - """ +class MockCPLEX(CPLEXSHELL, MockMIP): + """A Mock CPLEX solver used for testing""" def __init__(self, **kwds): try: CPLEXSHELL.__init__(self, **kwds) - except ApplicationError: #pragma:nocover - pass #pragma:nocover - MockMIP.__init__(self,"cplex") + except ApplicationError: # pragma:nocover + pass # pragma:nocover + MockMIP.__init__(self, "cplex") def available(self, exception_flag=True): - return CPLEXSHELL.available(self,exception_flag) + return CPLEXSHELL.available(self, exception_flag) def create_command_line(self, executable, problem_files): - command = CPLEXSHELL.create_command_line(self, - executable, - problem_files) - MockMIP.create_command_line(self, - executable, - problem_files) + command = CPLEXSHELL.create_command_line(self, executable, problem_files) + MockMIP.create_command_line(self, executable, problem_files) return command def _default_executable(self): @@ -942,5 +1071,3 @@ def _default_executable(self): def _execute_command(self, cmd): return MockMIP._execute_command(self, cmd) - - diff --git a/pyomo/solvers/plugins/solvers/GAMS.py b/pyomo/solvers/plugins/solvers/GAMS.py index cb4317bff5d..a5cc27635da 100644 --- a/pyomo/solvers/plugins/solvers/GAMS.py +++ b/pyomo/solvers/plugins/solvers/GAMS.py @@ -30,14 +30,22 @@ import pyomo.core.base.suffix import pyomo.core.kernel.suffix -from pyomo.opt.results import (SolverResults, SolverStatus, Solution, - SolutionStatus, TerminationCondition, ProblemSense) +from pyomo.opt.results import ( + SolverResults, + SolverStatus, + Solution, + SolutionStatus, + TerminationCondition, + ProblemSense, +) from pyomo.common.dependencies import attempt_import + gdxcc, gdxcc_available = attempt_import('gdxcc', defer_check=True) logger = logging.getLogger('pyomo.solvers') + class _GAMSSolver(object): """Aggregate of common methods for GAMS interfaces""" @@ -89,17 +97,17 @@ def _options_string_to_dict(istr): index = token.find('=') if index == -1: raise ValueError( - "Solver options must have the form option=value: '%s'" % istr) + "Solver options must have the form option=value: '%s'" % istr + ) try: - val = eval(token[(index+1):]) + val = eval(token[(index + 1) :]) except: - val = token[(index+1):] + val = token[(index + 1) :] ans[token[:index]] = val return ans def _simple_model(self, n): - return \ - """ + return """ option limrow = 0; option limcol = 0; option solprint = off; @@ -110,7 +118,9 @@ def _simple_model(self, n): obj.. ans =g= sum(I, x(I)); model test / all /; solve test using lp minimizing ans; - """ % (n,) + """ % ( + n, + ) # # Support "with" statements. @@ -122,7 +132,6 @@ def __exit__(self, t, v, traceback): pass - @SolverFactory.register('gams', doc='The GAMS modeling language') class GAMSSolver(_GAMSSolver): """ @@ -134,6 +143,7 @@ class GAMSSolver(_GAMSSolver): solver_io='shell' or 'gms' to use command line to call gams Requires the gams executable be on your system PATH. """ + def __new__(cls, *args, **kwds): mode = kwds.pop('solver_io', 'shell') if mode is None: @@ -148,8 +158,9 @@ def __new__(cls, *args, **kwds): return -@SolverFactory.register('_gams_direct', - doc='Direct python interface to the GAMS modeling language') +@SolverFactory.register( + '_gams_direct', doc='Direct python interface to the GAMS modeling language' +) class GAMSDirect(_GAMSSolver): """ A generic python interface to GAMS solvers. @@ -164,14 +175,17 @@ def available(self, exception_flag=True): except ImportError as e: if not exception_flag: return False - raise ImportError("Import of gams failed - GAMS direct " - "solver functionality is not available.\n" - "GAMS message: %s" % (e,)) + raise ImportError( + "Import of gams failed - GAMS direct " + "solver functionality is not available.\n" + "GAMS message: %s" % (e,) + ) avail = self._run_simple_model(1) if not avail and exception_flag: raise NameError( "'gams' command failed to solve a simple model - " - "GAMS shell solver functionality is not available.") + "GAMS shell solver functionality is not available." + ) return avail def license_is_valid(self): @@ -183,9 +197,10 @@ def _get_version(self): if not self.available(exception_flag=False): return _extract_version('') from gams import GamsWorkspace + ws = GamsWorkspace() version = tuple(int(i) for i in ws._version.split('.')[:4]) - while(len(version) < 4): + while len(version) < 4: version += (0,) return version @@ -193,8 +208,8 @@ def _run_simple_model(self, n): tmpdir = mkdtemp() try: from gams import GamsWorkspace, DebugLevel - ws = GamsWorkspace(debug=DebugLevel.Off, - working_directory=tmpdir) + + ws = GamsWorkspace(debug=DebugLevel.Off, working_directory=tmpdir) t1 = ws.add_job_from_string(self._simple_model(n)) t1.run() return True @@ -238,8 +253,9 @@ def solve(self, *args, **kwds): from gams.workspace import GamsExceptionExecution if len(args) != 1: - raise ValueError('Exactly one model must be passed ' - 'to solve method of GAMSSolver.') + raise ValueError( + 'Exactly one model must be passed ' 'to solve method of GAMSSolver.' + ) model = args[0] # self.options are default for each run, overwritten by kwds @@ -248,12 +264,12 @@ def solve(self, *args, **kwds): options.update(kwds) load_solutions = options.pop("load_solutions", True) - tee = options.pop("tee", False) - logfile = options.pop("logfile", None) - keepfiles = options.pop("keepfiles", False) - tmpdir = options.pop("tmpdir", None) - report_timing = options.pop("report_timing", False) - io_options = options.pop("io_options", {}) + tee = options.pop("tee", False) + logfile = options.pop("logfile", None) + keepfiles = options.pop("keepfiles", False) + tmpdir = options.pop("tmpdir", None) + report_timing = options.pop("report_timing", False) + io_options = options.pop("io_options", {}) # Pass remaining keywords to writer, which will handle # any unrecognized arguments @@ -277,21 +293,25 @@ def solve(self, *args, **kwds): output_file = StringIO() if isinstance(model, IBlock): # Kernel blocks have slightly different write method - smap_id = model.write(filename=output_file, - format=ProblemFormat.gams, - _called_by_solver=True, - **io_options) + smap_id = model.write( + filename=output_file, + format=ProblemFormat.gams, + _called_by_solver=True, + **io_options + ) symbolMap = getattr(model, "._symbol_maps")[smap_id] else: - (_, smap_id) = model.write(filename=output_file, - format=ProblemFormat.gams, - io_options=io_options) + (_, smap_id) = model.write( + filename=output_file, format=ProblemFormat.gams, io_options=io_options + ) symbolMap = model.solutions.symbol_map[smap_id] presolve_completion_time = time.time() if report_timing: - print(" %6.2f seconds required for presolve" % - (presolve_completion_time - initial_time)) + print( + " %6.2f seconds required for presolve" + % (presolve_completion_time - initial_time) + ) #################################################################### # Apply solver @@ -306,9 +326,10 @@ def solve(self, *args, **kwds): if tmpdir is not None and os.path.exists(tmpdir): newdir = False - ws = GamsWorkspace(debug=DebugLevel.KeepFiles if keepfiles - else DebugLevel.Off, - working_directory=tmpdir) + ws = GamsWorkspace( + debug=DebugLevel.KeepFiles if keepfiles else DebugLevel.Off, + working_directory=tmpdir, + ) t1 = ws.add_job_from_string(output_file.getvalue()) @@ -324,8 +345,7 @@ def solve(self, *args, **kwds): # Always name working directory or delete files, # regardless of any errors. if keepfiles: - print("\nGAMS WORKING DIRECTORY: %s\n" % - ws.working_directory) + print("\nGAMS WORKING DIRECTORY: %s\n" % ws.working_directory) elif tmpdir is not None: # Garbage collect all references to t1.out_db # So that .gdx file can be deleted @@ -345,8 +365,10 @@ def solve(self, *args, **kwds): solve_completion_time = time.time() if report_timing: - print(" %6.2f seconds required for solver" % - (solve_completion_time - presolve_completion_time)) + print( + " %6.2f seconds required for solver" + % (solve_completion_time - presolve_completion_time) + ) #################################################################### # Postsolve @@ -354,36 +376,40 @@ def solve(self, *args, **kwds): # import suffixes must be on the top-level model if isinstance(model, IBlock): - model_suffixes = list(comp.storage_key for comp \ - in pyomo.core.kernel.suffix.\ - import_suffix_generator(model, - active=True, - descend_into=False)) + model_suffixes = list( + comp.storage_key + for comp in pyomo.core.kernel.suffix.import_suffix_generator( + model, active=True, descend_into=False + ) + ) else: - model_suffixes = list(name for (name,comp) \ - in pyomo.core.base.suffix.\ - active_import_suffix_generator(model)) - extract_dual = ('dual' in model_suffixes) - extract_rc = ('rc' in model_suffixes) + model_suffixes = list( + name + for ( + name, + comp, + ) in pyomo.core.base.suffix.active_import_suffix_generator(model) + ) + extract_dual = 'dual' in model_suffixes + extract_rc = 'rc' in model_suffixes results = SolverResults() results.problem.name = os.path.join(ws.working_directory, t1.name + '.gms') results.problem.lower_bound = t1.out_db["OBJEST"].find_record().value results.problem.upper_bound = t1.out_db["OBJEST"].find_record().value - results.problem.number_of_variables = \ - t1.out_db["NUMVAR"].find_record().value - results.problem.number_of_constraints = \ - t1.out_db["NUMEQU"].find_record().value - results.problem.number_of_nonzeros = \ - t1.out_db["NUMNZ"].find_record().value + results.problem.number_of_variables = t1.out_db["NUMVAR"].find_record().value + results.problem.number_of_constraints = t1.out_db["NUMEQU"].find_record().value + results.problem.number_of_nonzeros = t1.out_db["NUMNZ"].find_record().value results.problem.number_of_binary_variables = None # Includes binary vars: - results.problem.number_of_integer_variables = \ + results.problem.number_of_integer_variables = ( t1.out_db["NUMDVAR"].find_record().value - results.problem.number_of_continuous_variables = \ - t1.out_db["NUMVAR"].find_record().value \ + ) + results.problem.number_of_continuous_variables = ( + t1.out_db["NUMVAR"].find_record().value - t1.out_db["NUMDVAR"].find_record().value - results.problem.number_of_objectives = 1 # required by GAMS writer + ) + results.problem.number_of_objectives = 1 # required by GAMS writer obj = list(model.component_data_objects(Objective, active=True)) assert len(obj) == 1, 'Only one objective is allowed.' obj = obj[0] @@ -416,7 +442,9 @@ def solve(self, *args, **kwds): results.solver.termination_condition = TerminationCondition.maxEvaluations elif solvestat == 7: results.solver.status = SolverStatus.aborted - results.solver.termination_condition = TerminationCondition.licensingProblems + results.solver.termination_condition = ( + TerminationCondition.licensingProblems + ) elif solvestat == 8: results.solver.status = SolverStatus.aborted results.solver.termination_condition = TerminationCondition.userInterrupt @@ -425,7 +453,9 @@ def solve(self, *args, **kwds): results.solver.termination_condition = TerminationCondition.solverFailure elif solvestat == 11: results.solver.status = SolverStatus.error - results.solver.termination_condition = TerminationCondition.internalSolverError + results.solver.termination_condition = ( + TerminationCondition.internalSolverError + ) elif solvestat == 4: results.solver.status = SolverStatus.warning results.solver.message = "Solver quit with a problem (see LST file)" @@ -465,13 +495,17 @@ def solve(self, *args, **kwds): results.solver.termination_condition = TerminationCondition.optimal soln.status = SolutionStatus.optimal elif modelstat == 9: - results.solver.termination_condition = TerminationCondition.intermediateNonInteger + results.solver.termination_condition = ( + TerminationCondition.intermediateNonInteger + ) soln.status = SolutionStatus.other elif modelstat == 11: # Should be handled above, if modelstat and solvestat both # indicate a licensing problem if results.solver.termination_condition is None: - results.solver.termination_condition = TerminationCondition.licensingProblems + results.solver.termination_condition = ( + TerminationCondition.licensingProblems + ) soln.status = SolutionStatus.error elif modelstat in [12, 13]: if results.solver.termination_condition is None: @@ -490,8 +524,7 @@ def solve(self, *args, **kwds): # This is just a backup catch, all cases are handled above soln.status = SolutionStatus.error - soln.gap = abs(results.problem.upper_bound \ - - results.problem.lower_bound) + soln.gap = abs(results.problem.upper_bound - results.problem.lower_bound) for sym, ref in symbolMap.bySymbol.items(): obj = ref() @@ -516,8 +549,7 @@ def solve(self, *args, **kwds): if extract_dual: for c in model.component_data_objects(Constraint, active=True): - if c.body.is_fixed() or \ - (not (c.has_lb() or c.has_ub())): + if c.body.is_fixed() or (not (c.has_lb() or c.has_ub())): # the constraint was not sent to GAMS continue sym = symbolMap.getSymbol(c) @@ -567,10 +599,12 @@ def solve(self, *args, **kwds): results._smap = None if isinstance(model, IBlock): if len(results.solution) == 1: - results.solution(0).symbol_map = \ - getattr(model, "._symbol_maps")[results._smap_id] - results.solution(0).default_variable_value = \ - self._default_variable_value + results.solution(0).symbol_map = getattr(model, "._symbol_maps")[ + results._smap_id + ] + results.solution( + 0 + ).default_variable_value = self._default_variable_value if load_solutions: model.load_solution(results.solution(0)) else: @@ -581,8 +615,7 @@ def solve(self, *args, **kwds): assert len(getattr(model, "._symbol_maps")) == 1 delattr(model, "._symbol_maps") del results._smap_id - if load_solutions and \ - (len(results.solution) == 0): + if load_solutions and (len(results.solution) == 0): logger.error("No solution is available") else: if load_solutions: @@ -595,16 +628,21 @@ def solve(self, *args, **kwds): postsolve_completion_time = time.time() if report_timing: - print(" %6.2f seconds required for postsolve" % - (postsolve_completion_time - solve_completion_time)) - print(" %6.2f seconds required total" % - (postsolve_completion_time - initial_time)) + print( + " %6.2f seconds required for postsolve" + % (postsolve_completion_time - solve_completion_time) + ) + print( + " %6.2f seconds required total" + % (postsolve_completion_time - initial_time) + ) return results -@SolverFactory.register('_gams_shell', - doc='Shell interface to the GAMS modeling language') +@SolverFactory.register( + '_gams_shell', doc='Shell interface to the GAMS modeling language' +) class GAMSShell(_GAMSSolver): """A generic shell interface to GAMS solvers.""" @@ -616,7 +654,8 @@ def available(self, exception_flag=True): return False raise NameError( "No 'gams' command found on system PATH - GAMS shell " - "solver functionality is not available.") + "solver functionality is not available." + ) # New versions of GAMS require a license to run anything. # Instead of parsing the output, we will try solving a trivial # model. @@ -624,7 +663,8 @@ def available(self, exception_flag=True): if not avail and exception_flag: raise NameError( "'gams' command failed to solve a simple model - " - "GAMS shell solver functionality is not available.") + "GAMS shell solver functionality is not available." + ) return avail def license_is_valid(self): @@ -643,7 +683,8 @@ def _run_simple_model(self, n): result = subprocess.run( [solver_exec, test, "curdir=" + tmpdir, 'lo=0'], stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL) + stderr=subprocess.DEVNULL, + ) return not result.returncode finally: shutil.rmtree(tmpdir) @@ -652,8 +693,10 @@ def _run_simple_model(self, n): def _default_executable(self): executable = pyomo.common.Executable("gams") if not executable: - logger.warning("Could not locate the 'gams' executable, " - "which is required for solver gams") + logger.warning( + "Could not locate the 'gams' executable, " + "which is required for solver gams" + ) self.enable = False return None return executable.path() @@ -671,9 +714,12 @@ def _get_version(self): else: # specify logging to stdout for windows compatibility cmd = [solver_exec, "audit", "lo=3"] - results = subprocess.run(cmd, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - universal_newlines=True) + results = subprocess.run( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True, + ) return _extract_version(results.stdout) @staticmethod @@ -721,8 +767,9 @@ def solve(self, *args, **kwds): self.available() if len(args) != 1: - raise ValueError('Exactly one model must be passed ' - 'to solve method of GAMSSolver.') + raise ValueError( + 'Exactly one model must be passed ' 'to solve method of GAMSSolver.' + ) model = args[0] # self.options are default for each run, overwritten by kwds @@ -731,12 +778,12 @@ def solve(self, *args, **kwds): options.update(kwds) load_solutions = options.pop("load_solutions", True) - tee = options.pop("tee", False) - logfile = options.pop("logfile", None) - keepfiles = options.pop("keepfiles", False) - tmpdir = options.pop("tmpdir", None) - report_timing = options.pop("report_timing", False) - io_options = options.pop("io_options", {}) + tee = options.pop("tee", False) + logfile = options.pop("logfile", None) + keepfiles = options.pop("keepfiles", False) + tmpdir = options.pop("tmpdir", None) + report_timing = options.pop("report_timing", False) + io_options = options.pop("io_options", {}) io_options.update(options) @@ -778,37 +825,38 @@ def solve(self, *args, **kwds): put_results = "results" io_options["put_results"] = put_results - io_options.setdefault("put_results_format", - 'gdx' if gdxcc_available else 'dat') + io_options.setdefault("put_results_format", 'gdx' if gdxcc_available else 'dat') if io_options['put_results_format'] == 'gdx': - results_filename = os.path.join( - tmpdir, "GAMS_MODEL_p.gdx") - statresults_filename = os.path.join( - tmpdir, "%s_s.gdx" % (put_results,)) + results_filename = os.path.join(tmpdir, "GAMS_MODEL_p.gdx") + statresults_filename = os.path.join(tmpdir, "%s_s.gdx" % (put_results,)) else: - results_filename = os.path.join( - tmpdir, "%s.dat" % (put_results,)) - statresults_filename = os.path.join( - tmpdir, "%sstat.dat" % (put_results,)) + results_filename = os.path.join(tmpdir, "%s.dat" % (put_results,)) + statresults_filename = os.path.join(tmpdir, "%sstat.dat" % (put_results,)) if isinstance(model, IBlock): # Kernel blocks have slightly different write method - smap_id = model.write(filename=output_filename, - format=ProblemFormat.gams, - _called_by_solver=True, - **io_options) + smap_id = model.write( + filename=output_filename, + format=ProblemFormat.gams, + _called_by_solver=True, + **io_options + ) symbolMap = getattr(model, "._symbol_maps")[smap_id] else: - (_, smap_id) = model.write(filename=output_filename, - format=ProblemFormat.gams, - io_options=io_options) + (_, smap_id) = model.write( + filename=output_filename, + format=ProblemFormat.gams, + io_options=io_options, + ) symbolMap = model.solutions.symbol_map[smap_id] presolve_completion_time = time.time() if report_timing: - print(" %6.2f seconds required for presolve" % - (presolve_completion_time - initial_time)) + print( + " %6.2f seconds required for presolve" + % (presolve_completion_time - initial_time) + ) #################################################################### # Apply solver @@ -835,8 +883,7 @@ def solve(self, *args, **kwds): if tee: ostreams.append(sys.stdout) with TeeStream(*ostreams) as t: - result = subprocess.run(command, stdout=t.STDOUT, - stderr=t.STDERR) + result = subprocess.run(command, stdout=t.STDOUT, stderr=t.STDERR) rc = result.returncode txt = ostreams[0].getvalue() @@ -851,22 +898,27 @@ def solve(self, *args, **kwds): # Run check_expr_evaluation, which errors if necessary check_expr_evaluation(model, symbolMap, 'shell') # If nothing was raised, or for all other cases, raise this - logger.error("GAMS encountered an error during solve. " - "Check listing file for details.") + logger.error( + "GAMS encountered an error during solve. " + "Check listing file for details." + ) logger.error(txt) if os.path.exists(lst_filename): with open(lst_filename, 'r') as FILE: - logger.error( - "GAMS Listing file:\n\n%s" % (FILE.read(),)) - raise RuntimeError("GAMS encountered an error during solve. " - "Check listing file for details.") + logger.error("GAMS Listing file:\n\n%s" % (FILE.read(),)) + raise RuntimeError( + "GAMS encountered an error during solve. " + "Check listing file for details." + ) if io_options['put_results_format'] == 'gdx': model_soln, stat_vars = self._parse_gdx_results( - results_filename, statresults_filename) + results_filename, statresults_filename + ) else: model_soln, stat_vars = self._parse_dat_results( - results_filename, statresults_filename) + results_filename, statresults_filename + ) finally: if not keepfiles: if newdir: @@ -879,8 +931,10 @@ def solve(self, *args, **kwds): solve_completion_time = time.time() if report_timing: - print(" %6.2f seconds required for solver" % - (solve_completion_time - presolve_completion_time)) + print( + " %6.2f seconds required for solver" + % (solve_completion_time - presolve_completion_time) + ) #################################################################### # Postsolve @@ -888,17 +942,22 @@ def solve(self, *args, **kwds): # import suffixes must be on the top-level model if isinstance(model, IBlock): - model_suffixes = list(comp.storage_key for comp \ - in pyomo.core.kernel.suffix.\ - import_suffix_generator(model, - active=True, - descend_into=False)) + model_suffixes = list( + comp.storage_key + for comp in pyomo.core.kernel.suffix.import_suffix_generator( + model, active=True, descend_into=False + ) + ) else: - model_suffixes = list(name for (name,comp) \ - in pyomo.core.base.suffix.\ - active_import_suffix_generator(model)) - extract_dual = ('dual' in model_suffixes) - extract_rc = ('rc' in model_suffixes) + model_suffixes = list( + name + for ( + name, + comp, + ) in pyomo.core.base.suffix.active_import_suffix_generator(model) + ) + extract_dual = 'dual' in model_suffixes + extract_rc = 'rc' in model_suffixes results = SolverResults() results.problem.name = output_filename @@ -910,9 +969,10 @@ def solve(self, *args, **kwds): results.problem.number_of_binary_variables = None # Includes binary vars: results.problem.number_of_integer_variables = stat_vars["NUMDVAR"] - results.problem.number_of_continuous_variables = stat_vars["NUMVAR"] \ - - stat_vars["NUMDVAR"] - results.problem.number_of_objectives = 1 # required by GAMS writer + results.problem.number_of_continuous_variables = ( + stat_vars["NUMVAR"] - stat_vars["NUMDVAR"] + ) + results.problem.number_of_objectives = 1 # required by GAMS writer obj = list(model.component_data_objects(Objective, active=True)) assert len(obj) == 1, 'Only one objective is allowed.' obj = obj[0] @@ -945,7 +1005,9 @@ def solve(self, *args, **kwds): results.solver.termination_condition = TerminationCondition.maxEvaluations elif solvestat == 7: results.solver.status = SolverStatus.aborted - results.solver.termination_condition = TerminationCondition.licensingProblems + results.solver.termination_condition = ( + TerminationCondition.licensingProblems + ) elif solvestat == 8: results.solver.status = SolverStatus.aborted results.solver.termination_condition = TerminationCondition.userInterrupt @@ -954,7 +1016,9 @@ def solve(self, *args, **kwds): results.solver.termination_condition = TerminationCondition.solverFailure elif solvestat == 11: results.solver.status = SolverStatus.error - results.solver.termination_condition = TerminationCondition.internalSolverError + results.solver.termination_condition = ( + TerminationCondition.internalSolverError + ) elif solvestat == 4: results.solver.status = SolverStatus.warning results.solver.message = "Solver quit with a problem (see LST file)" @@ -963,7 +1027,7 @@ def solve(self, *args, **kwds): elif solvestat == 6: results.solver.status = SolverStatus.unknown - results.solver.return_code = rc # 0 + results.solver.return_code = rc # 0 # Not sure if this value is actually user time # "the elapsed time it took to execute a solve statement in total" results.solver.user_time = stat_vars["ETSOLVE"] @@ -994,13 +1058,17 @@ def solve(self, *args, **kwds): results.solver.termination_condition = TerminationCondition.optimal soln.status = SolutionStatus.optimal elif modelstat == 9: - results.solver.termination_condition = TerminationCondition.intermediateNonInteger + results.solver.termination_condition = ( + TerminationCondition.intermediateNonInteger + ) soln.status = SolutionStatus.other elif modelstat == 11: # Should be handled above, if modelstat and solvestat both # indicate a licensing problem if results.solver.termination_condition is None: - results.solver.termination_condition = TerminationCondition.licensingProblems + results.solver.termination_condition = ( + TerminationCondition.licensingProblems + ) soln.status = SolutionStatus.error elif modelstat in [12, 13]: if results.solver.termination_condition is None: @@ -1019,8 +1087,7 @@ def solve(self, *args, **kwds): # This is just a backup catch, all cases are handled above soln.status = SolutionStatus.error - soln.gap = abs(results.problem.upper_bound \ - - results.problem.lower_bound) + soln.gap = abs(results.problem.upper_bound - results.problem.lower_bound) has_rc_info = True for sym, ref in symbolMap.bySymbol.items(): @@ -1053,8 +1120,7 @@ def solve(self, *args, **kwds): if extract_dual: for c in model.component_data_objects(Constraint, active=True): - if (c.body.is_fixed()) or \ - (not (c.has_lb() or c.has_ub())): + if (c.body.is_fixed()) or (not (c.has_lb() or c.has_ub())): # the constraint was not sent to GAMS continue sym = symbolMap.getSymbol(c) @@ -1116,10 +1182,12 @@ def solve(self, *args, **kwds): results._smap = None if isinstance(model, IBlock): if len(results.solution) == 1: - results.solution(0).symbol_map = \ - getattr(model, "._symbol_maps")[results._smap_id] - results.solution(0).default_variable_value = \ - self._default_variable_value + results.solution(0).symbol_map = getattr(model, "._symbol_maps")[ + results._smap_id + ] + results.solution( + 0 + ).default_variable_value = self._default_variable_value if load_solutions: model.load_solution(results.solution(0)) else: @@ -1130,8 +1198,7 @@ def solve(self, *args, **kwds): assert len(getattr(model, "._symbol_maps")) == 1 delattr(model, "._symbol_maps") del results._smap_id - if load_solutions and \ - (len(results.solution) == 0): + if load_solutions and (len(results.solution) == 0): logger.error("No solution is available") else: if load_solutions: @@ -1144,18 +1211,32 @@ def solve(self, *args, **kwds): postsolve_completion_time = time.time() if report_timing: - print(" %6.2f seconds required for postsolve" % - (postsolve_completion_time - solve_completion_time)) - print(" %6.2f seconds required total" % - (postsolve_completion_time - initial_time)) + print( + " %6.2f seconds required for postsolve" + % (postsolve_completion_time - solve_completion_time) + ) + print( + " %6.2f seconds required total" + % (postsolve_completion_time - initial_time) + ) return results def _parse_gdx_results(self, results_filename, statresults_filename): model_soln = dict() - stat_vars = dict.fromkeys(['MODELSTAT', 'SOLVESTAT', 'OBJEST', - 'OBJVAL', 'NUMVAR', 'NUMEQU', 'NUMDVAR', - 'NUMNZ', 'ETSOLVE']) + stat_vars = dict.fromkeys( + [ + 'MODELSTAT', + 'SOLVESTAT', + 'OBJEST', + 'OBJVAL', + 'NUMVAR', + 'NUMEQU', + 'NUMDVAR', + 'NUMNZ', + 'ETSOLVE', + ] + ) pgdx = gdxcc.new_gdxHandle_tp() ret = gdxcc.gdxCreateD(pgdx, os.path.dirname(self.executable()), 128) @@ -1324,6 +1405,7 @@ def check_expr_evaluation(model, symbolMap, solver_io): for var in uninit_vars: var.set_value(None) + def check_expr(expr, name, solver_io): # Check if GAMS will encounter domain violations in presolver # operations at current values, which are None (0) by default @@ -1331,17 +1413,20 @@ def check_expr(expr, name, solver_io): try: value(expr) except (ValueError, ZeroDivisionError): - logger.warning("While evaluating model.%s's expression, GAMS solver " - "encountered an error.\nGAMS requires that all " - "equations and expressions evaluate at initial values.\n" - "Ensure variable values do not violate any domains, " - "and use the warmstart=True keyword to solve()." % name) + logger.warning( + "While evaluating model.%s's expression, GAMS solver " + "encountered an error.\nGAMS requires that all " + "equations and expressions evaluate at initial values.\n" + "Ensure variable values do not violate any domains, " + "and use the warmstart=True keyword to solve()." % name + ) if solver_io == 'shell': # For shell, there is no previous exception to worry about # overwriting, so raise the ValueError. # But for direct, the GamsExceptionExecution will be raised. raise + def file_removal_gams_direct(tmpdir, newdir): if newdir: shutil.rmtree(tmpdir) diff --git a/pyomo/solvers/plugins/solvers/GLPK.py b/pyomo/solvers/plugins/solvers/GLPK.py index 5054f3a04d9..a5b8ad9c019 100644 --- a/pyomo/solvers/plugins/solvers/GLPK.py +++ b/pyomo/solvers/plugins/solvers/GLPK.py @@ -20,8 +20,14 @@ from pyomo.common import Executable from pyomo.common.collections import Bunch from pyomo.opt import ( - SolverFactory, OptSolver, ProblemFormat, ResultsFormat, SolverResults, - TerminationCondition, SolutionStatus, ProblemSense, + SolverFactory, + OptSolver, + ProblemFormat, + ResultsFormat, + SolverResults, + TerminationCondition, + SolutionStatus, + ProblemSense, ) from pyomo.opt.base.solvers import _extract_version from pyomo.opt.solver import SystemCallSolver @@ -32,19 +38,19 @@ # Not sure how better to get these constants, but pulled from GLPK # documentation and source code (include/glpk.h) - # status of auxiliary / structural variables -GLP_BS = 1 # inactive constraint / basic variable -GLP_NL = 2 # active constraint or non-basic variable on lower bound -GLP_NU = 3 # active constraint or non-basic variable on upper bound -GLP_NF = 4 # active free row or non-basic free variable -GLP_NS = 5 # active equality constraint or non-basic fixed variable +# status of auxiliary / structural variables +GLP_BS = 1 # inactive constraint / basic variable +GLP_NL = 2 # active constraint or non-basic variable on lower bound +GLP_NU = 3 # active constraint or non-basic variable on upper bound +GLP_NF = 4 # active free row or non-basic free variable +GLP_NS = 5 # active equality constraint or non-basic fixed variable - # solution status -GLP_UNDEF = 'u' # solution is undefined -GLP_FEAS = 'f' # solution is feasible +# solution status +GLP_UNDEF = 'u' # solution is undefined +GLP_FEAS = 'f' # solution is feasible GLP_INFEAS = 'i' # solution is infeasible GLP_NOFEAS = 'n' # no feasible solution exists -GLP_OPT = 'o' # solution is optimal +GLP_OPT = 'o' # solution is optimal @SolverFactory.register('glpk', doc='The GLPK LP/MIP solver') @@ -68,8 +74,8 @@ def __new__(cls, *args, **kwds): @SolverFactory.register( - '_glpk_shell', - doc='Shell interface to the GNU Linear Programming Kit') + '_glpk_shell', doc='Shell interface to the GNU Linear Programming Kit' +) class GLPKSHELL(SystemCallSolver): """Shell interface to the GLPK LP/MIP solver""" @@ -77,7 +83,7 @@ class GLPKSHELL(SystemCallSolver): # version every time we run the solver. _known_versions = {} - def __init__ (self, **kwargs): + def __init__(self, **kwargs): # # Call base constructor # @@ -89,13 +95,15 @@ def __init__ (self, **kwargs): # # Valid problem formats, and valid results for each format # - self._valid_problem_formats = [ProblemFormat.cpxlp, - ProblemFormat.mps, - ProblemFormat.mod] + self._valid_problem_formats = [ + ProblemFormat.cpxlp, + ProblemFormat.mps, + ProblemFormat.mod, + ] self._valid_result_formats = { - ProblemFormat.mod: ResultsFormat.soln, - ProblemFormat.cpxlp: ResultsFormat.soln, - ProblemFormat.mps: ResultsFormat.soln, + ProblemFormat.mod: ResultsFormat.soln, + ProblemFormat.cpxlp: ResultsFormat.soln, + ProblemFormat.mps: ResultsFormat.soln, } self.set_problem_format(ProblemFormat.cpxlp) @@ -110,8 +118,10 @@ def _default_results_format(self, prob_format): def _default_executable(self): executable = Executable('glpsol') if not executable: - msg = ("Could not locate the 'glpsol' executable, which is " - "required for solver '%s'") + msg = ( + "Could not locate the 'glpsol' executable, which is " + "required for solver '%s'" + ) logger.warning(msg % self.name) self.enable = False return None @@ -199,10 +209,10 @@ def process_logfile(self): results = SolverResults() # For the lazy programmer, handle long variable names - prob = results.problem - solv = results.solver + prob = results.problem + solv = results.solver solv.termination_condition = TerminationCondition.unknown - stats = results.solver.statistics + stats = results.solver.statistics bbound = stats.branch_and_bound prob.upper_bound = float('inf') @@ -222,33 +232,48 @@ def process_logfile(self): solv.user_time = toks[1] elif len(toks) > 2 and (toks[0], toks[2]) == ("TIME", "EXCEEDED;"): solv.termination_condition = TerminationCondition.maxTimeLimit - elif len(toks) > 5 and (toks[:6] == ['PROBLEM', 'HAS', 'NO', 'DUAL', 'FEASIBLE', 'SOLUTION']): + elif len(toks) > 5 and ( + toks[:6] == ['PROBLEM', 'HAS', 'NO', 'DUAL', 'FEASIBLE', 'SOLUTION'] + ): solv.termination_condition = TerminationCondition.unbounded - elif len(toks) > 5 and (toks[:6] == ['PROBLEM', 'HAS', 'NO', 'PRIMAL', 'FEASIBLE', 'SOLUTION']): + elif len(toks) > 5 and ( + toks[:6] + == ['PROBLEM', 'HAS', 'NO', 'PRIMAL', 'FEASIBLE', 'SOLUTION'] + ): solv.termination_condition = TerminationCondition.infeasible - elif len(toks) > 4 and (toks[:5] == ['PROBLEM', 'HAS', 'NO', 'FEASIBLE', 'SOLUTION']): + elif len(toks) > 4 and ( + toks[:5] == ['PROBLEM', 'HAS', 'NO', 'FEASIBLE', 'SOLUTION'] + ): solv.termination_condition = TerminationCondition.infeasible - elif len(toks) > 6 and (toks[:7] == ['LP', 'RELAXATION', 'HAS', 'NO', 'DUAL', 'FEASIBLE', 'SOLUTION']): + elif len(toks) > 6 and ( + toks[:7] + == ['LP', 'RELAXATION', 'HAS', 'NO', 'DUAL', 'FEASIBLE', 'SOLUTION'] + ): solv.termination_condition = TerminationCondition.unbounded return results def _glpk_get_solution_status(self, status): - if GLP_FEAS == status: return SolutionStatus.feasible - elif GLP_INFEAS == status: return SolutionStatus.infeasible - elif GLP_NOFEAS == status: return SolutionStatus.infeasible - elif GLP_UNDEF == status: return SolutionStatus.other - elif GLP_OPT == status: return SolutionStatus.optimal + if GLP_FEAS == status: + return SolutionStatus.feasible + elif GLP_INFEAS == status: + return SolutionStatus.infeasible + elif GLP_NOFEAS == status: + return SolutionStatus.infeasible + elif GLP_UNDEF == status: + return SolutionStatus.other + elif GLP_OPT == status: + return SolutionStatus.optimal raise RuntimeError("Unknown solution status returned by GLPK solver") - def process_soln_file (self, results): + def process_soln_file(self, results): pdata = self._glpfile psoln = self._rawfile prob = results.problem solv = results.solver - prob.name = 'unknown' # will ostensibly get updated + prob.name = 'unknown' # will ostensibly get updated # Step 1: Make use of the GLPK's machine parseable format (--wglp) to # collect variable and constraint names. @@ -259,7 +284,7 @@ def process_soln_file (self, results): # order as the --write output. # Note that documentation for these formats is available from the GLPK # documentation of 'glp_read_prob' and 'glp_write_sol' - variable_names = dict() # cols + variable_names = dict() # cols constraint_names = dict() # rows obj_name = 'objective' @@ -272,17 +297,23 @@ def process_soln_file (self, results): pcols = int(pcols) # fails if not a number; intentional pnonz = int(pnonz) # fails if not a number; intentional - if pprob != 'p' or \ - ptype not in ('lp', 'mip') or \ - psense not in ('max', 'min') or \ - prows < 0 or pcols < 0 or pnonz < 0: + if ( + pprob != 'p' + or ptype not in ('lp', 'mip') + or psense not in ('max', 'min') + or prows < 0 + or pcols < 0 + or pnonz < 0 + ): raise ValueError - self.is_integer = ('mip' == ptype and True or False) - prob.sense = 'min' == psense and ProblemSense.minimize or ProblemSense.maximize + self.is_integer = 'mip' == ptype and True or False + prob.sense = ( + 'min' == psense and ProblemSense.minimize or ProblemSense.maximize + ) prob.number_of_constraints = prows - prob.number_of_nonzeros = pnonz - prob.number_of_variables = pcols + prob.number_of_nonzeros = pnonz + prob.number_of_variables = pcols for line in f: glp_line_count += 1 @@ -293,20 +324,20 @@ def process_soln_file (self, results): pass elif 'n' == switch: # naming some attribute ntype = tokens.pop(0) - name = tokens.pop() - if 'i' == ntype: # row + name = tokens.pop() + if 'i' == ntype: # row row = tokens.pop() constraint_names[int(row)] = name # --write order == --wglp order; store name w/ row no - elif 'j' == ntype: # var + elif 'j' == ntype: # var col = tokens.pop() variable_names[int(col)] = name # --write order == --wglp order; store name w/ col no - elif 'z' == ntype: # objective + elif 'z' == ntype: # objective obj_name = name - elif 'p' == ntype: # problem name + elif 'p' == ntype: # problem name prob.name = name - else: # anything else is incorrect. + else: # anything else is incorrect. raise ValueError else: @@ -325,23 +356,31 @@ def process_soln_file (self, results): row = next(reader) try: row = next(reader) - while (row[0] == 'c'): + while row[0] == 'c': row = next(reader) if not row[0] == 's': raise ValueError("Expecting 's' row after 'c' rows") if row[1] == 'bas': - self._process_soln_bas(row, reader, results, obj_name, variable_names, constraint_names) + self._process_soln_bas( + row, reader, results, obj_name, variable_names, constraint_names + ) elif row[1] == 'ipt': - self._process_soln_ipt(row, reader, results, obj_name, variable_names, constraint_names) + self._process_soln_ipt( + row, reader, results, obj_name, variable_names, constraint_names + ) elif row[1] == 'mip': - self._process_soln_mip(row, reader, results, obj_name, variable_names, constraint_names) + self._process_soln_mip( + row, reader, results, obj_name, variable_names, constraint_names + ) except Exception: print("ERROR: " + str(sys.exc_info()[1])) msg = "Error parsing solution data file, line %d" % reader.line_num raise ValueError(msg) - def _process_soln_bas(self, row, reader, results, obj_name, variable_names, constraint_names): + def _process_soln_bas( + self, row, reader, results, obj_name, variable_names, constraint_names + ): """ Process a basic solution """ @@ -363,7 +402,7 @@ def _process_soln_bas(self, row, reader, results, obj_name, variable_names, cons solv.termination_condition = TerminationCondition.other elif pstat == 'f': - soln = results.solution.add() + soln = results.solution.add() soln.status = SolutionStatus.feasible solv.termination_condition = TerminationCondition.optimal @@ -404,11 +443,11 @@ def _process_soln_bas(self, row, reader, results, obj_name, variable_names, cons continue rdual = float(rdual) if cname.startswith('c_'): - soln.constraint[cname] = {"Dual":rdual} + soln.constraint[cname] = {"Dual": rdual} elif cname.startswith('r_l_'): - range_duals.setdefault(cname[4:],[0,0])[0] = rdual + range_duals.setdefault(cname[4:], [0, 0])[0] = rdual elif cname.startswith('r_u_'): - range_duals.setdefault(cname[4:],[0,0])[1] = rdual + range_duals.setdefault(cname[4:], [0, 0])[1] = rdual elif rtype == 'j': # NOTE: we are not using the column status (cst) value right now @@ -418,9 +457,9 @@ def _process_soln_bas(self, row, reader, results, obj_name, variable_names, cons continue cprim = float(cprim) if extract_reduced_costs is False: - soln.variable[vname] = {"Value" : cprim} + soln.variable[vname] = {"Value": cprim} else: - soln.variable[vname] = {"Value" : cprim, "Rc" : float(cdual)} + soln.variable[vname] = {"Value": cprim, "Rc": float(cdual)} elif rtype == 'e': break @@ -429,23 +468,25 @@ def _process_soln_bas(self, row, reader, results, obj_name, variable_names, cons continue else: - raise ValueError("Unexpected row type: "+rtype) + raise ValueError("Unexpected row type: " + rtype) # For the range constraints, supply only the dual with the largest # magnitude (at least one should always be numerically zero) scon = soln.Constraint - for key, (ld,ud) in range_duals.items(): + for key, (ld, ud) in range_duals.items(): if abs(ld) > abs(ud): - scon['r_l_'+key] = {"Dual":ld} + scon['r_l_' + key] = {"Dual": ld} else: - scon['r_l_'+key] = {"Dual":ud} # Use the same key + scon['r_l_' + key] = {"Dual": ud} # Use the same key - def _process_soln_mip(self, row, reader, results, obj_name, variable_names, constraint_names): + def _process_soln_mip( + self, row, reader, results, obj_name, variable_names, constraint_names + ): """ Process a basic solution """ - #prows = int(row[2]) - #pcols = int(row[3]) + # prows = int(row[2]) + # pcols = int(row[3]) status = row[4] obj_val = float(row[5]) @@ -461,7 +502,7 @@ def _process_soln_mip(self, row, reader, results, obj_name, variable_names, cons solv.termination_condition = TerminationCondition.unbounded return - soln = results.solution.add() + soln = results.solution.add() if status == 'f': soln.status = SolutionStatus.feasible solv.termination_condition = TerminationCondition.feasible @@ -496,7 +537,7 @@ def _process_soln_mip(self, row, reader, results, obj_name, variable_names, cons vname = variable_names[int(cid)] if 'ONE_VAR_CONSTANT' == vname: continue - soln.variable[vname] = {"Value" : float(cval)} + soln.variable[vname] = {"Value": float(cval)} elif rtype == 'e': break @@ -505,13 +546,12 @@ def _process_soln_mip(self, row, reader, results, obj_name, variable_names, cons continue else: - raise ValueError("Unexpected row type: "+rtype) + raise ValueError("Unexpected row type: " + rtype) @SolverFactory.register('_mock_glpk') class MockGLPK(GLPKSHELL, MockMIP): - """A Mock GLPK solver used for testing - """ + """A Mock GLPK solver used for testing""" def __init__(self, **kwds): try: @@ -531,7 +571,7 @@ def create_command_line(self, executable, problem_files): def executable(self): return MockMIP.executable(self) - def _execute_command(self,cmd): + def _execute_command(self, cmd): return MockMIP._execute_command(self, cmd) def _convert_problem(self, args, pformat, valid_pformats): diff --git a/pyomo/solvers/plugins/solvers/GUROBI.py b/pyomo/solvers/plugins/solvers/GUROBI.py index bde1bd9e31f..bf30b5a50f1 100644 --- a/pyomo/solvers/plugins/solvers/GUROBI.py +++ b/pyomo/solvers/plugins/solvers/GUROBI.py @@ -24,7 +24,11 @@ from pyomo.opt.base import ProblemFormat, ResultsFormat, OptSolver from pyomo.opt.base.solvers import _extract_version, SolverFactory from pyomo.opt.results import ( - SolverStatus, TerminationCondition, SolutionStatus, ProblemSense, Solution, + SolverStatus, + TerminationCondition, + SolutionStatus, + ProblemSense, + Solution, ) from pyomo.opt.solver import ILMLicensedSystemCallSolver from pyomo.core.kernel.block import IBlock @@ -36,14 +40,14 @@ @SolverFactory.register('gurobi', doc='The GUROBI LP/MIP solver') class GUROBI(OptSolver): - """The GUROBI LP/MIP solver - """ + """The GUROBI LP/MIP solver""" + def __new__(cls, *args, **kwds): mode = kwds.pop('solver_io', 'lp') if mode is None: mode = 'lp' # - if mode == 'lp': + if mode == 'lp': return SolverFactory('_gurobi_shell', **kwds) if mode == 'mps': opt = SolverFactory('_gurobi_shell', **kwds) @@ -73,12 +77,12 @@ def __new__(cls, *args, **kwds): return opt - @SolverFactory.register( - '_gurobi_shell', doc='Shell interface to the GUROBI LP/MIP solver') + '_gurobi_shell', doc='Shell interface to the GUROBI LP/MIP solver' +) class GUROBISHELL(ILMLicensedSystemCallSolver): - """Shell interface to the GUROBI LP/MIP solver - """ + """Shell interface to the GUROBI LP/MIP solver""" + _solver_info_cache = {} def __init__(self, **kwds): @@ -101,8 +105,8 @@ def __init__(self, **kwds): # # Define valid problem formats and associated results formats # - self._valid_problem_formats=[ProblemFormat.cpxlp, ProblemFormat.mps] - self._valid_result_formats={} + self._valid_problem_formats = [ProblemFormat.cpxlp, ProblemFormat.mps] + self._valid_result_formats = {} self._valid_result_formats[ProblemFormat.cpxlp] = [ResultsFormat.soln] self._valid_result_formats[ProblemFormat.mps] = [ResultsFormat.soln] self.set_problem_format(ProblemFormat.cpxlp) @@ -131,8 +135,7 @@ def license_is_valid(self): if not solver_exec: licensed = False else: - executable = os.path.join( - os.path.dirname(solver_exec), 'gurobi_cl') + executable = os.path.join(os.path.dirname(solver_exec), 'gurobi_cl') try: rc = subprocess.call( [executable, "--license"], @@ -143,8 +146,7 @@ def license_is_valid(self): try: rc = subprocess.run( [solver_exec], - input=('import gurobipy; ' - 'gurobipy.Env().dispose(); quit()'), + input=('import gurobipy; ' 'gurobipy.Env().dispose(); quit()'), stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, @@ -156,7 +158,6 @@ def license_is_valid(self): self._solver_info_cache[(solver_exec, 'licensed')] = licensed return licensed - def _default_results_format(self, prob_format): return ResultsFormat.soln @@ -180,18 +181,15 @@ def _warm_start(self, instance): # constraints output_index = 0 if isinstance(instance, IBlock): - smap = getattr(instance,"._symbol_maps")\ - [self._smap_id] + smap = getattr(instance, "._symbol_maps")[self._smap_id] else: smap = instance.solutions.symbol_map[self._smap_id] byObject = smap.byObject with open(self._warm_start_file_name, 'w') as mst_file: for vdata in instance.component_data_objects(Var): - if (vdata.value is not None) and \ - (id(vdata) in byObject): + if (vdata.value is not None) and (id(vdata) in byObject): name = byObject[id(vdata)] - mst_file.write("%s %s\n" - % (name, vdata.value)) + mst_file.write("%s %s\n" % (name, vdata.value)) # over-ride presolve to extract the warm-start keyword, if specified. def _presolve(self, *args, **kwds): @@ -215,12 +213,10 @@ def _presolve(self, *args, **kwds): # file - assuming that the user has already, via some external # mechanism, invoked warm_start() with a instance to create the # warm start file. - if self._warm_start_solve and \ - isinstance(args[0], str): + if self._warm_start_solve and isinstance(args[0], str): # we assume the user knows what they are doing... pass - elif self._warm_start_solve and \ - (not isinstance(args[0], str)): + elif self._warm_start_solve and (not isinstance(args[0], str)): # assign the name of the warm start file *before* calling # the base class presolve - the base class method ends up # creating the command line, and the warm start file-name is @@ -228,7 +224,8 @@ def _presolve(self, *args, **kwds): if self._warm_start_file_name is None: assert not user_warmstart self._warm_start_file_name = TempfileManager.create_tempfile( - suffix='.gurobi.mst') + suffix='.gurobi.mst' + ) # let the base class handle any remaining keywords/actions. ILMLicensedSystemCallSolver._presolve(self, *args, **kwds) @@ -241,7 +238,8 @@ def _presolve(self, *args, **kwds): if len(args) != 1: raise ValueError( "GUROBI _presolve method can only handle a single " - "problem instance - %s were supplied" % (len(args),)) + "problem instance - %s were supplied" % (len(args),) + ) # write the warm-start file - currently only supports MIPs. # we only know how to deal with a single problem instance. @@ -250,8 +248,9 @@ def _presolve(self, *args, **kwds): self._warm_start(args[0]) end_time = time.time() if self._report_timing is True: - print("Warm start write time=%.2f seconds" - % (end_time-start_time)) + print( + "Warm start write time=%.2f seconds" % (end_time - start_time) + ) def _default_executable(self): if sys.platform == 'win32': @@ -262,8 +261,10 @@ def _default_executable(self): return executable.path() if gurobipy_available: return sys.executable - logger.warning("Could not locate the 'gurobi' executable, " - "which is required for solver %s" % self.name) + logger.warning( + "Could not locate the 'gurobi' executable, " + "which is required for solver %s" % self.name + ) self.enable = False return None @@ -280,8 +281,7 @@ def _get_version(self): else: results = subprocess.run( [solver_exec], - input=('import gurobipy; ' - 'print(gurobipy.gurobi.version()); quit()'), + input=('import gurobipy; ' 'print(gurobipy.gurobi.version()); quit()'), stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, @@ -289,7 +289,7 @@ def _get_version(self): ver = None try: ver = tuple(eval(results.stdout.strip())) - while(len(ver) < 4): + while len(ver) < 4: ver += (0,) except SyntaxError: ver = _extract_version('') @@ -298,15 +298,14 @@ def _get_version(self): self._solver_info_cache[(solver_exec, 'version')] = ver return ver - def create_command_line(self,executable,problem_files): + def create_command_line(self, executable, problem_files): # # Define log file # The log file in CPLEX contains the solution trace, but the # solver status can be found in the solution file. # if self._log_file is None: - self._log_file = TempfileManager.\ - create_tempfile(suffix = '.gurobi.log') + self._log_file = TempfileManager.create_tempfile(suffix='.gurobi.log') # # Define solution file @@ -314,8 +313,7 @@ def create_command_line(self,executable,problem_files): # solver status. # if self._soln_file is None: - self._soln_file = TempfileManager.\ - create_tempfile(suffix = '.gurobi.txt') + self._soln_file = TempfileManager.create_tempfile(suffix='.gurobi.txt') # # Write the GUROBI execution script @@ -338,20 +336,20 @@ def create_command_line(self,executable,problem_files): # explicitly. # NOTE: The gurobi plugin (GUROBI.py) and GUROBI_RUN.py live in # the same directory. - script = "import sys\n" + script = "import sys\n" script += "from gurobipy import *\n" script += "sys.path.append(%r)\n" % (this_file_dir(),) script += "from GUROBI_RUN import *\n" script += "gurobi_run(" - mipgap = float(self.options.mipgap) if \ - self.options.mipgap is not None else \ - None - for x in ( problem_filename, - warmstart_filename, - solution_filename, - None, - options_dict, - self._suffixes ): + mipgap = float(self.options.mipgap) if self.options.mipgap is not None else None + for x in ( + problem_filename, + warmstart_filename, + solution_filename, + None, + options_dict, + self._suffixes, + ): script += "%r," % x script += ")\n" script += "quit()\n" @@ -359,17 +357,14 @@ def create_command_line(self,executable,problem_files): # dump the script and warm-start file names for the # user if we're keeping files around. if self._keepfiles: - script_fname = TempfileManager.create_tempfile( - suffix='.gurobi.script') + script_fname = TempfileManager.create_tempfile(suffix='.gurobi.script') script_file = open(script_fname, 'w') - script_file.write( script ) + script_file.write(script) script_file.close() print("Solver script file: '%s'" % script_fname) - if self._warm_start_solve and \ - (self._warm_start_file_name is not None): - print("Solver warm-start file: " - +self._warm_start_file_name) + if self._warm_start_solve and (self._warm_start_file_name is not None): + print("Solver warm-start file: " + self._warm_start_file_name) # # Define command line @@ -377,9 +372,7 @@ def create_command_line(self,executable,problem_files): cmd = [executable] if self._timer: cmd.insert(0, self._timer) - return Bunch(cmd=cmd, script=script, - log_file=self._log_file, env=None) - + return Bunch(cmd=cmd, script=script, log_file=self._log_file, env=None) def process_soln_file(self, results): # the only suffixes that we extract from CPLEX are @@ -391,18 +384,21 @@ def process_soln_file(self, results): extract_slacks = False extract_rc = False for suffix in self._suffixes: - flag=False + flag = False if re.match(suffix, "dual"): extract_duals = True - flag=True + flag = True if re.match(suffix, "slack"): extract_slacks = True - flag=True + flag = True if re.match(suffix, "rc"): extract_rc = True - flag=True + flag = True if not flag: - raise RuntimeError("***The GUROBI solver plugin cannot extract solution suffix="+suffix) + raise RuntimeError( + "***The GUROBI solver plugin cannot extract solution suffix=" + + suffix + ) # check for existence of the solution file # not sure why we just return - would think that we @@ -425,7 +421,7 @@ def process_soln_file(self, results): # 2 - solution # 3 - solver - section = 0 # unknown + section = 0 # unknown solution_seen = False @@ -436,56 +432,69 @@ def process_soln_file(self, results): for line in INPUT: line = line.strip() tokens = [token.strip() for token in line.split(":")] - if (tokens[0] == 'section'): - if (tokens[1] == 'problem'): + if tokens[0] == 'section': + if tokens[1] == 'problem': section = 1 - elif (tokens[1] == 'solution'): + elif tokens[1] == 'solution': section = 2 solution_seen = True - elif (tokens[1] == 'solver'): + elif tokens[1] == 'solver': section = 3 else: - if (section == 2): - if (tokens[0] == 'var'): + if section == 2: + if tokens[0] == 'var': if tokens[1] != "ONE_VAR_CONSTANT": - soln_variables[tokens[1]] = {"Value" : float(tokens[2])} + soln_variables[tokens[1]] = {"Value": float(tokens[2])} num_variables_read += 1 - elif (tokens[0] == 'status'): + elif tokens[0] == 'status': soln.status = getattr(SolutionStatus, tokens[1]) - elif (tokens[0] == 'gap'): + elif tokens[0] == 'gap': soln.gap = float(tokens[1]) - elif (tokens[0] == 'objective'): + elif tokens[0] == 'objective': if tokens[1].strip() != 'None': - soln.objective['__default_objective__'] = \ - {'Value': float(tokens[1])} + soln.objective['__default_objective__'] = { + 'Value': float(tokens[1]) + } if results.problem.sense == ProblemSense.minimize: results.problem.upper_bound = float(tokens[1]) else: results.problem.lower_bound = float(tokens[1]) - elif (tokens[0] == 'constraintdual'): + elif tokens[0] == 'constraintdual': name = tokens[1] if name != "c_e_ONE_VAR_CONSTANT": if name.startswith('c_'): - soln_constraints.setdefault(tokens[1],{})["Dual"] = float(tokens[2]) + soln_constraints.setdefault(tokens[1], {})[ + "Dual" + ] = float(tokens[2]) elif name.startswith('r_l_'): - range_duals.setdefault(name[4:],[0,0])[0] = float(tokens[2]) + range_duals.setdefault(name[4:], [0, 0])[0] = float( + tokens[2] + ) elif name.startswith('r_u_'): - range_duals.setdefault(name[4:],[0,0])[1] = float(tokens[2]) - elif (tokens[0] == 'constraintslack'): + range_duals.setdefault(name[4:], [0, 0])[1] = float( + tokens[2] + ) + elif tokens[0] == 'constraintslack': name = tokens[1] if name != "c_e_ONE_VAR_CONSTANT": if name.startswith('c_'): - soln_constraints.setdefault(tokens[1],{})["Slack"] = float(tokens[2]) + soln_constraints.setdefault(tokens[1], {})[ + "Slack" + ] = float(tokens[2]) elif name.startswith('r_l_'): - range_slacks.setdefault(name[4:],[0,0])[0] = float(tokens[2]) + range_slacks.setdefault(name[4:], [0, 0])[0] = float( + tokens[2] + ) elif name.startswith('r_u_'): - range_slacks.setdefault(name[4:],[0,0])[1] = float(tokens[2]) - elif (tokens[0] == 'varrc'): + range_slacks.setdefault(name[4:], [0, 0])[1] = float( + tokens[2] + ) + elif tokens[0] == 'varrc': if tokens[1] != "ONE_VAR_CONSTANT": soln_variables[tokens[1]]["Rc"] = float(tokens[2]) else: setattr(soln, tokens[0], tokens[1]) - elif (section == 1): + elif section == 1: if tokens[0] == 'sense': if tokens[1] == 'minimize': results.problem.sense = ProblemSense.minimize @@ -497,14 +506,18 @@ def process_soln_file(self, results): except: val = tokens[1] setattr(results.problem, tokens[0], val) - elif (section == 3): - if (tokens[0] == 'status'): + elif section == 3: + if tokens[0] == 'status': results.solver.status = getattr(SolverStatus, tokens[1]) - elif (tokens[0] == 'termination_condition'): + elif tokens[0] == 'termination_condition': try: - results.solver.termination_condition = getattr(TerminationCondition, tokens[1]) + results.solver.termination_condition = getattr( + TerminationCondition, tokens[1] + ) except AttributeError: - results.solver.termination_condition = TerminationCondition.unknown + results.solver.termination_condition = ( + TerminationCondition.unknown + ) else: setattr(results.solver, tokens[0], tokens[1]) @@ -512,19 +525,19 @@ def process_soln_file(self, results): # For the range constraints, supply only the dual with the largest # magnitude (at least one should always be numerically zero) - for key,(ld,ud) in range_duals.items(): + for key, (ld, ud) in range_duals.items(): if abs(ld) > abs(ud): - soln_constraints['r_l_'+key] = {"Dual" : ld} + soln_constraints['r_l_' + key] = {"Dual": ld} else: # Use the same key - soln_constraints['r_l_'+key] = {"Dual" : ud} + soln_constraints['r_l_' + key] = {"Dual": ud} # slacks - for key,(ls,us) in range_slacks.items(): + for key, (ls, us) in range_slacks.items(): if abs(ls) > abs(us): - soln_constraints.setdefault('r_l_'+key,{})["Slack"] = ls + soln_constraints.setdefault('r_l_' + key, {})["Slack"] = ls else: # Use the same key - soln_constraints.setdefault('r_l_'+key,{})["Slack"] = us + soln_constraints.setdefault('r_l_' + key, {})["Slack"] = us if solution_seen: results.solution.insert(soln) diff --git a/pyomo/solvers/plugins/solvers/GUROBI_RUN.py b/pyomo/solvers/plugins/solvers/GUROBI_RUN.py index 5f2ae4d46e2..059ace780cf 100644 --- a/pyomo/solvers/plugins/solvers/GUROBI_RUN.py +++ b/pyomo/solvers/plugins/solvers/GUROBI_RUN.py @@ -19,6 +19,7 @@ """ from gurobipy import gurobi, read, GRB import sys + if sys.version_info[0] < 3: from itertools import izip as zip @@ -30,6 +31,7 @@ # rather, print an error message and return - the caller will know to look # in the logs in case of a failure. + def _is_numeric(x): try: float(x) @@ -37,6 +39,7 @@ def _is_numeric(x): return False return True + def gurobi_run(model_file, warmstart_file, soln_file, mipgap, options, suffixes): # figure out what suffixes we need to extract. @@ -44,18 +47,20 @@ def gurobi_run(model_file, warmstart_file, soln_file, mipgap, options, suffixes) extract_slacks = False extract_reduced_costs = False for suffix in suffixes: - flag=False - if re.match(suffix,"dual"): + flag = False + if re.match(suffix, "dual"): extract_duals = True - flag=True - if re.match(suffix,"slack"): + flag = True + if re.match(suffix, "slack"): extract_slacks = True - flag=True - if re.match(suffix,"rc"): + flag = True + if re.match(suffix, "rc"): extract_reduced_costs = True - flag=True + flag = True if not flag: - print("***The GUROBI solver plugin cannot extract solution suffix="+suffix) + print( + "***The GUROBI solver plugin cannot extract solution suffix=" + suffix + ) return # Load the lp model @@ -69,10 +74,12 @@ def gurobi_run(model_file, warmstart_file, soln_file, mipgap, options, suffixes) # printing the module will crash (when we have a QCP) if GUROBI_VERSION[0] >= 5: if (extract_reduced_costs is True) or (extract_duals is True): - model.setParam(GRB.Param.QCPDual,1) + model.setParam(GRB.Param.QCPDual, 1) if model is None: - print("***The GUROBI solver plugin failed to load the input LP file="+soln_file) + print( + "***The GUROBI solver plugin failed to load the input LP file=" + soln_file + ) return if warmstart_file is not None: @@ -104,7 +111,6 @@ def gurobi_run(model_file, warmstart_file, soln_file, mipgap, options, suffixes) raise model.setParam(key, float(value)) - if 'relax_integrality' in options: for v in model.getVars(): if v.vType != GRB.CONTINUOUS: @@ -122,47 +128,47 @@ def gurobi_run(model_file, warmstart_file, soln_file, mipgap, options, suffixes) solver_status = model.getAttr(GRB.Attr.Status) solution_status = None return_code = 0 - if (solver_status == GRB.LOADED): + if solver_status == GRB.LOADED: status = 'aborted' message = 'Model is loaded, but no solution information is availale.' term_cond = 'error' solution_status = 'unknown' - elif (solver_status == GRB.OPTIMAL): + elif solver_status == GRB.OPTIMAL: status = 'ok' message = 'Model was solved to optimality (subject to tolerances), and an optimal solution is available.' term_cond = 'optimal' solution_status = 'optimal' - elif (solver_status == GRB.INFEASIBLE): + elif solver_status == GRB.INFEASIBLE: status = 'warning' message = 'Model was proven to be infeasible.' term_cond = 'infeasible' solution_status = 'infeasible' - elif (solver_status == GRB.INF_OR_UNBD): + elif solver_status == GRB.INF_OR_UNBD: status = 'warning' message = 'Problem proven to be infeasible or unbounded.' term_cond = 'infeasibleOrUnbounded' solution_status = 'unsure' - elif (solver_status == GRB.UNBOUNDED): + elif solver_status == GRB.UNBOUNDED: status = 'warning' message = 'Model was proven to be unbounded.' term_cond = 'unbounded' solution_status = 'unbounded' - elif (solver_status == GRB.CUTOFF): + elif solver_status == GRB.CUTOFF: status = 'aborted' message = 'Optimal objective for model was proven to be worse than the value specified in the Cutoff parameter. No solution information is available.' term_cond = 'minFunctionValue' solution_status = 'unknown' - elif (solver_status == GRB.ITERATION_LIMIT): + elif solver_status == GRB.ITERATION_LIMIT: status = 'aborted' message = 'Optimization terminated because the total number of simplex iterations performed exceeded the value specified in the IterationLimit parameter.' term_cond = 'maxIterations' solution_status = 'stoppedByLimit' - elif (solver_status == GRB.NODE_LIMIT): + elif solver_status == GRB.NODE_LIMIT: status = 'aborted' message = 'Optimization terminated because the total number of branch-and-cut nodes explored exceeded the value specified in the NodeLimit parameter.' term_cond = 'maxEvaluations' solution_status = 'stoppedByLimit' - elif (solver_status == GRB.TIME_LIMIT): + elif solver_status == GRB.TIME_LIMIT: status = 'aborted' message = 'Optimization terminated because the time expended exceeded the value specified in the TimeLimit parameter.' term_cond = 'maxTimeLimit' @@ -172,40 +178,44 @@ def gurobi_run(model_file, warmstart_file, soln_file, mipgap, options, suffixes) message = 'Optimization terminated because the work expended exceeded the value specified in the WorkLimit parameter.' term_cond = 'maxTimeLimit' solution_status = 'stoppedByLimit' - elif (solver_status == GRB.SOLUTION_LIMIT): + elif solver_status == GRB.SOLUTION_LIMIT: status = 'aborted' message = 'Optimization terminated because the number of solutions found reached the value specified in the SolutionLimit parameter.' term_cond = 'stoppedByLimit' solution_status = 'stoppedByLimit' - elif (solver_status == GRB.INTERRUPTED): + elif solver_status == GRB.INTERRUPTED: status = 'aborted' message = 'Optimization was terminated by the user.' term_cond = 'error' solution_status = 'error' - elif (solver_status == GRB.NUMERIC): + elif solver_status == GRB.NUMERIC: status = 'error' - message = 'Optimization was terminated due to unrecoverable numerical difficulties.' + message = ( + 'Optimization was terminated due to unrecoverable numerical difficulties.' + ) term_cond = 'error' solution_status = 'error' - elif (solver_status == GRB.SUBOPTIMAL): + elif solver_status == GRB.SUBOPTIMAL: status = 'warning' message = 'Unable to satisfy optimality tolerances; a sub-optimal solution is available.' term_cond = 'other' solution_status = 'feasible' # note that USER_OBJ_LIMIT was added in Gurobi 7.0, so it may not be present - elif (solver_status is not None) and \ - (solver_status == getattr(GRB,'USER_OBJ_LIMIT',None)): + elif (solver_status is not None) and ( + solver_status == getattr(GRB, 'USER_OBJ_LIMIT', None) + ): status = 'aborted' - message = "User specified an objective limit " \ - "(a bound on either the best objective " \ - "or the best bound), and that limit has " \ - "been reached. Solution is available." + message = ( + "User specified an objective limit " + "(a bound on either the best objective " + "or the best bound), and that limit has " + "been reached. Solution is available." + ) term_cond = 'other' solution_status = 'stoppedByLimit' else: status = 'error' - message = ("Unhandled Gurobi solve status " - "("+str(solver_status)+")") + message = "Unhandled Gurobi solve status " "(" + str(solver_status) + ")" term_cond = 'error' solution_status = 'error' assert solution_status is not None @@ -216,14 +226,14 @@ def gurobi_run(model_file, warmstart_file, soln_file, mipgap, options, suffixes) except: obj_value = None if term_cond == "unbounded": - if (sense < 0): + if sense < 0: # maximize obj_value = float('inf') else: # minimize obj_value = float('-inf') elif term_cond == "infeasible": - if (sense < 0): + if sense < 0: # maximize obj_value = float('-inf') else: @@ -236,7 +246,7 @@ def gurobi_run(model_file, warmstart_file, soln_file, mipgap, options, suffixes) # write the information required by results.problem solnfile.write("section:problem\n") name = model.getAttr(GRB.Attr.ModelName) - solnfile.write("name: "+name+'\n') + solnfile.write("name: " + name + '\n') # TODO: find out about bounds and fix this with error checking # this line fails for some reason so set the value to unknown @@ -248,7 +258,7 @@ def gurobi_run(model_file, warmstart_file, soln_file, mipgap, options, suffixes) else: bound = None - if (sense < 0): + if sense < 0: solnfile.write("sense:maximize\n") if bound is None: solnfile.write("upper_bound: %f\n" % float('inf')) @@ -269,7 +279,9 @@ def gurobi_run(model_file, warmstart_file, soln_file, mipgap, options, suffixes) qcons = [] if GUROBI_VERSION[0] >= 5: qcons = model.getQConstrs() - solnfile.write("number_of_constraints: %d\n" % (len(cons)+len(qcons)+model.NumSOS,)) + solnfile.write( + "number_of_constraints: %d\n" % (len(cons) + len(qcons) + model.NumSOS,) + ) vars = model.getVars() solnfile.write("number_of_variables: %d\n" % len(vars)) @@ -280,7 +292,7 @@ def gurobi_run(model_file, warmstart_file, soln_file, mipgap, options, suffixes) n_intvars = model.getAttr(GRB.Attr.NumIntVars) solnfile.write("number_of_integer_variables: %d\n" % n_intvars) - solnfile.write("number_of_continuous_variables: %d\n" % (len(vars)-n_intvars,)) + solnfile.write("number_of_continuous_variables: %d\n" % (len(vars) - n_intvars,)) solnfile.write("number_of_nonzeros: %d\n" % model.getAttr(GRB.Attr.NumNZs)) @@ -295,7 +307,7 @@ def gurobi_run(model_file, warmstart_file, soln_file, mipgap, options, suffixes) solnfile.write('termination_message: %s\n' % message) is_discrete = False - if (model.getAttr(GRB.Attr.IsMIP)): + if model.getAttr(GRB.Attr.IsMIP): is_discrete = True if (term_cond == 'optimal') or (model.getAttr(GRB.Attr.SolCount) >= 1): @@ -323,7 +335,7 @@ def gurobi_run(model_file, warmstart_file, soln_file, mipgap, options, suffixes) if (is_discrete is False) and (extract_duals is True): vals = model.getAttr("Pi", cons) for val, name in zip(vals, con_names): - # Pi attributes in Gurobi are the constraint duals + # Pi attributes in Gurobi are the constraint duals solnfile.write("constraintdual: %s : %s\n" % (str(name), str(val))) if GUROBI_VERSION[0] >= 5: vals = model.getAttr("QCPi", qcons) @@ -331,7 +343,7 @@ def gurobi_run(model_file, warmstart_file, soln_file, mipgap, options, suffixes) # QCPI attributes in Gurobi are the constraint duals solnfile.write("constraintdual: %s : %s\n" % (str(name), str(val))) - if (extract_slacks is True): + if extract_slacks is True: vals = model.getAttr("Slack", cons) for val, name in zip(vals, con_names): solnfile.write("constraintslack: %s : %s\n" % (str(name), str(val))) diff --git a/pyomo/solvers/plugins/solvers/IPOPT.py b/pyomo/solvers/plugins/solvers/IPOPT.py index 0ff58e1d46c..db942114c7c 100644 --- a/pyomo/solvers/plugins/solvers/IPOPT.py +++ b/pyomo/solvers/plugins/solvers/IPOPT.py @@ -19,9 +19,10 @@ from pyomo.opt.base import ProblemFormat, ResultsFormat from pyomo.opt.base.solvers import _extract_version, SolverFactory from pyomo.opt.results import SolverStatus, SolverResults, TerminationCondition -from pyomo.opt.solver import SystemCallSolver +from pyomo.opt.solver import SystemCallSolver import logging + logger = logging.getLogger('pyomo.solvers') @@ -41,7 +42,7 @@ def __init__(self, **kwds): # Setup valid problem formats, and valid results for each problem format # Also set the default problem and results formats. # - self._valid_problem_formats=[ProblemFormat.nl] + self._valid_problem_formats = [ProblemFormat.nl] self._valid_result_formats = {} self._valid_result_formats[ProblemFormat.nl] = [ResultsFormat.sol] self.set_problem_format(ProblemFormat.nl) @@ -61,8 +62,10 @@ def _default_results_format(self, prob_format): def _default_executable(self): executable = Executable("ipopt") if not executable: - logger.warning("Could not locate the 'ipopt' executable, " - "which is required for solver %s" % self.name) + logger.warning( + "Could not locate the 'ipopt' executable, " + "which is required for solver %s" % self.name + ) self.enable = False return None return executable.path() @@ -74,23 +77,25 @@ def _get_version(self): solver_exec = self.executable() if solver_exec is None: return _extract_version('') - results = subprocess.run( [solver_exec,"-v"], timeout=1, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - universal_newlines=True) + results = subprocess.run( + [solver_exec, "-v"], + timeout=1, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True, + ) return _extract_version(results.stdout) def create_command_line(self, executable, problem_files): - assert(self._problem_format == ProblemFormat.nl) - assert(self._results_format == ResultsFormat.sol) + assert self._problem_format == ProblemFormat.nl + assert self._results_format == ResultsFormat.sol # # Define log file # if self._log_file is None: - self._log_file = TempfileManager.\ - create_tempfile(suffix="_ipopt.log") + self._log_file = TempfileManager.create_tempfile(suffix="_ipopt.log") fname = problem_files[0] if '.' in fname: @@ -99,7 +104,7 @@ def create_command_line(self, executable, problem_files): fname = '.'.join(tmp[:-1]) else: fname = tmp[0] - self._soln_file = fname+".sol" + self._soln_file = fname + ".sol" # # Define results file (since an external parser is used) @@ -109,7 +114,7 @@ def create_command_line(self, executable, problem_files): # # Define command line # - env=os.environ.copy() + env = os.environ.copy() # # Merge the PYOMO_AMPLFUNC (externals defined within # Pyomo/Pyomo) with any user-specified external function @@ -138,11 +143,11 @@ def create_command_line(self, executable, problem_files): if key == "option_file_name": ofn_option_used = True if isinstance(self.options[key], str) and ' ' in self.options[key]: - env_opt.append(key+"=\""+str(self.options[key])+"\"") - cmd.append(str(key)+"="+str(self.options[key])) + env_opt.append(key + "=\"" + str(self.options[key]) + "\"") + cmd.append(str(key) + "=" + str(self.options[key])) else: - env_opt.append(key+"="+str(self.options[key])) - cmd.append(str(key)+"="+str(self.options[key])) + env_opt.append(key + "=" + str(self.options[key])) + cmd.append(str(key) + "=" + str(self.options[key])) if len(of_opt) > 0: # If the 'option_file_name' command-line option @@ -155,32 +160,33 @@ def create_command_line(self, executable, problem_files): "option for Ipopt can not be used " "when specifying options for the " "options file (i.e., options that " - "start with 'OF_'") + "start with 'OF_'" + ) # Now check if an 'ipopt.opt' file exists in the # current working directory. If so, we need to # make it clear that this file will be ignored. default_of_name = os.path.join(os.getcwd(), 'ipopt.opt') if os.path.exists(default_of_name): - logger.warning("A file named '%s' exists in " - "the current working directory, but " - "Ipopt options file options (i.e., " - "options that start with 'OF_') were " - "provided. The options file '%s' will " - "be ignored." % (default_of_name, - default_of_name)) + logger.warning( + "A file named '%s' exists in " + "the current working directory, but " + "Ipopt options file options (i.e., " + "options that start with 'OF_') were " + "provided. The options file '%s' will " + "be ignored." % (default_of_name, default_of_name) + ) # Now write the new options file - options_filename = TempfileManager.\ - create_tempfile(suffix="_ipopt.opt") + options_filename = TempfileManager.create_tempfile(suffix="_ipopt.opt") with open(options_filename, "w") as f: for key, val in of_opt: - f.write(key+" "+str(val)+"\n") + f.write(key + " " + str(val) + "\n") # Now set the command-line option telling Ipopt # to use this file - env_opt.append('option_file_name="'+str(options_filename)+'"') - cmd.append('option_file_name='+str(options_filename)) + env_opt.append('option_file_name="' + str(options_filename) + '"') + cmd.append('option_file_name=' + str(options_filename)) envstr = "%s_options" % self.options.solver # Merge with any options coming in through the environment diff --git a/pyomo/solvers/plugins/solvers/SCIPAMPL.py b/pyomo/solvers/plugins/solvers/SCIPAMPL.py index f63bc4ff206..b40bef93072 100644 --- a/pyomo/solvers/plugins/solvers/SCIPAMPL.py +++ b/pyomo/solvers/plugins/solvers/SCIPAMPL.py @@ -10,7 +10,8 @@ # ___________________________________________________________________________ import os -#import os.path + +# import os.path import subprocess from pyomo.common import Executable @@ -19,17 +20,22 @@ from pyomo.opt.base import ProblemFormat, ResultsFormat from pyomo.opt.base.solvers import _extract_version, SolverFactory -from pyomo.opt.results import SolverStatus, TerminationCondition, SolutionStatus , ProblemSense +from pyomo.opt.results import ( + SolverStatus, + TerminationCondition, + SolutionStatus, + ProblemSense, +) from pyomo.opt.solver import SystemCallSolver import logging + logger = logging.getLogger('pyomo.solvers') @SolverFactory.register('scip', doc='The SCIP LP/MIP solver') class SCIPAMPL(SystemCallSolver): - """A generic optimizer that uses the AMPL Solver Library to interface with applications. - """ + """A generic optimizer that uses the AMPL Solver Library to interface with applications.""" # Cache default executable, so we do not need to repeatedly query the # versions every time. @@ -45,7 +51,7 @@ def __init__(self, **kwds): # Setup valid problem formats, and valid results for each problem format # Also set the default problem and results formats. # - self._valid_problem_formats=[ProblemFormat.nl] + self._valid_problem_formats = [ProblemFormat.nl] self._valid_result_formats = {} self._valid_result_formats[ProblemFormat.nl] = [ResultsFormat.sol] self.set_problem_format(ProblemFormat.nl) @@ -69,7 +75,9 @@ def _default_executable(self): if executable: executable_path = executable.path() if executable_path not in self._known_versions: - self._known_versions[executable_path] = self._get_version(executable_path) + self._known_versions[executable_path] = self._get_version( + executable_path + ) _ver = self._known_versions[executable_path] if _ver and _ver >= (8,): return executable_path @@ -77,9 +85,11 @@ def _default_executable(self): # revert to scipampl for older versions executable = Executable("scipampl") if not executable: - logger.warning("Could not locate the 'scip' executable or" - " the older 'scipampl' executable, which is " - "required for solver %s" % self.name) + logger.warning( + "Could not locate the 'scip' executable or" + " the older 'scipampl' executable, which is " + "required for solver %s" % self.name + ) self.enable = False return None return executable.path() @@ -92,23 +102,25 @@ def _get_version(self, solver_exec=None): solver_exec = self.executable() if solver_exec is None: return _extract_version('') - results = subprocess.run([solver_exec, "--version"], timeout=1, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - universal_newlines=True) + results = subprocess.run( + [solver_exec, "--version"], + timeout=1, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True, + ) return _extract_version(results.stdout) def create_command_line(self, executable, problem_files): - assert(self._problem_format == ProblemFormat.nl) - assert(self._results_format == ResultsFormat.sol) + assert self._problem_format == ProblemFormat.nl + assert self._results_format == ResultsFormat.sol # # Define log file # if self._log_file is None: - self._log_file = TempfileManager.\ - create_tempfile(suffix="_scip.log") + self._log_file = TempfileManager.create_tempfile(suffix="_scip.log") fname = problem_files[0] if '.' in fname: @@ -117,7 +129,7 @@ def create_command_line(self, executable, problem_files): fname = '.'.join(tmp[:-1]) else: fname = tmp[0] - self._soln_file = fname+".sol" + self._soln_file = fname + ".sol" # # Define results file (since an external parser is used) @@ -127,7 +139,7 @@ def create_command_line(self, executable, problem_files): # # Define command line # - env=os.environ.copy() + env = os.environ.copy() # # Merge the PYOMO_AMPLFUNC (externals defined within # Pyomo/Pyomo) with any user-specified external function @@ -157,18 +169,22 @@ def create_command_line(self, executable, problem_files): # to the command line. I'm not sure what solvers this method of passing options # through the envstr variable works for, but it does not seem to work for cplex # or gurobi - env_opt=[] + env_opt = [] of_opt = [] for key in self.options: if key == 'solver': continue if isinstance(self.options[key], str) and ' ' in self.options[key]: - env_opt.append(key+"=\""+str(self.options[key])+"\"") + env_opt.append(key + "=\"" + str(self.options[key]) + "\"") else: - env_opt.append(key+"="+str(self.options[key])) - of_opt.append(str(key)+" = "+str(self.options[key])) - - if self._timelimit is not None and self._timelimit > 0.0 and 'limits/time' not in self.options: + env_opt.append(key + "=" + str(self.options[key])) + of_opt.append(str(key) + " = " + str(self.options[key])) + + if ( + self._timelimit is not None + and self._timelimit > 0.0 + and 'limits/time' not in self.options + ): of_opt.append("limits/time = " + str(self._timelimit)) envstr = "%s_options" % self.options.solver @@ -181,78 +197,78 @@ def create_command_line(self, executable, problem_files): # make it clear that this file will be ignored. default_of_name = os.path.join(os.getcwd(), 'scip.set') if os.path.exists(default_of_name): - logger.warning("A file named '%s' exists in " - "the current working directory, but " - "SCIP options are being set using a " - "separate options file. The options " - "file '%s' will be ignored." - % (default_of_name, default_of_name)) + logger.warning( + "A file named '%s' exists in " + "the current working directory, but " + "SCIP options are being set using a " + "separate options file. The options " + "file '%s' will be ignored." % (default_of_name, default_of_name) + ) options_dir = TempfileManager.create_tempdir() # Now write the new options file with open(os.path.join(options_dir, 'scip.set'), 'w') as f: for line in of_opt: - f.write(line+"\n") + f.write(line + "\n") else: options_dir = None return Bunch(cmd=cmd, log_file=self._log_file, env=env, cwd=options_dir) def _postsolve(self): - + # find SCIP version (calling version() and _get_version() mess things) - + executable = self._command.cmd[0] - + version = self._known_versions[executable] - + if version < (8, 0, 0, 0): - + # it may be possible to get results from older version but this was # not tested, so the old way of doing things is here preserved - + results = super(SCIPAMPL, self)._postsolve() - + else: - + # repeat code from super(SCIPAMPL, self)._postsolve() # in order to access the log file and get the results from there if self._log_file is not None: - OUTPUT=open(self._log_file,"w") - OUTPUT.write("Solver command line: "+str(self._command.cmd)+'\n') + OUTPUT = open(self._log_file, "w") + OUTPUT.write("Solver command line: " + str(self._command.cmd) + '\n') OUTPUT.write("\n") - OUTPUT.write(self._log+'\n') + OUTPUT.write(self._log + '\n') OUTPUT.close() - + # JPW: The cleanup of the problem file probably shouldn't be here, but # rather in the base OptSolver class. That would require movement of # the keepfiles attribute and associated cleanup logic to the base # class, which I didn't feel like doing at this present time. the # base class remove_files method should clean up the problem file. - if (self._log_file is not None) and \ - (not os.path.exists(self._log_file)): + if (self._log_file is not None) and (not os.path.exists(self._log_file)): msg = "File '%s' not generated while executing %s" raise IOError(msg % (self._log_file, self.path)) results = None if self._results_format is not None: results = self.process_output(self._rc) - + # read results from the log file - + log_dict = self.read_scip_log(self._log_file) - + if len(log_dict) != 0: - + # if any were read, store them - + results.solver.time = log_dict['solving_time'] results.solver.gap = log_dict['gap'] results.solver.primal_bound = log_dict['primal_bound'] results.solver.dual_bound = log_dict['dual_bound'] - + # TODO: get scip to produce a statistics file and read it # Why? It has all the information one can possibly need. # @@ -266,255 +282,213 @@ def _postsolve(self): # instead being automatically derived from # the input lp/nl filename. so, we may have # to clean it up manually. - if (not self._soln_file is None) and \ - os.path.exists(self._soln_file): + if (not self._soln_file is None) and os.path.exists( + self._soln_file + ): os.remove(self._soln_file) TempfileManager.pop(remove=not self._keepfiles) - - #********************************************************************** - #********************************************************************** - + + # ********************************************************************** + # ********************************************************************** + # UNKNOWN # unknown='unknown' # An unitialized value - - if results.solver.message == "unknown": - results.solver.status = \ - SolverStatus.unknown - results.solver.termination_condition = \ - TerminationCondition.unknown + + if results.solver.message == "unknown": + results.solver.status = SolverStatus.unknown + results.solver.termination_condition = TerminationCondition.unknown if len(results.solution) > 0: - results.solution(0).status = \ - SolutionStatus.unknown - + results.solution(0).status = SolutionStatus.unknown + # ABORTED # userInterrupt='userInterrupt' # Interrupt signal generated by user - + elif results.solver.message == "user interrupt": - results.solver.status = \ - SolverStatus.aborted - results.solver.termination_condition = \ - TerminationCondition.userInterrupt + results.solver.status = SolverStatus.aborted + results.solver.termination_condition = TerminationCondition.userInterrupt if len(results.solution) > 0: - results.solution(0).status = \ - SolutionStatus.unknown - + results.solution(0).status = SolutionStatus.unknown + # OK # maxEvaluations='maxEvaluations' # Exceeded maximum number of problem evaluations - + elif results.solver.message == "node limit reached": - - results.solver.status = \ - SolverStatus.ok - results.solver.termination_condition = \ - TerminationCondition.maxEvaluations + + results.solver.status = SolverStatus.ok + results.solver.termination_condition = TerminationCondition.maxEvaluations if len(results.solution) > 0: - results.solution(0).status = \ - SolutionStatus.stoppedByLimit - + results.solution(0).status = SolutionStatus.stoppedByLimit + # OK # maxEvaluations='maxEvaluations' # Exceeded maximum number of problem evaluations - + elif results.solver.message == "total node limit reached": - results.solver.status = \ - SolverStatus.ok - results.solver.termination_condition = \ - TerminationCondition.maxEvaluations + results.solver.status = SolverStatus.ok + results.solver.termination_condition = TerminationCondition.maxEvaluations if len(results.solution) > 0: - results.solution(0).status = \ - SolutionStatus.stoppedByLimit - + results.solution(0).status = SolutionStatus.stoppedByLimit + # OK # maxEvaluations='maxEvaluations' # Exceeded maximum number of problem evaluations - + elif results.solver.message == "stall node limit reached": - results.solver.status = \ - SolverStatus.ok - results.solver.termination_condition = \ - TerminationCondition.maxEvaluations + results.solver.status = SolverStatus.ok + results.solver.termination_condition = TerminationCondition.maxEvaluations if len(results.solution) > 0: - results.solution(0).status = \ - SolutionStatus.stoppedByLimit - + results.solution(0).status = SolutionStatus.stoppedByLimit + # OK # maxTimeLimit='maxTimeLimit' # Exceeded maximum time limited allowed by user but having return a feasible solution - + elif results.solver.message == "time limit reached": - results.solver.status = \ - SolverStatus.ok - results.solver.termination_condition = \ - TerminationCondition.maxTimeLimit + results.solver.status = SolverStatus.ok + results.solver.termination_condition = TerminationCondition.maxTimeLimit if len(results.solution) > 0: - results.solution(0).status = \ - SolutionStatus.stoppedByLimit - + results.solution(0).status = SolutionStatus.stoppedByLimit + # OK # other='other' # Other, uncategorized normal termination - + elif results.solver.message == "memory limit reached": - results.solver.status = \ - SolverStatus.ok - results.solver.termination_condition = \ - TerminationCondition.other + results.solver.status = SolverStatus.ok + results.solver.termination_condition = TerminationCondition.other if len(results.solution) > 0: - results.solution(0).status = \ - SolutionStatus.stoppedByLimit - + results.solution(0).status = SolutionStatus.stoppedByLimit + # OK # other='other' # Other, uncategorized normal termination - + elif results.solver.message == "gap limit reached": - results.solver.status = \ - SolverStatus.ok - results.solver.termination_condition = \ - TerminationCondition.other + results.solver.status = SolverStatus.ok + results.solver.termination_condition = TerminationCondition.other if len(results.solution) > 0: - results.solution(0).status = \ - SolutionStatus.stoppedByLimit - + results.solution(0).status = SolutionStatus.stoppedByLimit + # OK # other='other' # Other, uncategorized normal termination - + elif results.solver.message == "solution limit reached": - results.solver.status = \ - SolverStatus.ok - results.solver.termination_condition = \ - TerminationCondition.other + results.solver.status = SolverStatus.ok + results.solver.termination_condition = TerminationCondition.other if len(results.solution) > 0: - results.solution(0).status = \ - SolutionStatus.stoppedByLimit - + results.solution(0).status = SolutionStatus.stoppedByLimit + # OK # other='other' # Other, uncategorized normal termination - + elif results.solver.message == "solution improvement limit reached": - results.solver.status = \ - SolverStatus.ok - results.solver.termination_condition = \ - TerminationCondition.other + results.solver.status = SolverStatus.ok + results.solver.termination_condition = TerminationCondition.other if len(results.solution) > 0: - results.solution(0).status = \ - SolutionStatus.stoppedByLimit - + results.solution(0).status = SolutionStatus.stoppedByLimit + # OK # optimal='optimal' # Found an optimal solution - + elif results.solver.message == "optimal solution found": - results.solver.status = \ - SolverStatus.ok - results.solver.termination_condition = \ - TerminationCondition.optimal + results.solver.status = SolverStatus.ok + results.solver.termination_condition = TerminationCondition.optimal if len(results.solution) > 0: - results.solution(0).status = \ - SolutionStatus.optimal + results.solution(0).status = SolutionStatus.optimal if results.problem.sense == ProblemSense.minimize: results.problem.lower_bound = results.solver.primal_bound else: results.problem.upper_bound = results.solver.primal_bound - + # WARNING # infeasible='infeasible' # Demonstrated that the problem is infeasible - + elif results.solver.message == "infeasible": - results.solver.status = \ - SolverStatus.warning - results.solver.termination_condition = \ - TerminationCondition.infeasible + results.solver.status = SolverStatus.warning + results.solver.termination_condition = TerminationCondition.infeasible if len(results.solution) > 0: - results.solution(0).status = \ - SolutionStatus.infeasible - + results.solution(0).status = SolutionStatus.infeasible + # WARNING # unbounded='unbounded' # Demonstrated that problem is unbounded - + elif results.solver.message == "unbounded": - results.solver.status = \ - SolverStatus.warning - results.solver.termination_condition = \ - TerminationCondition.unbounded + results.solver.status = SolverStatus.warning + results.solver.termination_condition = TerminationCondition.unbounded if len(results.solution) > 0: - results.solution(0).status = \ - SolutionStatus.unbounded - + results.solution(0).status = SolutionStatus.unbounded + # WARNING # infeasibleOrUnbounded='infeasibleOrUnbounded' # Problem is either infeasible or unbounded - + elif results.solver.message == "infeasible or unbounded": - results.solver.status = \ - SolverStatus.warning - results.solver.termination_condition = \ + results.solver.status = SolverStatus.warning + results.solver.termination_condition = ( TerminationCondition.infeasibleOrUnbounded + ) if len(results.solution) > 0: - results.solution(0).status = \ - SolutionStatus.unsure - + results.solution(0).status = SolutionStatus.unsure + # UNKNOWN # unknown='unknown' # An unitialized value - + else: - logger.warning("Unexpected SCIP solver message: %s" - % (results.solver.message)) - results.solver.status = \ - SolverStatus.unknown - results.solver.termination_condition = \ - TerminationCondition.unknown + logger.warning( + "Unexpected SCIP solver message: %s" % (results.solver.message) + ) + results.solver.status = SolverStatus.unknown + results.solver.termination_condition = TerminationCondition.unknown if len(results.solution) > 0: - results.solution(0).status = \ - SolutionStatus.unknown - + results.solution(0).status = SolutionStatus.unknown + return results - + @staticmethod def read_scip_log(filename: str): - + # TODO: check file exists, ensure opt has finished, etc - + from collections import deque - + with open(filename) as f: - + scip_lines = list(deque(f, 7)) scip_lines.pop() - - expected_labels = ['SCIP Status :', - 'Solving Time (sec) :', - 'Solving Nodes :', - 'Primal Bound :', - 'Dual Bound :', - 'Gap :'] - - colon_position = 19 # or scip_lines[0].index(':') - + + expected_labels = [ + 'SCIP Status :', + 'Solving Time (sec) :', + 'Solving Nodes :', + 'Primal Bound :', + 'Dual Bound :', + 'Gap :', + ] + + colon_position = 19 # or scip_lines[0].index(':') + for i, log_file_line in enumerate(scip_lines): - - if expected_labels[i] != log_file_line[0:colon_position+1]: - + + if expected_labels[i] != log_file_line[0 : colon_position + 1]: + return {} - + # get data - - solver_status = scip_lines[0][colon_position+2:scip_lines[0].index('\n')] - + + solver_status = scip_lines[0][colon_position + 2 : scip_lines[0].index('\n')] + solving_time = float( - scip_lines[1][colon_position+2:scip_lines[1].index('\n')] - ) - + scip_lines[1][colon_position + 2 : scip_lines[1].index('\n')] + ) + try: solving_nodes = int( - scip_lines[2][colon_position+2:scip_lines[2].index('(')] - ) + scip_lines[2][colon_position + 2 : scip_lines[2].index('(')] + ) except ValueError: - + solving_nodes = int( - scip_lines[2][colon_position+2:scip_lines[2].index('\n')] - ) - - primal_bound = float( - scip_lines[3][colon_position+2:scip_lines[3].index('(')] + scip_lines[2][colon_position + 2 : scip_lines[2].index('\n')] ) - + + primal_bound = float( + scip_lines[3][colon_position + 2 : scip_lines[3].index('(')] + ) + dual_bound = float( - scip_lines[4][colon_position+2:scip_lines[4].index('\n')] - ) - - + scip_lines[4][colon_position + 2 : scip_lines[4].index('\n')] + ) + try: - gap = float( - scip_lines[5][colon_position+2:scip_lines[5].index('%')] - ) + gap = float(scip_lines[5][colon_position + 2 : scip_lines[5].index('%')]) except ValueError: - - gap = scip_lines[5][colon_position+2:scip_lines[5].index('\n')] - + + gap = scip_lines[5][colon_position + 2 : scip_lines[5].index('\n')] + if gap == 'infinite': - + gap = float('inf') - + out_dict = { 'solver_status': solver_status, 'solving_time': solving_time, @@ -522,6 +496,6 @@ def read_scip_log(filename: str): 'primal_bound': primal_bound, 'dual_bound': dual_bound, 'gap': gap, - } - + } + return out_dict diff --git a/pyomo/solvers/plugins/solvers/XPRESS.py b/pyomo/solvers/plugins/solvers/XPRESS.py index 70ef36ee07a..6ab51cfbbf3 100644 --- a/pyomo/solvers/plugins/solvers/XPRESS.py +++ b/pyomo/solvers/plugins/solvers/XPRESS.py @@ -8,8 +8,7 @@ @SolverFactory.register('xpress', doc='The XPRESS LP/MIP solver') class XPRESS(OptSolver): - """The XPRESS LP/MIP solver - """ + """The XPRESS LP/MIP solver""" def __new__(cls, *args, **kwds): mode = kwds.pop('solver_io', 'python') @@ -17,8 +16,10 @@ def __new__(cls, *args, **kwds): mode = 'python' if mode not in {'python', 'direct', 'persistent'}: - logger.error('Pyomo currently only supports a Python interface to XPRESS. ' - 'Please use one of python, direct, or persistent for solver_io.') + logger.error( + 'Pyomo currently only supports a Python interface to XPRESS. ' + 'Please use one of python, direct, or persistent for solver_io.' + ) return if mode in ['python', 'direct']: opt = SolverFactory('xpress_direct', **kwds) diff --git a/pyomo/solvers/plugins/solvers/cplex_direct.py b/pyomo/solvers/plugins/solvers/cplex_direct.py index f38c56dcfec..c403fc1722b 100644 --- a/pyomo/solvers/plugins/solvers/cplex_direct.py +++ b/pyomo/solvers/plugins/solvers/cplex_direct.py @@ -21,7 +21,9 @@ from pyomo.core.staleflag import StaleFlagManager from pyomo.repn import generate_standard_repn from pyomo.solvers.plugins.solvers.direct_solver import DirectSolver -from pyomo.solvers.plugins.solvers.direct_or_persistent_solver import DirectOrPersistentSolver +from pyomo.solvers.plugins.solvers.direct_or_persistent_solver import ( + DirectOrPersistentSolver, +) from pyomo.core.kernel.objective import minimize, maximize from pyomo.opt.results.results_ import SolverResults from pyomo.opt.results.solution import Solution, SolutionStatus @@ -111,7 +113,6 @@ def store_in_cplex(self): @SolverFactory.register('cplex_direct', doc='Direct python interface to CPLEX') class CPLEXDirect(DirectSolver): - def __init__(self, **kwds): kwds['type'] = 'cplexdirect' DirectSolver.__init__(self, **kwds) @@ -123,10 +124,12 @@ def __init__(self, **kwds): def _init(self): try: import cplex + self._cplex = cplex self._python_api_exists = True self._version = tuple( - int(k) for k in self._cplex.Cplex().get_version().split('.')) + int(k) for k in self._cplex.Cplex().get_version().split('.') + ) while len(self._version) < 4: self._version += (0,) self._version = tuple(int(i) for i in self._version[:4]) @@ -166,68 +169,86 @@ def _apply_solver(self): # log file. Passing in an opened file object is supported at # least as far back as CPLEX 12.5.1 [the oldest version # supported by IBM as of 1 Oct 2020] - if self.version() >= (12, 5, 1) \ - and isinstance(self._log_file, str): + if self.version() >= (12, 5, 1) and isinstance(self._log_file, str): _log_file = (open(self._log_file, 'a'),) _close_log_file = True else: _log_file = (self._log_file,) _close_log_file = False if self._tee: + def _process_stream(arg): sys.stdout.write(arg) return arg + _log_file += (_process_stream,) try: self._solver_model.set_results_stream(*_log_file) if self._keepfiles: - print("Solver log file: "+self._log_file) - + print("Solver log file: " + self._log_file) + obj_degree = self._objective.expr.polynomial_degree() if obj_degree is None or obj_degree > 2: - raise DegreeError('CPLEXDirect does not support expressions of degree {0}.'\ - .format(obj_degree)) + raise DegreeError( + 'CPLEXDirect does not support expressions of degree {0}.'.format( + obj_degree + ) + ) elif obj_degree == 2: quadratic_objective = True else: quadratic_objective = False - + num_integer_vars = self._solver_model.variables.get_num_integer() num_binary_vars = self._solver_model.variables.get_num_binary() num_sos = self._solver_model.SOS.get_num() - + if self._solver_model.quadratic_constraints.get_num() != 0: quadratic_cons = True else: quadratic_cons = False - + if (num_integer_vars + num_binary_vars + num_sos) > 0: integer = True else: integer = False - + if integer: if quadratic_cons: - self._solver_model.set_problem_type(self._solver_model.problem_type.MIQCP) + self._solver_model.set_problem_type( + self._solver_model.problem_type.MIQCP + ) elif quadratic_objective: - self._solver_model.set_problem_type(self._solver_model.problem_type.MIQP) + self._solver_model.set_problem_type( + self._solver_model.problem_type.MIQP + ) else: - self._solver_model.set_problem_type(self._solver_model.problem_type.MILP) + self._solver_model.set_problem_type( + self._solver_model.problem_type.MILP + ) else: if quadratic_cons: - self._solver_model.set_problem_type(self._solver_model.problem_type.QCP) + self._solver_model.set_problem_type( + self._solver_model.problem_type.QCP + ) elif quadratic_objective: - self._solver_model.set_problem_type(self._solver_model.problem_type.QP) + self._solver_model.set_problem_type( + self._solver_model.problem_type.QP + ) else: - self._solver_model.set_problem_type(self._solver_model.problem_type.LP) + self._solver_model.set_problem_type( + self._solver_model.problem_type.LP + ) # if the user specifies a 'mipgap' # set cplex's mip.tolerances.mipgap if self.options.mipgap is not None: - self._solver_model.parameters.mip.tolerances.mipgap.set(float(self.options.mipgap)) - + self._solver_model.parameters.mip.tolerances.mipgap.set( + float(self.options.mipgap) + ) + for key, option in self.options.items(): - if key == 'mipgap': # handled above + if key == 'mipgap': # handled above continue opt_cmd = self._solver_model.parameters key_pieces = key.split('_') @@ -250,7 +271,7 @@ def _process_stream(arg): if not _is_numeric(option): raise opt_cmd.set(float(option)) - + t0 = time.time() self._solver_model.solve() t1 = time.time() @@ -302,7 +323,9 @@ def _get_expr_from_pyomo_expr(self, expr, max_degree=2): repn = generate_standard_repn(expr, quadratic=False) try: - cplex_expr, referenced_vars = self._get_expr_from_pyomo_repn(repn, max_degree) + cplex_expr, referenced_vars = self._get_expr_from_pyomo_repn( + repn, max_degree + ) except DegreeError as e: msg = e.args[0] msg += '\nexpr: {0}'.format(expr) @@ -351,10 +374,11 @@ def _set_instance(self, model, kwds={}): self._solver_model = self._cplex.Cplex() except Exception: e = sys.exc_info()[1] - msg = ("Unable to create CPLEX model. " - "Have you installed the Python " - "bindings for CPLEX?\n\n\t"+ - "Error message: {0}".format(e)) + msg = ( + "Unable to create CPLEX model. " + "Have you installed the Python " + "bindings for CPLEX?\n\n\t" + "Error message: {0}".format(e) + ) raise Exception(msg) self._add_block(model) @@ -371,7 +395,8 @@ def _set_instance(self, model, kwds={}): "the IO-option 'output_fixed_variable_bounds=True' " "to suppress this error and fix the variable " "by overwriting its bounds in the CPLEX instance." - % (var.name, self._pyomo_model.name,)) + % (var.name, self._pyomo_model.name) + ) def _add_block(self, block): var_data = _VariableData(self._solver_model) @@ -384,10 +409,7 @@ def _add_block(self, block): lin_con_data = _LinearConstraintData(self._solver_model) for sub_block in block.block_data_objects(descend_into=True, active=True): for con in sub_block.component_data_objects( - ctype=Constraint, - descend_into=False, - active=True, - sort=True, + ctype=Constraint, descend_into=False, active=True, sort=True ): if not con.has_lb() and not con.has_ub(): assert not con.equality @@ -396,18 +418,13 @@ def _add_block(self, block): self._add_constraint(con, lin_con_data) for con in sub_block.component_data_objects( - ctype=SOSConstraint, - descend_into=False, - active=True, - sort=True, + ctype=SOSConstraint, descend_into=False, active=True, sort=True ): self._add_sos_constraint(con) obj_counter = 0 for obj in sub_block.component_data_objects( - ctype=Objective, - descend_into=False, - active=True, + ctype=Objective, descend_into=False, active=True ): obj_counter += 1 if obj_counter > 1: @@ -479,18 +496,22 @@ def _add_constraint(self, con, lin_con_data=None): cplex_lin_con_data.store_in_cplex() else: if sense == 'R': - raise ValueError("The CPLEXDirect interface does not " - "support quadratic range constraints: " - "{0}".format(con)) + raise ValueError( + "The CPLEXDirect interface does not " + "support quadratic range constraints: " + "{0}".format(con) + ) self._solver_model.quadratic_constraints.add( - lin_expr=[cplex_expr.variables, - cplex_expr.coefficients], - quad_expr=[cplex_expr.q_variables1, - cplex_expr.q_variables2, - cplex_expr.q_coefficients], + lin_expr=[cplex_expr.variables, cplex_expr.coefficients], + quad_expr=[ + cplex_expr.q_variables1, + cplex_expr.q_variables2, + cplex_expr.q_coefficients, + ], sense=sense, rhs=rhs, - name=conname) + name=conname, + ) for var in referenced_vars: self._referenced_variables[var] += 1 @@ -509,8 +530,9 @@ def _add_sos_constraint(self, con): elif level == 2: sos_type = self._solver_model.SOS.type.SOS2 else: - raise ValueError("Solver does not support SOS " - "level {0} constraints".format(level)) + raise ValueError( + "Solver does not support SOS " "level {0} constraints".format(level) + ) cplex_vars = [] weights = [] @@ -530,7 +552,9 @@ def _add_sos_constraint(self, con): self._referenced_variables[v] += 1 weights.append(w) - self._solver_model.SOS.add(type=sos_type, SOS=[cplex_vars, weights], name=conname) + self._solver_model.SOS.add( + type=sos_type, SOS=[cplex_vars, weights], name=conname + ) self._pyomo_con_to_solver_con_map[con] = conname self._solver_con_to_pyomo_con_map[conname] = con @@ -547,7 +571,9 @@ def _cplex_vtype_from_var(self, var): elif var.is_continuous(): vtype = self._solver_model.variables.type.continuous else: - raise ValueError('Variable domain type is not recognized for {0}'.format(var.domain)) + raise ValueError( + 'Variable domain type is not recognized for {0}'.format(var.domain) + ) return vtype def _set_objective(self, obj): @@ -567,7 +593,9 @@ def _set_objective(self, obj): else: raise ValueError('Objective sense is not recognized: {0}'.format(obj.sense)) - cplex_expr, referenced_vars = self._get_expr_from_pyomo_expr(obj.expr, self._max_obj_degree) + cplex_expr, referenced_vars = self._get_expr_from_pyomo_expr( + obj.expr, self._max_obj_degree + ) for i in range(len(cplex_expr.q_coefficients)): cplex_expr.q_coefficients[i] *= 2 @@ -579,7 +607,9 @@ def _set_objective(self, obj): self._solver_model.objective.set_offset(cplex_expr.offset) linear_objective_already_exists = any(self._solver_model.objective.get_linear()) - quadratic_objective_already_exists = self._solver_model.objective.get_num_quadratic_nonzeros() + quadratic_objective_already_exists = ( + self._solver_model.objective.get_num_quadratic_nonzeros() + ) contains_linear_terms = any(cplex_expr.coefficients) contains_quadratic_terms = any(cplex_expr.q_coefficients) @@ -589,7 +619,9 @@ def _set_objective(self, obj): self._solver_model.objective.set_linear([(i, 0.0) for i in range(num_cols)]) if contains_linear_terms: - self._solver_model.objective.set_linear(list(zip(cplex_expr.variables, cplex_expr.coefficients))) + self._solver_model.objective.set_linear( + list(zip(cplex_expr.variables, cplex_expr.coefficients)) + ) if quadratic_objective_already_exists or contains_quadratic_terms: self._solver_model.objective.set_quadratic([0.0] * num_cols) @@ -600,7 +632,7 @@ def _set_objective(self, obj): zip( cplex_expr.q_variables1, cplex_expr.q_variables2, - cplex_expr.q_coefficients + cplex_expr.q_coefficients, ) ) ) @@ -628,14 +660,19 @@ def _postsolve(self): extract_reduced_costs = True flag = True if not flag: - raise RuntimeError("***The cplex_direct solver plugin cannot extract solution suffix="+suffix) + raise RuntimeError( + "***The cplex_direct solver plugin cannot extract solution suffix=" + + suffix + ) cpxprob = self._solver_model status = cpxprob.solution.get_status() - if cpxprob.get_problem_type() in [cpxprob.problem_type.MILP, - cpxprob.problem_type.MIQP, - cpxprob.problem_type.MIQCP]: + if cpxprob.get_problem_type() in [ + cpxprob.problem_type.MILP, + cpxprob.problem_type.MIQP, + cpxprob.problem_type.MIQCP, + ]: if extract_reduced_costs: logger.warning("Cannot get reduced costs for MIP.") if extract_duals: @@ -646,7 +683,7 @@ def _postsolve(self): self.results = SolverResults() soln = Solution() - self.results.solver.name = ("CPLEX {0}".format(cpxprob.get_version())) + self.results.solver.name = "CPLEX {0}".format(cpxprob.get_version()) self.results.solver.wallclock_time = self._wallclock_time if status in [1, 101, 102]: @@ -661,8 +698,9 @@ def _postsolve(self): # Note: status of 4 means infeasible or unbounded # and 119 means MIP infeasible or unbounded self.results.solver.status = SolverStatus.warning - self.results.solver.termination_condition = \ + self.results.solver.termination_condition = ( TerminationCondition.infeasibleOrUnbounded + ) soln.status = SolutionStatus.unsure elif status in [3, 103]: self.results.solver.status = SolverStatus.warning @@ -670,11 +708,15 @@ def _postsolve(self): soln.status = SolutionStatus.infeasible elif status in [10]: self.results.solver.status = SolverStatus.aborted - self.results.solver.termination_condition = TerminationCondition.maxIterations + self.results.solver.termination_condition = ( + TerminationCondition.maxIterations + ) soln.status = SolutionStatus.stoppedByLimit elif status in [11, 25, 107, 131]: self.results.solver.status = SolverStatus.aborted - self.results.solver.termination_condition = TerminationCondition.maxTimeLimit + self.results.solver.termination_condition = ( + TerminationCondition.maxTimeLimit + ) soln.status = SolutionStatus.stoppedByLimit else: self.results.solver.status = SolverStatus.error @@ -686,44 +728,69 @@ def _postsolve(self): elif cpxprob.objective.get_sense() == cpxprob.objective.sense.maximize: self.results.problem.sense = maximize else: - raise RuntimeError('Unrecognized cplex objective sense: {0}'.\ - format(cpxprob.objective.get_sense())) + raise RuntimeError( + 'Unrecognized cplex objective sense: {0}'.format( + cpxprob.objective.get_sense() + ) + ) self.results.problem.upper_bound = None self.results.problem.lower_bound = None if cpxprob.solution.get_solution_type() != cpxprob.solution.type.none: - if (cpxprob.variables.get_num_binary() + cpxprob.variables.get_num_integer()) == 0: - self.results.problem.upper_bound = cpxprob.solution.get_objective_value() - self.results.problem.lower_bound = cpxprob.solution.get_objective_value() + if ( + cpxprob.variables.get_num_binary() + cpxprob.variables.get_num_integer() + ) == 0: + self.results.problem.upper_bound = ( + cpxprob.solution.get_objective_value() + ) + self.results.problem.lower_bound = ( + cpxprob.solution.get_objective_value() + ) elif cpxprob.objective.get_sense() == cpxprob.objective.sense.minimize: - self.results.problem.upper_bound = cpxprob.solution.get_objective_value() - self.results.problem.lower_bound = cpxprob.solution.MIP.get_best_objective() + self.results.problem.upper_bound = ( + cpxprob.solution.get_objective_value() + ) + self.results.problem.lower_bound = ( + cpxprob.solution.MIP.get_best_objective() + ) else: assert cpxprob.objective.get_sense() == cpxprob.objective.sense.maximize - self.results.problem.upper_bound = cpxprob.solution.MIP.get_best_objective() - self.results.problem.lower_bound = cpxprob.solution.get_objective_value() + self.results.problem.upper_bound = ( + cpxprob.solution.MIP.get_best_objective() + ) + self.results.problem.lower_bound = ( + cpxprob.solution.get_objective_value() + ) try: - soln.gap = self.results.problem.upper_bound - self.results.problem.lower_bound + soln.gap = ( + self.results.problem.upper_bound - self.results.problem.lower_bound + ) except TypeError: soln.gap = None self.results.problem.name = cpxprob.get_problem_name() assert cpxprob.indicator_constraints.get_num() == 0 - self.results.problem.number_of_constraints = \ - (cpxprob.linear_constraints.get_num() + - cpxprob.quadratic_constraints.get_num() + - cpxprob.SOS.get_num()) + self.results.problem.number_of_constraints = ( + cpxprob.linear_constraints.get_num() + + cpxprob.quadratic_constraints.get_num() + + cpxprob.SOS.get_num() + ) self.results.problem.number_of_nonzeros = None self.results.problem.number_of_variables = cpxprob.variables.get_num() - self.results.problem.number_of_binary_variables = cpxprob.variables.get_num_binary() - self.results.problem.number_of_integer_variables = cpxprob.variables.get_num_integer() + self.results.problem.number_of_binary_variables = ( + cpxprob.variables.get_num_binary() + ) + self.results.problem.number_of_integer_variables = ( + cpxprob.variables.get_num_integer() + ) assert cpxprob.variables.get_num_semiinteger() == 0 assert cpxprob.variables.get_num_semicontinuous() == 0 - self.results.problem.number_of_continuous_variables = \ - (cpxprob.variables.get_num() - - cpxprob.variables.get_num_binary() - - cpxprob.variables.get_num_integer()) + self.results.problem.number_of_continuous_variables = ( + cpxprob.variables.get_num() + - cpxprob.variables.get_num_binary() + - cpxprob.variables.get_num_integer() + ) self.results.problem.number_of_objectives = 1 # only try to get objective and variable values if a solution exists @@ -745,7 +812,9 @@ def _postsolve(self): soln_variables[name] = {"Value": val} if extract_reduced_costs: - reduced_costs = self._solver_model.solution.get_reduced_costs(var_names) + reduced_costs = self._solver_model.solution.get_reduced_costs( + var_names + ) for i, name in enumerate(var_names): pyomo_var = self._solver_var_to_pyomo_var_map[name] if self._referenced_variables[pyomo_var] > 0: @@ -754,7 +823,9 @@ def _postsolve(self): if extract_slacks: for con_name in self._solver_model.linear_constraints.get_names(): soln_constraints[con_name] = {} - for con_name in self._solver_model.quadratic_constraints.get_names(): + for ( + con_name + ) in self._solver_model.quadratic_constraints.get_names(): soln_constraints[con_name] = {} elif extract_duals: # CPLEX PYTHON API DOES NOT SUPPORT QUADRATIC DUAL COLLECTION @@ -763,16 +834,22 @@ def _postsolve(self): if extract_duals: dual_values = self._solver_model.solution.get_dual_values() - for i, con_name in enumerate(self._solver_model.linear_constraints.get_names()): + for i, con_name in enumerate( + self._solver_model.linear_constraints.get_names() + ): soln_constraints[con_name]["Dual"] = dual_values[i] if extract_slacks: linear_slacks = self._solver_model.solution.get_linear_slacks() qudratic_slacks = self._solver_model.solution.get_quadratic_slacks() - for i, con_name in enumerate(self._solver_model.linear_constraints.get_names()): + for i, con_name in enumerate( + self._solver_model.linear_constraints.get_names() + ): pyomo_con = self._solver_con_to_pyomo_con_map[con_name] if pyomo_con in self._range_constraints: - R_ = self._solver_model.linear_constraints.get_range_values(con_name) + R_ = self._solver_model.linear_constraints.get_range_values( + con_name + ) if R_ == 0: soln_constraints[con_name]["Slack"] = linear_slacks[i] else: @@ -784,7 +861,9 @@ def _postsolve(self): soln_constraints[con_name]["Slack"] = -Ls_ else: soln_constraints[con_name]["Slack"] = linear_slacks[i] - for i, con_name in enumerate(self._solver_model.quadratic_constraints.get_names()): + for i, con_name in enumerate( + self._solver_model.quadratic_constraints.get_names() + ): soln_constraints[con_name]["Slack"] = qudratic_slacks[i] elif self._load_solutions: if cpxprob.solution.get_solution_type() > 0: @@ -814,9 +893,11 @@ def _warm_start(self): # here warm start means MIP start, which we can not add # if the problem type is not discrete cpxprob = self._solver_model - if cpxprob.get_problem_type() in [cpxprob.problem_type.MILP, - cpxprob.problem_type.MIQP, - cpxprob.problem_type.MIQCP]: + if cpxprob.get_problem_type() in [ + cpxprob.problem_type.MILP, + cpxprob.problem_type.MIQP, + cpxprob.problem_type.MIQCP, + ]: var_names = [] var_values = [] for pyomo_var, cplex_var in self._pyomo_var_to_solver_var_map.items(): @@ -827,7 +908,8 @@ def _warm_start(self): if len(var_names): self._solver_model.MIP_starts.add( [var_names, var_values], - self._solver_model.MIP_starts.effort_level.auto) + self._solver_model.MIP_starts.effort_level.auto, + ) def _load_vars(self, vars_to_load=None): var_map = self._pyomo_var_to_ndx_map @@ -870,7 +952,9 @@ def _load_duals(self, cons_to_load=None): vals = self._solver_model.solution.get_dual_values() else: cplex_cons_to_load = set([con_map[pyomo_con] for pyomo_con in cons_to_load]) - linear_cons_to_load = cplex_cons_to_load.intersection(set(self._solver_model.linear_constraints.get_names())) + linear_cons_to_load = cplex_cons_to_load.intersection( + set(self._solver_model.linear_constraints.get_names()) + ) vals = self._solver_model.solution.get_dual_values(linear_cons_to_load) for i, cplex_con in enumerate(linear_cons_to_load): @@ -887,14 +971,24 @@ def _load_slacks(self, cons_to_load=None): if cons_to_load is None: linear_cons_to_load = self._solver_model.linear_constraints.get_names() linear_vals = self._solver_model.solution.get_linear_slacks() - quadratic_cons_to_load = self._solver_model.quadratic_constraints.get_names() + quadratic_cons_to_load = ( + self._solver_model.quadratic_constraints.get_names() + ) quadratic_vals = self._solver_model.solution.get_quadratic_slacks() else: cplex_cons_to_load = set([con_map[pyomo_con] for pyomo_con in cons_to_load]) - linear_cons_to_load = cplex_cons_to_load.intersection(set(self._solver_model.linear_constraints.get_names())) - linear_vals = self._solver_model.solution.get_linear_slacks(linear_cons_to_load) - quadratic_cons_to_load = cplex_cons_to_load.intersection(set(self._solver_model.quadratic_constraints.get_names())) - quadratic_vals = self._solver_model.solution.get_quadratic_slacks(quadratic_cons_to_load) + linear_cons_to_load = cplex_cons_to_load.intersection( + set(self._solver_model.linear_constraints.get_names()) + ) + linear_vals = self._solver_model.solution.get_linear_slacks( + linear_cons_to_load + ) + quadratic_cons_to_load = cplex_cons_to_load.intersection( + set(self._solver_model.quadratic_constraints.get_names()) + ) + quadratic_vals = self._solver_model.solution.get_quadratic_slacks( + quadratic_cons_to_load + ) for i, cplex_con in enumerate(linear_cons_to_load): pyomo_con = reverse_con_map[cplex_con] diff --git a/pyomo/solvers/plugins/solvers/cplex_persistent.py b/pyomo/solvers/plugins/solvers/cplex_persistent.py index b2ca7ba5ece..17e0ed6ec38 100644 --- a/pyomo/solvers/plugins/solvers/cplex_persistent.py +++ b/pyomo/solvers/plugins/solvers/cplex_persistent.py @@ -54,7 +54,9 @@ def _remove_constraint(self, solver_con): try: self._solver_model.quadratic_constraints.delete(solver_con) except self._cplex.exceptions.CplexError: - raise ValueError('Failed to find the cplex constraint {0}'.format(solver_con)) + raise ValueError( + 'Failed to find the cplex constraint {0}'.format(solver_con) + ) def _remove_sos_constraint(self, solver_sos_con): self._solver_model.SOS.delete(solver_sos_con) @@ -86,12 +88,16 @@ def update_var(self, var): # see PR #366 for discussion about handling indexed # objects and keeping compatibility with the # pyomo.kernel objects - #if var.is_indexed(): + # if var.is_indexed(): # for child_var in var.values(): # self.compile_var(child_var) # return if var not in self._pyomo_var_to_solver_var_map: - raise ValueError('The Var provided to compile_var needs to be added first: {0}'.format(var)) + raise ValueError( + 'The Var provided to compile_var needs to be added first: {0}'.format( + var + ) + ) cplex_var = self._pyomo_var_to_solver_var_map[var] vtype = self._cplex_vtype_from_var(var) lb, ub = self._cplex_lb_ub_from_var(var) @@ -117,9 +123,9 @@ def _add_column(self, var, obj_coef, constraints, coefficients): """Add a column to the solver's model This will add the Pyomo variable var to the solver's - model, and put the coefficients on the associated + model, and put the coefficients on the associated constraints in the solver model. If the obj_coef is - not zero, it will add obj_coef*var to the objective + not zero, it will add obj_coef*var to the objective of the solver's model. Parameters @@ -136,8 +142,14 @@ def _add_column(self, var, obj_coef, constraints, coefficients): lb, ub = self._cplex_lb_ub_from_var(var) ## do column addition - self._solver_model.variables.add(obj=[obj_coef], lb=[lb], ub=[ub], types=[vtype], names=[varname], - columns=[self._cplex.SparsePair(ind=constraints, val=coefficients)]) + self._solver_model.variables.add( + obj=[obj_coef], + lb=[lb], + ub=[ub], + types=[vtype], + names=[varname], + columns=[self._cplex.SparsePair(ind=constraints, val=coefficients)], + ) self._pyomo_var_to_solver_var_map[var] = varname self._solver_var_to_pyomo_var_map[varname] = var diff --git a/pyomo/solvers/plugins/solvers/direct_or_persistent_solver.py b/pyomo/solvers/plugins/solvers/direct_or_persistent_solver.py index ad88d161c15..bdba4b2b4d7 100644 --- a/pyomo/solvers/plugins/solvers/direct_or_persistent_solver.py +++ b/pyomo/solvers/plugins/solvers/direct_or_persistent_solver.py @@ -42,6 +42,7 @@ class DirectOrPersistentSolver(OptSolver): options: dict Dictionary of solver options """ + def __init__(self, **kwds): OptSolver.__init__(self, **kwds) @@ -145,8 +146,9 @@ def _presolve(self, **kwds): # BIG HACK (see pyomo.core.kernel write function) if not hasattr(self._pyomo_model, "._symbol_maps"): setattr(self._pyomo_model, "._symbol_maps", {}) - getattr(self._pyomo_model, - "._symbol_maps")[self._smap_id] = self._symbol_map + getattr(self._pyomo_model, "._symbol_maps")[ + self._smap_id + ] = self._symbol_map else: self._pyomo_model.solutions.add_symbol_map(self._symbol_map) # *********************************************************** @@ -155,30 +157,42 @@ def _presolve(self, **kwds): if self.warm_start_capable(): self._warm_start() else: - raise ValueError('{0} solver plugin is not capable of warmstart.'.format(type(self))) + raise ValueError( + '{0} solver plugin is not capable of warmstart.'.format(type(self)) + ) if self._log_file is None: self._log_file = TempfileManager.create_tempfile(suffix='.log') """ This method should be implemented by subclasses.""" + def _apply_solver(self): raise NotImplementedError('This method should be implemented by subclasses') """ This method should be implemented by subclasses.""" + def _postsolve(self): return OptSolver._postsolve(self) """ This method should be implemented by subclasses.""" + def _set_instance(self, model, kwds={}): if not isinstance(model, (Model, IBlock, Block, _BlockData)): - msg = "The problem instance supplied to the {0} plugin " \ - "'_presolve' method must be a Model or a Block".format(type(self)) + msg = ( + "The problem instance supplied to the {0} plugin " + "'_presolve' method must be a Model or a Block".format(type(self)) + ) raise ValueError(msg) self._pyomo_model = model - self._symbolic_solver_labels = kwds.pop('symbolic_solver_labels', self._symbolic_solver_labels) - self._skip_trivial_constraints = kwds.pop('skip_trivial_constraints', self._skip_trivial_constraints) - self._output_fixed_variable_bounds = kwds.pop('output_fixed_variable_bounds', - self._output_fixed_variable_bounds) + self._symbolic_solver_labels = kwds.pop( + 'symbolic_solver_labels', self._symbolic_solver_labels + ) + self._skip_trivial_constraints = kwds.pop( + 'skip_trivial_constraints', self._skip_trivial_constraints + ) + self._output_fixed_variable_bounds = kwds.pop( + 'output_fixed_variable_bounds', self._output_fixed_variable_bounds + ) self._pyomo_var_to_solver_var_map = ComponentMap() self._solver_var_to_pyomo_var_map = dict() self._pyomo_con_to_solver_con_map = dict() @@ -198,77 +212,77 @@ def _set_instance(self, model, kwds={}): def _add_block(self, block): for var in block.component_data_objects( - ctype=pyomo.core.base.var.Var, - descend_into=True, - active=True, - sort=True): + ctype=pyomo.core.base.var.Var, descend_into=True, active=True, sort=True + ): self._add_var(var) - for sub_block in block.block_data_objects(descend_into=True, - active=True): + for sub_block in block.block_data_objects(descend_into=True, active=True): for con in sub_block.component_data_objects( - ctype=pyomo.core.base.constraint.Constraint, - descend_into=False, - active=True, - sort=True): - if (not con.has_lb()) and \ - (not con.has_ub()): + ctype=pyomo.core.base.constraint.Constraint, + descend_into=False, + active=True, + sort=True, + ): + if (not con.has_lb()) and (not con.has_ub()): assert not con.equality continue # non-binding, so skip self._add_constraint(con) for con in sub_block.component_data_objects( - ctype=pyomo.core.base.sos.SOSConstraint, - descend_into=False, - active=True, - sort=True): + ctype=pyomo.core.base.sos.SOSConstraint, + descend_into=False, + active=True, + sort=True, + ): self._add_sos_constraint(con) obj_counter = 0 for obj in sub_block.component_data_objects( - ctype=pyomo.core.base.objective.Objective, - descend_into=False, - active=True): + ctype=pyomo.core.base.objective.Objective, + descend_into=False, + active=True, + ): obj_counter += 1 if obj_counter > 1: - raise ValueError("Solver interface does not " - "support multiple objectives.") + raise ValueError( + "Solver interface does not " "support multiple objectives." + ) self._set_objective(obj) """ This method should be implemented by subclasses.""" + def _set_objective(self, obj): - raise NotImplementedError("This method should be implemented " - "by subclasses") + raise NotImplementedError("This method should be implemented " "by subclasses") """ This method should be implemented by subclasses.""" + def _add_constraint(self, con): - raise NotImplementedError("This method should be implemented " - "by subclasses") + raise NotImplementedError("This method should be implemented " "by subclasses") """ This method should be implemented by subclasses.""" + def _add_sos_constraint(self, con): - raise NotImplementedError("This method should be implemented " - "by subclasses") + raise NotImplementedError("This method should be implemented " "by subclasses") """ This method should be implemented by subclasses.""" + def _add_var(self, var): - raise NotImplementedError("This method should be implemented " - "by subclasses") + raise NotImplementedError("This method should be implemented " "by subclasses") """ This method should be implemented by subclasses.""" + def _get_expr_from_pyomo_repn(self, repn, max_degree=None): - raise NotImplementedError("This method should be implemented " - "by subclasses") + raise NotImplementedError("This method should be implemented " "by subclasses") """ This method should be implemented by subclasses.""" + def _get_expr_from_pyomo_expr(self, expr, max_degree=None): - raise NotImplementedError("This method should be implemented " - "by subclasses") + raise NotImplementedError("This method should be implemented " "by subclasses") """ This method should be implemented by subclasses.""" + def _load_vars(self, vars_to_load): - raise NotImplementedError("This method should be implemented " - "by subclasses") + raise NotImplementedError("This method should be implemented " "by subclasses") def load_vars(self, vars_to_load=None): """ @@ -282,11 +296,14 @@ def load_vars(self, vars_to_load=None): StaleFlagManager.mark_all_as_stale(delayed=True) """ This method should be implemented by subclasses.""" + def warm_start_capable(self): raise NotImplementedError('This method should be implemented by subclasses') def _warm_start(self): - raise NotImplementedError('If a subclass can warmstart, then it should implement this method.') + raise NotImplementedError( + 'If a subclass can warmstart, then it should implement this method.' + ) def available(self, exception_flag=True): """True if the solver is available.""" @@ -294,8 +311,8 @@ def available(self, exception_flag=True): _api = getattr(self, '_python_api_exists', False) if exception_flag and not _api: raise ApplicationError( - "No Python bindings available for %s solver plugin" - % (type(self),)) + "No Python bindings available for %s solver plugin" % (type(self),) + ) return bool(_api) def _get_version(self): diff --git a/pyomo/solvers/plugins/solvers/direct_solver.py b/pyomo/solvers/plugins/solvers/direct_solver.py index 3064ca48154..fffec5f8190 100644 --- a/pyomo/solvers/plugins/solvers/direct_solver.py +++ b/pyomo/solvers/plugins/solvers/direct_solver.py @@ -12,7 +12,9 @@ import time import logging -from pyomo.solvers.plugins.solvers.direct_or_persistent_solver import DirectOrPersistentSolver +from pyomo.solvers.plugins.solvers.direct_or_persistent_solver import ( + DirectOrPersistentSolver, +) from pyomo.core.base.block import _BlockData from pyomo.core.kernel.block import IBlock from pyomo.core.base.suffix import active_import_suffix_generator @@ -22,6 +24,7 @@ logger = logging.getLogger('pyomo.solvers') + class DirectSolver(DirectOrPersistentSolver): """ Subclasses need to: @@ -56,8 +59,10 @@ def _presolve(self, *args, **kwds): """ model = args[0] if len(args) != 1: - msg = ("The {0} plugin method '_presolve' must be supplied a single problem instance - {1} were " + - "supplied.").format(type(self), len(args)) + msg = ( + "The {0} plugin method '_presolve' must be supplied a single problem instance - {1} were " + + "supplied." + ).format(type(self), len(args)) raise ValueError(msg) self._set_instance(model, kwds) @@ -65,7 +70,7 @@ def _presolve(self, *args, **kwds): DirectOrPersistentSolver._presolve(self, **kwds) def solve(self, *args, **kwds): - """ Solve the problem """ + """Solve the problem""" self.available(exception_flag=True) # @@ -79,21 +84,26 @@ def solve(self, *args, **kwds): if not arg.is_constructed(): raise RuntimeError( "Attempting to solve model=%s with unconstructed " - "component(s)" % (arg.name,) ) + "component(s)" % (arg.name,) + ) _model = arg # import suffixes must be on the top-level model if isinstance(arg, _BlockData): - model_suffixes = list(name for (name,comp) in active_import_suffix_generator(arg)) + model_suffixes = list( + name for (name, comp) in active_import_suffix_generator(arg) + ) else: assert isinstance(arg, IBlock) - model_suffixes = list(comp.storage_key for comp in - import_suffix_generator(arg, - active=True, - descend_into=False)) + model_suffixes = list( + comp.storage_key + for comp in import_suffix_generator( + arg, active=True, descend_into=False + ) + ) if len(model_suffixes) > 0: - kwds_suffixes = kwds.setdefault('suffixes',[]) + kwds_suffixes = kwds.setdefault('suffixes', []) for name in model_suffixes: if name not in kwds_suffixes: kwds_suffixes.append(name) @@ -111,7 +121,8 @@ def solve(self, *args, **kwds): self.options.update(orig_options) self.options.update(kwds.pop('options', {})) self.options.update( - self._options_string_to_dict(kwds.pop('options_string', ''))) + self._options_string_to_dict(kwds.pop('options_string', '')) + ) try: # we're good to go. @@ -121,7 +132,10 @@ def solve(self, *args, **kwds): presolve_completion_time = time.time() if self._report_timing: - print(" %6.2f seconds required for presolve" % (presolve_completion_time - initial_time)) + print( + " %6.2f seconds required for presolve" + % (presolve_completion_time - initial_time) + ) if not _model is None: self._initialize_callbacks(_model) @@ -133,21 +147,24 @@ def solve(self, *args, **kwds): logger.warning( "Solver (%s) did not return a solver status code.\n" "This is indicative of an internal solver plugin error.\n" - "Please report this to the Pyomo developers." ) + "Please report this to the Pyomo developers." + ) elif _status.rc: logger.error( "Solver (%s) returned non-zero return code (%s)" - % (self.name, _status.rc,)) + % (self.name, _status.rc) + ) if self._tee: - logger.error( - "See the solver log above for diagnostic information." ) + logger.error("See the solver log above for diagnostic information.") elif hasattr(_status, 'log') and _status.log: logger.error("Solver log:\n" + str(_status.log)) - raise ApplicationError( - "Solver (%s) did not exit normally" % self.name) + raise ApplicationError("Solver (%s) did not exit normally" % self.name) solve_completion_time = time.time() if self._report_timing: - print(" %6.2f seconds required for solver" % (solve_completion_time - presolve_completion_time)) + print( + " %6.2f seconds required for solver" + % (solve_completion_time - presolve_completion_time) + ) result = self._postsolve() # *********************************************************** @@ -159,10 +176,12 @@ def solve(self, *args, **kwds): if _model: if isinstance(_model, IBlock): if len(result.solution) == 1: - result.solution(0).symbol_map = \ - getattr(_model, "._symbol_maps")[result._smap_id] - result.solution(0).default_variable_value = \ - self._default_variable_value + result.solution(0).symbol_map = getattr( + _model, "._symbol_maps" + )[result._smap_id] + result.solution( + 0 + ).default_variable_value = self._default_variable_value if self._load_solutions: _model.load_solution(result.solution(0)) else: @@ -173,15 +192,15 @@ def solve(self, *args, **kwds): assert len(getattr(_model, "._symbol_maps")) == 1 delattr(_model, "._symbol_maps") del result._smap_id - if self._load_solutions and \ - (len(result.solution) == 0): + if self._load_solutions and (len(result.solution) == 0): logger.error("No solution is available") else: if self._load_solutions: _model.solutions.load_from( result, select=self._select_index, - default_variable_value=self._default_variable_value) + default_variable_value=self._default_variable_value, + ) result._smap_id = None result.solution.clear() else: @@ -191,7 +210,10 @@ def solve(self, *args, **kwds): postsolve_completion_time = time.time() if self._report_timing: - print(" %6.2f seconds required for postsolve" % (postsolve_completion_time - solve_completion_time)) + print( + " %6.2f seconds required for postsolve" + % (postsolve_completion_time - solve_completion_time) + ) finally: # @@ -200,5 +222,3 @@ def solve(self, *args, **kwds): self.options = orig_options return result - - diff --git a/pyomo/solvers/plugins/solvers/gurobi_direct.py b/pyomo/solvers/plugins/solvers/gurobi_direct.py index c2a73946197..80a0f5214bd 100644 --- a/pyomo/solvers/plugins/solvers/gurobi_direct.py +++ b/pyomo/solvers/plugins/solvers/gurobi_direct.py @@ -23,7 +23,9 @@ from pyomo.core.staleflag import StaleFlagManager from pyomo.repn import generate_standard_repn from pyomo.solvers.plugins.solvers.direct_solver import DirectSolver -from pyomo.solvers.plugins.solvers.direct_or_persistent_solver import DirectOrPersistentSolver +from pyomo.solvers.plugins.solvers.direct_or_persistent_solver import ( + DirectOrPersistentSolver, +) from pyomo.core.kernel.objective import minimize, maximize from pyomo.opt.results.results_ import SolverResults from pyomo.opt.results.solution import Solution, SolutionStatus @@ -39,6 +41,7 @@ class DegreeError(ValueError): pass + def _is_numeric(x): try: float(x) @@ -57,6 +60,7 @@ def _parse_gurobi_version(gurobipy, avail): GurobiDirect._version = GurobiDirect._version[:4] GurobiDirect._version_major = GurobiDirect._version[0] + gurobipy, gurobipy_available = attempt_import( 'gurobipy', # Other forms of exceptions can be thrown by the gurobi python @@ -123,8 +127,8 @@ def available(self, exception_flag=True): if exception_flag: gurobipy.log_import_warning(logger=__name__) raise ApplicationError( - "No Python bindings available for %s solver plugin" - % (type(self),)) + "No Python bindings available for %s solver plugin" % (type(self),) + ) return False if self._verified_license is None: with capture_output(capture_fd=True) as OUT: @@ -136,16 +140,17 @@ def available(self, exception_flag=True): m.dispose() GurobiDirect._verified_license = True except Exception as e: - GurobiDirect._import_messages += \ + GurobiDirect._import_messages += ( "\nCould not create Model - gurobi message=%s\n" % (e,) + ) GurobiDirect._verified_license = False if OUT.getvalue(): GurobiDirect._import_messages += "\n" + OUT.getvalue() if exception_flag and not self._verified_license: logger.warning(GurobiDirect._import_messages) raise ApplicationError( - "Could not create a gurobipy Model for %s solver plugin" - % (type(self),)) + "Could not create a gurobipy Model for %s solver plugin" % (type(self),) + ) return self._verified_license def _apply_solver(self): @@ -159,7 +164,7 @@ def _apply_solver(self): if self._keepfiles: # Only save log file when the user wants to keep it. self._solver_model.setParam('LogFile', self._log_file) - print("Solver log file: "+self._log_file) + print("Solver log file: " + self._log_file) # Options accepted by gurobi (case insensitive): # ['Cutoff', 'IterationLimit', 'NodeLimit', 'SolutionLimit', 'TimeLimit', @@ -201,7 +206,7 @@ def _apply_solver(self): self._solver_model.optimize(self._callback) self._needs_updated = False - + if self._keepfiles: # Change LogFile to make Gurobi close the original log file. # May not work for all Gurobi versions, like ver. 9.5.0. @@ -215,17 +220,28 @@ def _get_expr_from_pyomo_repn(self, repn, max_degree=2): degree = repn.polynomial_degree() if (degree is None) or (degree > max_degree): - raise DegreeError('GurobiDirect does not support expressions of degree {0}.'.format(degree)) + raise DegreeError( + 'GurobiDirect does not support expressions of degree {0}.'.format( + degree + ) + ) if len(repn.linear_vars) > 0: referenced_vars.update(repn.linear_vars) - new_expr = gurobipy.LinExpr(repn.linear_coefs, [self._pyomo_var_to_solver_var_map[i] for i in repn.linear_vars]) + new_expr = gurobipy.LinExpr( + repn.linear_coefs, + [self._pyomo_var_to_solver_var_map[i] for i in repn.linear_vars], + ) else: new_expr = 0.0 - for i,v in enumerate(repn.quadratic_vars): - x,y = v - new_expr += repn.quadratic_coefs[i] * self._pyomo_var_to_solver_var_map[x] * self._pyomo_var_to_solver_var_map[y] + for i, v in enumerate(repn.quadratic_vars): + x, y = v + new_expr += ( + repn.quadratic_coefs[i] + * self._pyomo_var_to_solver_var_map[x] + * self._pyomo_var_to_solver_var_map[y] + ) referenced_vars.add(x) referenced_vars.add(y) @@ -240,7 +256,9 @@ def _get_expr_from_pyomo_expr(self, expr, max_degree=2): repn = generate_standard_repn(expr, quadratic=False) try: - gurobi_expr, referenced_vars = self._get_expr_from_pyomo_repn(repn, max_degree) + gurobi_expr, referenced_vars = self._get_expr_from_pyomo_repn( + repn, max_degree + ) except DegreeError as e: msg = e.args[0] msg += '\nexpr: {0}'.format(expr) @@ -267,7 +285,9 @@ def _add_var(self, var): vtype = self._gurobi_vtype_from_var(var) lb, ub = self._gurobi_lb_ub_from_var(var) - gurobipy_var = self._solver_model.addVar(lb=lb, ub=ub, vtype=vtype, name=varname) + gurobipy_var = self._solver_model.addVar( + lb=lb, ub=ub, vtype=vtype, name=varname + ) self._pyomo_var_to_solver_var_map[var] = gurobipy_var self._solver_var_to_pyomo_var_map[gurobipy_var] = var @@ -289,10 +309,11 @@ def _set_instance(self, model, kwds={}): self._solver_model = gurobipy.Model() except Exception: e = sys.exc_info()[1] - msg = ("Unable to create Gurobi model. " - "Have you installed the Python " - "bindings for Gurobi?\n\n\t"+ - "Error message: {0}".format(e)) + msg = ( + "Unable to create Gurobi model. " + "Have you installed the Python " + "bindings for Gurobi?\n\n\t" + "Error message: {0}".format(e) + ) raise Exception(msg) self._add_block(model) @@ -309,7 +330,8 @@ def _set_instance(self, model, kwds={}): "the IO-option 'output_fixed_variable_bounds=True' " "to suppress this error and fix the variable " "by overwriting its bounds in the Gurobi instance." - % (var.name, self._pyomo_model.name,)) + % (var.name, self._pyomo_model.name) + ) def _add_block(self, block): DirectOrPersistentSolver._add_block(self, block) @@ -326,50 +348,59 @@ def _add_constraint(self, con): if con._linear_canonical_form: gurobi_expr, referenced_vars = self._get_expr_from_pyomo_repn( - con.canonical_form(), - self._max_constraint_degree) - #elif isinstance(con, LinearCanonicalRepn): + con.canonical_form(), self._max_constraint_degree + ) + # elif isinstance(con, LinearCanonicalRepn): # gurobi_expr, referenced_vars = self._get_expr_from_pyomo_repn( # con, # self._max_constraint_degree) else: gurobi_expr, referenced_vars = self._get_expr_from_pyomo_expr( - con.body, - self._max_constraint_degree) + con.body, self._max_constraint_degree + ) if con.has_lb(): if not is_fixed(con.lower): - raise ValueError("Lower bound of constraint {0} " - "is not constant.".format(con)) + raise ValueError( + "Lower bound of constraint {0} " "is not constant.".format(con) + ) if con.has_ub(): if not is_fixed(con.upper): - raise ValueError("Upper bound of constraint {0} " - "is not constant.".format(con)) + raise ValueError( + "Upper bound of constraint {0} " "is not constant.".format(con) + ) if con.equality: - gurobipy_con = self._solver_model.addConstr(lhs=gurobi_expr, - sense=gurobipy.GRB.EQUAL, - rhs=value(con.lower), - name=conname) + gurobipy_con = self._solver_model.addConstr( + lhs=gurobi_expr, + sense=gurobipy.GRB.EQUAL, + rhs=value(con.lower), + name=conname, + ) elif con.has_lb() and con.has_ub(): - gurobipy_con = self._solver_model.addRange(gurobi_expr, - value(con.lower), - value(con.upper), - name=conname) + gurobipy_con = self._solver_model.addRange( + gurobi_expr, value(con.lower), value(con.upper), name=conname + ) self._range_constraints.add(con) elif con.has_lb(): - gurobipy_con = self._solver_model.addConstr(lhs=gurobi_expr, - sense=gurobipy.GRB.GREATER_EQUAL, - rhs=value(con.lower), - name=conname) + gurobipy_con = self._solver_model.addConstr( + lhs=gurobi_expr, + sense=gurobipy.GRB.GREATER_EQUAL, + rhs=value(con.lower), + name=conname, + ) elif con.has_ub(): - gurobipy_con = self._solver_model.addConstr(lhs=gurobi_expr, - sense=gurobipy.GRB.LESS_EQUAL, - rhs=value(con.upper), - name=conname) + gurobipy_con = self._solver_model.addConstr( + lhs=gurobi_expr, + sense=gurobipy.GRB.LESS_EQUAL, + rhs=value(con.upper), + name=conname, + ) else: - raise ValueError("Constraint does not have a lower " - "or an upper bound: {0} \n".format(con)) + raise ValueError( + "Constraint does not have a lower " + "or an upper bound: {0} \n".format(con) + ) for var in referenced_vars: self._referenced_variables[var] += 1 @@ -390,8 +421,9 @@ def _add_sos_constraint(self, con): elif level == 2: sos_type = gurobipy.GRB.SOS_TYPE2 else: - raise ValueError("Solver does not support SOS " - "level {0} constraints".format(level)) + raise ValueError( + "Solver does not support SOS " "level {0} constraints".format(level) + ) gurobi_vars = [] weights = [] @@ -430,7 +462,9 @@ def _gurobi_vtype_from_var(self, var): elif var.is_continuous(): vtype = gurobipy.GRB.CONTINUOUS else: - raise ValueError('Variable domain type is not recognized for {0}'.format(var.domain)) + raise ValueError( + 'Variable domain type is not recognized for {0}'.format(var.domain) + ) return vtype def _set_objective(self, obj): @@ -450,7 +484,9 @@ def _set_objective(self, obj): else: raise ValueError('Objective sense is not recognized: {0}'.format(obj.sense)) - gurobi_expr, referenced_vars = self._get_expr_from_pyomo_expr(obj.expr, self._max_obj_degree) + gurobi_expr, referenced_vars = self._get_expr_from_pyomo_expr( + obj.expr, self._max_obj_degree + ) for var in referenced_vars: self._referenced_variables[var] += 1 @@ -482,7 +518,10 @@ def _postsolve(self): extract_reduced_costs = True flag = True if not flag: - raise RuntimeError("***The gurobi_direct solver plugin cannot extract solution suffix="+suffix) + raise RuntimeError( + "***The gurobi_direct solver plugin cannot extract solution suffix=" + + suffix + ) gprob = self._solver_model grb = gurobipy.GRB @@ -504,95 +543,132 @@ def _postsolve(self): if status == grb.LOADED: # problem is loaded, but no solution self.results.solver.status = SolverStatus.aborted - self.results.solver.termination_message = "Model is loaded, but no solution information is available." + self.results.solver.termination_message = ( + "Model is loaded, but no solution information is available." + ) self.results.solver.termination_condition = TerminationCondition.error soln.status = SolutionStatus.unknown elif status == grb.OPTIMAL: # optimal self.results.solver.status = SolverStatus.ok - self.results.solver.termination_message = "Model was solved to optimality (subject to tolerances), " \ - "and an optimal solution is available." + self.results.solver.termination_message = ( + "Model was solved to optimality (subject to tolerances), " + "and an optimal solution is available." + ) self.results.solver.termination_condition = TerminationCondition.optimal soln.status = SolutionStatus.optimal elif status == grb.INFEASIBLE: self.results.solver.status = SolverStatus.warning - self.results.solver.termination_message = "Model was proven to be infeasible" + self.results.solver.termination_message = ( + "Model was proven to be infeasible" + ) self.results.solver.termination_condition = TerminationCondition.infeasible soln.status = SolutionStatus.infeasible elif status == grb.INF_OR_UNBD: self.results.solver.status = SolverStatus.warning - self.results.solver.termination_message = "Problem proven to be infeasible or unbounded." - self.results.solver.termination_condition = TerminationCondition.infeasibleOrUnbounded + self.results.solver.termination_message = ( + "Problem proven to be infeasible or unbounded." + ) + self.results.solver.termination_condition = ( + TerminationCondition.infeasibleOrUnbounded + ) soln.status = SolutionStatus.unsure elif status == grb.UNBOUNDED: self.results.solver.status = SolverStatus.warning - self.results.solver.termination_message = "Model was proven to be unbounded." + self.results.solver.termination_message = ( + "Model was proven to be unbounded." + ) self.results.solver.termination_condition = TerminationCondition.unbounded soln.status = SolutionStatus.unbounded elif status == grb.CUTOFF: self.results.solver.status = SolverStatus.aborted - self.results.solver.termination_message = "Optimal objective for model was proven to be worse than the " \ - "value specified in the Cutoff parameter. No solution " \ - "information is available." - self.results.solver.termination_condition = TerminationCondition.minFunctionValue + self.results.solver.termination_message = ( + "Optimal objective for model was proven to be worse than the " + "value specified in the Cutoff parameter. No solution " + "information is available." + ) + self.results.solver.termination_condition = ( + TerminationCondition.minFunctionValue + ) soln.status = SolutionStatus.unknown elif status == grb.ITERATION_LIMIT: self.results.solver.status = SolverStatus.aborted - self.results.solver.termination_message = "Optimization terminated because the total number of simplex " \ - "iterations performed exceeded the value specified in the " \ - "IterationLimit parameter." - self.results.solver.termination_condition = TerminationCondition.maxIterations + self.results.solver.termination_message = ( + "Optimization terminated because the total number of simplex " + "iterations performed exceeded the value specified in the " + "IterationLimit parameter." + ) + self.results.solver.termination_condition = ( + TerminationCondition.maxIterations + ) soln.status = SolutionStatus.stoppedByLimit elif status == grb.NODE_LIMIT: self.results.solver.status = SolverStatus.aborted - self.results.solver.termination_message = "Optimization terminated because the total number of " \ - "branch-and-cut nodes explored exceeded the value specified " \ - "in the NodeLimit parameter" - self.results.solver.termination_condition = TerminationCondition.maxEvaluations + self.results.solver.termination_message = ( + "Optimization terminated because the total number of " + "branch-and-cut nodes explored exceeded the value specified " + "in the NodeLimit parameter" + ) + self.results.solver.termination_condition = ( + TerminationCondition.maxEvaluations + ) soln.status = SolutionStatus.stoppedByLimit elif status == grb.TIME_LIMIT: self.results.solver.status = SolverStatus.aborted - self.results.solver.termination_message = "Optimization terminated because the time expended exceeded " \ - "the value specified in the TimeLimit parameter." - self.results.solver.termination_condition = TerminationCondition.maxTimeLimit + self.results.solver.termination_message = ( + "Optimization terminated because the time expended exceeded " + "the value specified in the TimeLimit parameter." + ) + self.results.solver.termination_condition = ( + TerminationCondition.maxTimeLimit + ) soln.status = SolutionStatus.stoppedByLimit elif status == grb.SOLUTION_LIMIT: self.results.solver.status = SolverStatus.aborted - self.results.solver.termination_message = "Optimization terminated because the number of solutions found " \ - "reached the value specified in the SolutionLimit parameter." + self.results.solver.termination_message = ( + "Optimization terminated because the number of solutions found " + "reached the value specified in the SolutionLimit parameter." + ) self.results.solver.termination_condition = TerminationCondition.unknown soln.status = SolutionStatus.stoppedByLimit elif status == grb.INTERRUPTED: self.results.solver.status = SolverStatus.aborted - self.results.solver.termination_message = "Optimization was terminated by the user." + self.results.solver.termination_message = ( + "Optimization was terminated by the user." + ) self.results.solver.termination_condition = TerminationCondition.error soln.status = SolutionStatus.error elif status == grb.NUMERIC: self.results.solver.status = SolverStatus.error - self.results.solver.termination_message = "Optimization was terminated due to unrecoverable numerical " \ - "difficulties." + self.results.solver.termination_message = ( + "Optimization was terminated due to unrecoverable numerical " + "difficulties." + ) self.results.solver.termination_condition = TerminationCondition.error soln.status = SolutionStatus.error elif status == grb.SUBOPTIMAL: self.results.solver.status = SolverStatus.warning - self.results.solver.termination_message = "Unable to satisfy optimality tolerances; a sub-optimal " \ - "solution is available." + self.results.solver.termination_message = ( + "Unable to satisfy optimality tolerances; a sub-optimal " + "solution is available." + ) self.results.solver.termination_condition = TerminationCondition.other soln.status = SolutionStatus.feasible # note that USER_OBJ_LIMIT was added in Gurobi 7.0, so it may not be present - elif (status is not None) and \ - (status == getattr(grb,'USER_OBJ_LIMIT',None)): + elif (status is not None) and (status == getattr(grb, 'USER_OBJ_LIMIT', None)): self.results.solver.status = SolverStatus.aborted - self.results.solver.termination_message = "User specified an objective limit " \ - "(a bound on either the best objective " \ - "or the best bound), and that limit has " \ - "been reached. Solution is available." + self.results.solver.termination_message = ( + "User specified an objective limit " + "(a bound on either the best objective " + "or the best bound), and that limit has " + "been reached. Solution is available." + ) self.results.solver.termination_condition = TerminationCondition.other soln.status = SolutionStatus.stoppedByLimit else: self.results.solver.status = SolverStatus.error - self.results.solver.termination_message = \ - ("Unhandled Gurobi solve status " - "("+str(status)+")") + self.results.solver.termination_message = ( + "Unhandled Gurobi solve status " "(" + str(status) + ")" + ) self.results.solver.termination_condition = TerminationCondition.error soln.status = SolutionStatus.error @@ -603,7 +679,9 @@ def _postsolve(self): elif gprob.ModelSense == -1: self.results.problem.sense = maximize else: - raise RuntimeError('Unrecognized gurobi objective sense: {0}'.format(gprob.ModelSense)) + raise RuntimeError( + 'Unrecognized gurobi objective sense: {0}'.format(gprob.ModelSense) + ) self.results.problem.upper_bound = None self.results.problem.lower_bound = None @@ -632,19 +710,27 @@ def _postsolve(self): except (gurobipy.GurobiError, AttributeError): pass else: - raise RuntimeError('Unrecognized gurobi objective sense: {0}'.format(gprob.ModelSense)) + raise RuntimeError( + 'Unrecognized gurobi objective sense: {0}'.format(gprob.ModelSense) + ) try: - soln.gap = self.results.problem.upper_bound - self.results.problem.lower_bound + soln.gap = ( + self.results.problem.upper_bound - self.results.problem.lower_bound + ) except TypeError: soln.gap = None - self.results.problem.number_of_constraints = gprob.NumConstrs + gprob.NumQConstrs + gprob.NumSOS + self.results.problem.number_of_constraints = ( + gprob.NumConstrs + gprob.NumQConstrs + gprob.NumSOS + ) self.results.problem.number_of_nonzeros = gprob.NumNZs self.results.problem.number_of_variables = gprob.NumVars self.results.problem.number_of_binary_variables = gprob.NumBinVars self.results.problem.number_of_integer_variables = gprob.NumIntVars - self.results.problem.number_of_continuous_variables = gprob.NumVars - gprob.NumIntVars - gprob.NumBinVars + self.results.problem.number_of_continuous_variables = ( + gprob.NumVars - gprob.NumIntVars - gprob.NumBinVars + ) self.results.problem.number_of_objectives = 1 self.results.problem.number_of_solutions = gprob.SolCount @@ -661,7 +747,11 @@ def _postsolve(self): soln_constraints = soln.constraint gurobi_vars = self._solver_model.getVars() - gurobi_vars = list(set(gurobi_vars).intersection(set(self._pyomo_var_to_solver_var_map.values()))) + gurobi_vars = list( + set(gurobi_vars).intersection( + set(self._pyomo_var_to_solver_var_map.values()) + ) + ) var_vals = self._solver_model.getAttr("X", gurobi_vars) names = self._solver_model.getAttr("VarName", gurobi_vars) for gurobi_var, val, name in zip(gurobi_vars, var_vals, names): @@ -683,7 +773,9 @@ def _postsolve(self): soln_constraints[name] = {} if self._version_major >= 5: gurobi_q_cons = self._solver_model.getQConstrs() - q_con_names = self._solver_model.getAttr("QCName", gurobi_q_cons) + q_con_names = self._solver_model.getAttr( + "QCName", gurobi_q_cons + ) for name in q_con_names: soln_constraints[name] = {} @@ -697,7 +789,9 @@ def _postsolve(self): soln_constraints[name]["Dual"] = val if extract_slacks: - gurobi_range_con_vars = set(self._solver_model.getVars()) - set(self._pyomo_var_to_solver_var_map.values()) + gurobi_range_con_vars = set(self._solver_model.getVars()) - set( + self._pyomo_var_to_solver_var_map.values() + ) vals = self._solver_model.getAttr("Slack", gurobi_cons) for gurobi_con, val, name in zip(gurobi_cons, vals, con_names): pyomo_con = self._solver_con_to_pyomo_con_map[gurobi_con] @@ -791,10 +885,16 @@ def _load_duals(self, cons_to_load=None): if self._version_major >= 5: quadratic_cons_to_load = self._solver_model.getQConstrs() else: - gurobi_cons_to_load = set([con_map[pyomo_con] for pyomo_con in cons_to_load]) - linear_cons_to_load = gurobi_cons_to_load.intersection(set(self._solver_model.getConstrs())) + gurobi_cons_to_load = set( + [con_map[pyomo_con] for pyomo_con in cons_to_load] + ) + linear_cons_to_load = gurobi_cons_to_load.intersection( + set(self._solver_model.getConstrs()) + ) if self._version_major >= 5: - quadratic_cons_to_load = gurobi_cons_to_load.intersection(set(self._solver_model.getQConstrs())) + quadratic_cons_to_load = gurobi_cons_to_load.intersection( + set(self._solver_model.getQConstrs()) + ) linear_vals = self._solver_model.getAttr("Pi", linear_cons_to_load) if self._version_major >= 5: quadratic_vals = self._solver_model.getAttr("QCPi", quadratic_cons_to_load) @@ -814,20 +914,30 @@ def _load_slacks(self, cons_to_load=None): reverse_con_map = self._solver_con_to_pyomo_con_map slack = self._pyomo_model.slack - gurobi_range_con_vars = set(self._solver_model.getVars()) - set(self._pyomo_var_to_solver_var_map.values()) + gurobi_range_con_vars = set(self._solver_model.getVars()) - set( + self._pyomo_var_to_solver_var_map.values() + ) if cons_to_load is None: linear_cons_to_load = self._solver_model.getConstrs() if self._version_major >= 5: quadratic_cons_to_load = self._solver_model.getQConstrs() else: - gurobi_cons_to_load = set([con_map[pyomo_con] for pyomo_con in cons_to_load]) - linear_cons_to_load = gurobi_cons_to_load.intersection(set(self._solver_model.getConstrs())) + gurobi_cons_to_load = set( + [con_map[pyomo_con] for pyomo_con in cons_to_load] + ) + linear_cons_to_load = gurobi_cons_to_load.intersection( + set(self._solver_model.getConstrs()) + ) if self._version_major >= 5: - quadratic_cons_to_load = gurobi_cons_to_load.intersection(set(self._solver_model.getQConstrs())) + quadratic_cons_to_load = gurobi_cons_to_load.intersection( + set(self._solver_model.getQConstrs()) + ) linear_vals = self._solver_model.getAttr("Slack", linear_cons_to_load) if self._version_major >= 5: - quadratic_vals = self._solver_model.getAttr("QCSlack", quadratic_cons_to_load) + quadratic_vals = self._solver_model.getAttr( + "QCSlack", quadratic_cons_to_load + ) for gurobi_con, val in zip(linear_cons_to_load, linear_vals): pyomo_con = reverse_con_map[gurobi_con] diff --git a/pyomo/solvers/plugins/solvers/gurobi_persistent.py b/pyomo/solvers/plugins/solvers/gurobi_persistent.py index a9a94ee8a75..01f241759cc 100644 --- a/pyomo/solvers/plugins/solvers/gurobi_persistent.py +++ b/pyomo/solvers/plugins/solvers/gurobi_persistent.py @@ -18,7 +18,9 @@ from pyomo.opt.base import SolverFactory -@SolverFactory.register('gurobi_persistent', doc='Persistent python interface to Gurobi') +@SolverFactory.register( + 'gurobi_persistent', doc='Persistent python interface to Gurobi' +) class GurobiPersistent(PersistentSolver, GurobiDirect): """ A class that provides a persistent interface to Gurobi. Direct solver interfaces do not use any file io. @@ -54,7 +56,9 @@ def _remove_constraint(self, solver_con): if self._solver_model.getAttr('NumConstrs') == 0: self._update() else: - name = self._symbol_map.getSymbol(self._solver_con_to_pyomo_con_map[solver_con]) + name = self._symbol_map.getSymbol( + self._solver_con_to_pyomo_con_map[solver_con] + ) if self._solver_model.getConstrByName(name) is None: self._update() elif isinstance(solver_con, gurobipy.QConstr): @@ -74,7 +78,9 @@ def _remove_constraint(self, solver_con): except gurobipy.GurobiError: self._update() else: - raise ValueError('Unrecognized type for gurobi constraint: {0}'.format(type(solver_con))) + raise ValueError( + 'Unrecognized type for gurobi constraint: {0}'.format(type(solver_con)) + ) self._solver_model.remove(solver_con) self._needs_updated = True @@ -86,7 +92,9 @@ def _remove_var(self, solver_var): if self._solver_model.getAttr('NumVars') == 0: self._update() else: - name = self._symbol_map.getSymbol(self._solver_var_to_pyomo_var_map[solver_var]) + name = self._symbol_map.getSymbol( + self._solver_var_to_pyomo_var_map[solver_var] + ) if self._solver_model.getVarByName(name) is None: self._update() self._solver_model.remove(solver_var) @@ -109,12 +117,16 @@ def update_var(self, var): # see PR #366 for discussion about handling indexed # objects and keeping compatibility with the # pyomo.kernel objects - #if var.is_indexed(): + # if var.is_indexed(): # for child_var in var.values(): # self.update_var(child_var) # return if var not in self._pyomo_var_to_solver_var_map: - raise ValueError('The Var provided to update_var needs to be added first: {0}'.format(var)) + raise ValueError( + 'The Var provided to update_var needs to be added first: {0}'.format( + var + ) + ) gurobipy_var = self._pyomo_var_to_solver_var_map[var] vtype = self._gurobi_vtype_from_var(var) lb, ub = self._gurobi_lb_ub_from_var(var) @@ -159,12 +171,17 @@ def set_linear_constraint_attr(self, con, attr, val): See gurobi documentation for acceptable values. """ if attr in {'Sense', 'RHS', 'ConstrName'}: - raise ValueError('Linear constraint attr {0} cannot be set with' + - ' the set_linear_constraint_attr method. Please use' + - ' the remove_constraint and add_constraint methods.'.format(attr)) + raise ValueError( + 'Linear constraint attr {0} cannot be set with' + + ' the set_linear_constraint_attr method. Please use' + + ' the remove_constraint and add_constraint methods.'.format(attr) + ) if self._version_major < 7: - if (self._solver_model.getAttr('NumConstrs') == 0 or - self._solver_model.getConstrByName(self._symbol_map.getSymbol(con)) is None): + if ( + self._solver_model.getAttr('NumConstrs') == 0 + or self._solver_model.getConstrByName(self._symbol_map.getSymbol(con)) + is None + ): self._solver_model.update() self._pyomo_con_to_solver_con_map[con].setAttr(attr, val) self._needs_updated = True @@ -192,16 +209,23 @@ def set_var_attr(self, var, attr, val): See gurobi documentation for acceptable values. """ if attr in {'LB', 'UB', 'VType', 'VarName'}: - raise ValueError('Var attr {0} cannot be set with' + - ' the set_var_attr method. Please use' + - ' the update_var method.'.format(attr)) + raise ValueError( + 'Var attr {0} cannot be set with' + + ' the set_var_attr method. Please use' + + ' the update_var method.'.format(attr) + ) if attr == 'Obj': - raise ValueError('Var attr Obj cannot be set with' + - ' the set_var_attr method. Please use' + - ' the set_objective method.') + raise ValueError( + 'Var attr Obj cannot be set with' + + ' the set_var_attr method. Please use' + + ' the set_objective method.' + ) if self._version_major < 7: - if (self._solver_model.getAttr('NumVars') == 0 or - self._solver_model.getVarByName(self._symbol_map.getSymbol(var)) is None): + if ( + self._solver_model.getAttr('NumVars') == 0 + or self._solver_model.getVarByName(self._symbol_map.getSymbol(var)) + is None + ): self._solver_model.update() self._pyomo_var_to_solver_var_map[var].setAttr(attr, val) self._needs_updated = True @@ -456,6 +480,7 @@ def get_gurobi_param_info(self, param): def _intermediate_callback(self): def f(gurobi_model, where): self._callback_func(self._pyomo_model, self, where) + return f def set_callback(self, func=None): @@ -553,28 +578,39 @@ def cbCut(self, con): if is_fixed(con.body): raise ValueError('cbCut expected a non-trival constraint') - gurobi_expr, referenced_vars = self._get_expr_from_pyomo_expr(con.body, self._max_constraint_degree) + gurobi_expr, referenced_vars = self._get_expr_from_pyomo_expr( + con.body, self._max_constraint_degree + ) if con.has_lb(): if con.has_ub(): raise ValueError('Range constraints are not supported in cbCut.') if not is_fixed(con.lower): - raise ValueError('Lower bound of constraint {0} is not constant.'.format(con)) + raise ValueError( + 'Lower bound of constraint {0} is not constant.'.format(con) + ) if con.has_ub(): if not is_fixed(con.upper): - raise ValueError('Upper bound of constraint {0} is not constant.'.format(con)) + raise ValueError( + 'Upper bound of constraint {0} is not constant.'.format(con) + ) if con.equality: - self._solver_model.cbCut(lhs=gurobi_expr, sense=gurobipy.GRB.EQUAL, - rhs=value(con.lower)) + self._solver_model.cbCut( + lhs=gurobi_expr, sense=gurobipy.GRB.EQUAL, rhs=value(con.lower) + ) elif con.has_lb() and (value(con.lower) > -float('inf')): - self._solver_model.cbCut(lhs=gurobi_expr, sense=gurobipy.GRB.GREATER_EQUAL, - rhs=value(con.lower)) + self._solver_model.cbCut( + lhs=gurobi_expr, sense=gurobipy.GRB.GREATER_EQUAL, rhs=value(con.lower) + ) elif con.has_ub() and (value(con.upper) < float('inf')): - self._solver_model.cbCut(lhs=gurobi_expr, sense=gurobipy.GRB.LESS_EQUAL, - rhs=value(con.upper)) + self._solver_model.cbCut( + lhs=gurobi_expr, sense=gurobipy.GRB.LESS_EQUAL, rhs=value(con.upper) + ) else: - raise ValueError('Constraint does not have a lower or an upper bound {0} \n'.format(con)) + raise ValueError( + 'Constraint does not have a lower or an upper bound {0} \n'.format(con) + ) def cbGet(self, what): return self._solver_model.cbGet(what) @@ -620,28 +656,39 @@ def cbLazy(self, con): if is_fixed(con.body): raise ValueError('cbLazy expected a non-trival constraint') - gurobi_expr, referenced_vars = self._get_expr_from_pyomo_expr(con.body, self._max_constraint_degree) + gurobi_expr, referenced_vars = self._get_expr_from_pyomo_expr( + con.body, self._max_constraint_degree + ) if con.has_lb(): if con.has_ub(): raise ValueError('Range constraints are not supported in cbLazy.') if not is_fixed(con.lower): - raise ValueError('Lower bound of constraint {0} is not constant.'.format(con)) + raise ValueError( + 'Lower bound of constraint {0} is not constant.'.format(con) + ) if con.has_ub(): if not is_fixed(con.upper): - raise ValueError('Upper bound of constraint {0} is not constant.'.format(con)) + raise ValueError( + 'Upper bound of constraint {0} is not constant.'.format(con) + ) if con.equality: - self._solver_model.cbLazy(lhs=gurobi_expr, sense=gurobipy.GRB.EQUAL, - rhs=value(con.lower)) + self._solver_model.cbLazy( + lhs=gurobi_expr, sense=gurobipy.GRB.EQUAL, rhs=value(con.lower) + ) elif con.has_lb() and (value(con.lower) > -float('inf')): - self._solver_model.cbLazy(lhs=gurobi_expr, sense=gurobipy.GRB.GREATER_EQUAL, - rhs=value(con.lower)) + self._solver_model.cbLazy( + lhs=gurobi_expr, sense=gurobipy.GRB.GREATER_EQUAL, rhs=value(con.lower) + ) elif con.has_ub() and (value(con.upper) < float('inf')): - self._solver_model.cbLazy(lhs=gurobi_expr, sense=gurobipy.GRB.LESS_EQUAL, - rhs=value(con.upper)) + self._solver_model.cbLazy( + lhs=gurobi_expr, sense=gurobipy.GRB.LESS_EQUAL, rhs=value(con.upper) + ) else: - raise ValueError('Constraint does not have a lower or an upper bound {0} \n'.format(con)) + raise ValueError( + 'Constraint does not have a lower or an upper bound {0} \n'.format(con) + ) def cbSetSolution(self, vars, solution): if not isinstance(vars, Iterable): @@ -656,9 +703,9 @@ def _add_column(self, var, obj_coef, constraints, coefficients): """Add a column to the solver's model This will add the Pyomo variable var to the solver's - model, and put the coefficients on the associated + model, and put the coefficients on the associated constraints in the solver model. If the obj_coef is - not zero, it will add obj_coef*var to the objective + not zero, it will add obj_coef*var to the objective of the solver's model. Parameters @@ -674,10 +721,16 @@ def _add_column(self, var, obj_coef, constraints, coefficients): vtype = self._gurobi_vtype_from_var(var) lb, ub = self._gurobi_lb_ub_from_var(var) - gurobipy_var = self._solver_model.addVar(obj=obj_coef, lb=lb, ub=ub, vtype=vtype, name=varname, - column=gurobipy.Column(coeffs=coefficients, constrs=constraints) ) + gurobipy_var = self._solver_model.addVar( + obj=obj_coef, + lb=lb, + ub=ub, + vtype=vtype, + name=varname, + column=gurobipy.Column(coeffs=coefficients, constrs=constraints), + ) - self._pyomo_var_to_solver_var_map[var] = gurobipy_var + self._pyomo_var_to_solver_var_map[var] = gurobipy_var self._solver_var_to_pyomo_var_map[gurobipy_var] = var self._referenced_variables[var] = len(coefficients) diff --git a/pyomo/solvers/plugins/solvers/mosek_direct.py b/pyomo/solvers/plugins/solvers/mosek_direct.py index 04210c8076e..92518981ab3 100644 --- a/pyomo/solvers/plugins/solvers/mosek_direct.py +++ b/pyomo/solvers/plugins/solvers/mosek_direct.py @@ -24,16 +24,27 @@ from pyomo.opt.base.solvers import OptSolver from pyomo.repn import generate_standard_repn from pyomo.solvers.plugins.solvers.direct_solver import DirectSolver -from pyomo.solvers.plugins.solvers.direct_or_persistent_solver import \ - DirectOrPersistentSolver +from pyomo.solvers.plugins.solvers.direct_or_persistent_solver import ( + DirectOrPersistentSolver, +) from pyomo.common.collections import ComponentMap, ComponentSet, Bunch from pyomo.opt import SolverFactory -from pyomo.core.kernel.conic import (_ConicBase, quadratic, rotated_quadratic, - primal_exponential, primal_power, primal_geomean, - dual_exponential, dual_power, dual_geomean, svec_psdcone) +from pyomo.core.kernel.conic import ( + _ConicBase, + quadratic, + rotated_quadratic, + primal_exponential, + primal_power, + primal_geomean, + dual_exponential, + dual_power, + dual_geomean, + svec_psdcone, +) from pyomo.opt.results.results_ import SolverResults from pyomo.opt.results.solution import Solution, SolutionStatus from pyomo.opt.results.solver import TerminationCondition, SolverStatus + logger = logging.getLogger('pyomo.solvers') inf = float('inf') @@ -59,8 +70,8 @@ def _is_numeric(x): @SolverFactory.register('mosek', doc='The MOSEK LP/QP/SOCP/MIP solver') class MOSEK(OptSolver): """ - The MOSEK solver for continuous/mixed-integer linear, quadratic and conic - (quadratic, exponential, power cones) problems. MOSEK also supports + The MOSEK solver for continuous/mixed-integer linear, quadratic and conic + (quadratic, exponential, power cones) problems. MOSEK also supports continuous SDPs. """ @@ -69,18 +80,15 @@ def __new__(cls, *args, **kwds): if mode in {'python', 'direct'}: opt = SolverFactory('mosek_direct', **kwds) if opt is None: - logger.error( - 'MOSEK\'s Optimizer API for python is not installed.') + logger.error('MOSEK\'s Optimizer API for python is not installed.') return opt if mode == 'persistent': opt = SolverFactory('mosek_persistent', **kwds) if opt is None: - logger.error( - 'MOSEK\'s Optimizer API for python is not installed.') + logger.error('MOSEK\'s Optimizer API for python is not installed.') return opt else: - logger.error( - 'Unknown solver interface: \"{}\"'.format(mode)) + logger.error('Unknown solver interface: \"{}\"'.format(mode)) return None @@ -142,11 +150,12 @@ def _apply_solver(self): StaleFlagManager.mark_all_as_stale() if self._tee: + def _process_stream(msg): sys.stdout.write(msg) sys.stdout.flush() - self._solver_model.set_Stream( - mosek.streamtype.log, _process_stream) + + self._solver_model.set_Stream(mosek.streamtype.log, _process_stream) if self._keepfiles: logger.info("Solver log file: {}".format(self._log_file)) @@ -189,14 +198,12 @@ def _set_instance(self, model, kwds={}): super(MOSEKDirect, self)._set_instance(model, kwds) self._pyomo_cone_to_solver_cone_map = dict() self._solver_cone_to_pyomo_cone_map = ComponentMap() - self._whichsol = getattr(mosek.soltype, kwds.pop( - 'soltype', 'bas')) + self._whichsol = getattr(mosek.soltype, kwds.pop('soltype', 'bas')) try: self._solver_model = self._mosek_env.Task() except: err_msg = sys.exc_info()[1] - logger.error("MOSEK task creation failed. " - + "Reason: {}".format(err_msg)) + logger.error("MOSEK task creation failed. " + "Reason: {}".format(err_msg)) raise self._add_block(model) @@ -225,10 +232,14 @@ def _get_cone_data(self, con): cone_members = [con.r1, con.r2] + list(con.x) else: raise UnsupportedDomainError( - "MOSEK version 9 does not support {}.".format(type(con))) + "MOSEK version 9 does not support {}.".format(type(con)) + ) else: raise UnsupportedDomainError( - "MOSEK version {} does not support {}".format(self._version[0], type(con))) + "MOSEK version {} does not support {}".format( + self._version[0], type(con) + ) + ) return (cone_type, cone_param, ComponentSet(cone_members)) def _get_acc_domain(self, cone): @@ -252,12 +263,14 @@ def _get_acc_domain(self, cone): elif isinstance(cone, primal_power): domdim = 2 + len(cone.x) domidx = self._solver_model.appendprimalpowerconedomain( - domdim, [value(cone.alpha), 1-value(cone.alpha)]) + domdim, [value(cone.alpha), 1 - value(cone.alpha)] + ) members = [cone.r1, cone.r2] + list(cone.x) elif isinstance(cone, dual_power): domdim = 2 + len(cone.x) domidx = self._solver_model.appenddualpowerconedomain( - domdim, [value(cone.alpha), 1-value(cone.alpha)]) + domdim, [value(cone.alpha), 1 - value(cone.alpha)] + ) members = [cone.r1, cone.r2] + list(cone.x) elif isinstance(cone, primal_geomean): domdim = len(cone.r) + 1 @@ -277,12 +290,11 @@ def _get_expr_from_pyomo_repn(self, repn, max_degree=2): degree = repn.polynomial_degree() if (degree is None) or degree > max_degree: raise DegreeError( - 'MOSEK does not support expressions of degree {}.'.format( - degree)) + 'MOSEK does not support expressions of degree {}.'.format(degree) + ) referenced_vars = ComponentSet(repn.linear_vars) - indices = tuple(self._pyomo_var_to_solver_var_map[i] - for i in repn.linear_vars) + indices = tuple(self._pyomo_var_to_solver_var_map[i] for i in repn.linear_vars) mosek_arow = (indices, tuple(repn.linear_coefs), repn.constant) if len(repn.quadratic_vars) == 0: @@ -291,12 +303,21 @@ def _get_expr_from_pyomo_repn(self, repn, max_degree=2): else: q_vars = itertools.chain.from_iterable(repn.quadratic_vars) referenced_vars.update(q_vars) - qsubi, qsubj = zip(*[(i, j) if self._pyomo_var_to_solver_var_map[i] >= - self._pyomo_var_to_solver_var_map[j] else (j, i) for i, j in repn.quadratic_vars]) + qsubi, qsubj = zip( + *[ + (i, j) + if self._pyomo_var_to_solver_var_map[i] + >= self._pyomo_var_to_solver_var_map[j] + else (j, i) + for i, j in repn.quadratic_vars + ] + ) qsubi = tuple(self._pyomo_var_to_solver_var_map[i] for i in qsubi) qsubj = tuple(self._pyomo_var_to_solver_var_map[j] for j in qsubj) - qvals = tuple(v * 2 if qsubi[i] is qsubj[i] else v - for i, v in enumerate(repn.quadratic_coefs)) + qvals = tuple( + v * 2 if qsubi[i] is qsubj[i] else v + for i, v in enumerate(repn.quadratic_coefs) + ) mosek_qexp = (qsubi, qsubj, qvals) return mosek_arow, mosek_qexp, referenced_vars @@ -304,7 +325,8 @@ def _get_expr_from_pyomo_expr(self, expr, max_degree=2): repn = generate_standard_repn(expr, quadratic=(max_degree == 2)) try: mosek_arow, mosek_qexp, referenced_vars = self._get_expr_from_pyomo_repn( - repn, max_degree) + repn, max_degree + ) except DegreeError as e: msg = e.args[0] msg += '\nexpr: {}'.format(expr) @@ -336,52 +358,43 @@ def _add_vars(self, var_seq): if not var_seq: return var_num = self._solver_model.getnumvar() - vnames = tuple(self._symbol_map.getSymbol( - v, self._labeler) for v in var_seq) + vnames = tuple(self._symbol_map.getSymbol(v, self._labeler) for v in var_seq) vtypes = tuple(map(self._mosek_vartype_from_var, var_seq)) - lbs = tuple(value(v) if v.fixed - else -inf if value(v.lb) is None - else value(v.lb) - for v in var_seq - ) - ubs = tuple(value(v) if v.fixed - else inf if value(v.ub) is None - else value(v.ub) - for v in var_seq - ) + lbs = tuple( + value(v) if v.fixed else -inf if value(v.lb) is None else value(v.lb) + for v in var_seq + ) + ubs = tuple( + value(v) if v.fixed else inf if value(v.ub) is None else value(v.ub) + for v in var_seq + ) fxs = tuple(v.is_fixed() for v in var_seq) bound_types = tuple(map(self._mosek_bounds, lbs, ubs, fxs)) self._solver_model.appendvars(len(var_seq)) - var_ids = range(var_num, - var_num + len(var_seq)) + var_ids = range(var_num, var_num + len(var_seq)) _vnames = tuple(map(self._solver_model.putvarname, var_ids, vnames)) self._solver_model.putvartypelist(var_ids, vtypes) self._solver_model.putvarboundlist(var_ids, bound_types, lbs, ubs) self._pyomo_var_to_solver_var_map.update(zip(var_seq, var_ids)) self._solver_var_to_pyomo_var_map.update(zip(var_ids, var_seq)) - self._referenced_variables.update(zip(var_seq, [0]*len(var_seq))) + self._referenced_variables.update(zip(var_seq, [0] * len(var_seq))) def _add_cones(self, cones, num_cones): - cone_names = tuple(self._symbol_map.getSymbol( - c, self._labeler) for c in cones) + cone_names = tuple(self._symbol_map.getSymbol(c, self._labeler) for c in cones) # MOSEK v<10 : use "cones" if self._version[0] < 10: cone_num = self._solver_model.getnumcone() cone_indices = range(cone_num, cone_num + num_cones) - cone_type, cone_param, cone_members = zip(*map( - self._get_cone_data, cones)) + cone_type, cone_param, cone_members = zip(*map(self._get_cone_data, cones)) for i in range(num_cones): - members = tuple(self._pyomo_var_to_solver_var_map[c_m] - for c_m in cone_members[i]) - self._solver_model.appendcone( - cone_type[i], cone_param[i], members) - self._solver_model.putconename( - cone_indices[i], cone_names[i]) - self._pyomo_cone_to_solver_cone_map.update( - zip(cones, cone_indices)) - self._solver_cone_to_pyomo_cone_map.update( - zip(cone_indices, cones)) + members = tuple( + self._pyomo_var_to_solver_var_map[c_m] for c_m in cone_members[i] + ) + self._solver_model.appendcone(cone_type[i], cone_param[i], members) + self._solver_model.putconename(cone_indices[i], cone_names[i]) + self._pyomo_cone_to_solver_cone_map.update(zip(cones, cone_indices)) + self._solver_cone_to_pyomo_cone_map.update(zip(cone_indices, cones)) for i, c in enumerate(cones): self._vars_referenced_by_con[c] = cone_members[i] @@ -390,21 +403,24 @@ def _add_cones(self, cones, num_cones): else: # MOSEK v>=10 : use affine conic constraints (old cones are deprecated) domain_dims, domain_indices, cone_members = zip( - *map(self._get_acc_domain, cones)) + *map(self._get_acc_domain, cones) + ) total_dim = sum(domain_dims) numafe = self._solver_model.getnumafe() numacc = self._solver_model.getnumacc() - members = tuple(self._pyomo_var_to_solver_var_map[c_m] - for c_m in itertools.chain(*cone_members)) + members = tuple( + self._pyomo_var_to_solver_var_map[c_m] + for c_m in itertools.chain(*cone_members) + ) afe_indices = tuple(range(numafe, numafe + total_dim)) acc_indices = tuple(range(numacc, numacc + num_cones)) self._solver_model.appendafes(total_dim) - self._solver_model.putafefentrylist( - afe_indices, members, [1]*total_dim) + self._solver_model.putafefentrylist(afe_indices, members, [1] * total_dim) self._solver_model.appendaccsseq( - domain_indices, total_dim, afe_indices[0], None) + domain_indices, total_dim, afe_indices[0], None + ) for name in cone_names: self._solver_model.putaccname(numacc, name) @@ -427,35 +443,45 @@ def _add_constraints(self, con_seq): logger.warning("Inactive constraints will be skipped.") con_seq = active_seq if self._skip_trivial_constraints: - con_seq = tuple(filter(is_fixed( - operator.attrgetter('body')), con_seq)) + con_seq = tuple(filter(is_fixed(operator.attrgetter('body')), con_seq)) # Linear/Quadratic constraints - lq = tuple(filter(operator.attrgetter( - "_linear_canonical_form"), con_seq)) - lq_ex = tuple(filter(lambda x: not isinstance(x, _ConicBase) - and not (x._linear_canonical_form), con_seq)) + lq = tuple(filter(operator.attrgetter("_linear_canonical_form"), con_seq)) + lq_ex = tuple( + filter( + lambda x: not isinstance(x, _ConicBase) + and not (x._linear_canonical_form), + con_seq, + ) + ) lq_all = lq + lq_ex num_lq = len(lq) + len(lq_ex) if num_lq > 0: con_num = self._solver_model.getnumcon() - lq_data = [self._get_expr_from_pyomo_repn(c.canonical_form()) - for c in lq] - lq_data.extend( - self._get_expr_from_pyomo_expr(c.body) for c in lq_ex) + lq_data = [self._get_expr_from_pyomo_repn(c.canonical_form()) for c in lq] + lq_data.extend(self._get_expr_from_pyomo_expr(c.body) for c in lq_ex) arow, qexp, referenced_vars = zip(*lq_data) q_is, q_js, q_vals = zip(*qexp) l_ids, l_coefs, constants = zip(*arow) - lbs = tuple(-inf if value(lq_all[i].lower) is None else value( - lq_all[i].lower) - constants[i] for i in range(num_lq)) - ubs = tuple(inf if value(lq_all[i].upper) is None else value( - lq_all[i].upper) - constants[i] for i in range(num_lq)) + lbs = tuple( + -inf + if value(lq_all[i].lower) is None + else value(lq_all[i].lower) - constants[i] + for i in range(num_lq) + ) + ubs = tuple( + inf + if value(lq_all[i].upper) is None + else value(lq_all[i].upper) - constants[i] + for i in range(num_lq) + ) fxs = tuple(c.equality for c in lq_all) bound_types = tuple(map(self._mosek_bounds, lbs, ubs, fxs)) sub = range(con_num, con_num + num_lq) - sub_names = tuple(self._symbol_map.getSymbol(c, self._labeler) - for c in lq_all) + sub_names = tuple( + self._symbol_map.getSymbol(c, self._labeler) for c in lq_all + ) ptre = tuple(itertools.accumulate(list(map(len, l_ids)))) ptrb = (0,) + ptre[:-1] asubs = tuple(itertools.chain.from_iterable(l_ids)) @@ -463,7 +489,7 @@ def _add_constraints(self, con_seq): qcsubi = tuple(itertools.chain.from_iterable(q_is)) qcsubj = tuple(itertools.chain.from_iterable(q_js)) qcval = tuple(itertools.chain.from_iterable(q_vals)) - qcsubk = tuple(i for i in sub for j in range(len(q_is[i-con_num]))) + qcsubk = tuple(i for i in sub for j in range(len(q_is[i - con_num]))) self._solver_model.appendcons(num_lq) self._solver_model.putarowlist(sub, ptrb, ptre, asubs, avals) self._solver_model.putqcon(qcsubk, qcsubi, qcsubj, qcval) @@ -502,7 +528,8 @@ def _set_objective(self, obj): raise ValueError("Objective sense not recognized.") mosek_arow, mosek_qexp, referenced_vars = self._get_expr_from_pyomo_expr( - obj.expr, self._max_obj_degree) + obj.expr, self._max_obj_degree + ) for var in referenced_vars: self._referenced_variables[var] += 1 @@ -527,52 +554,58 @@ def _add_block(self, block): ---------- block: Block (scalar Block or single _BlockData) """ - var_seq = tuple(block.component_data_objects( - ctype=pyomo.core.base.var.Var, - descend_into=True, active=True, - sort=True)) + var_seq = tuple( + block.component_data_objects( + ctype=pyomo.core.base.var.Var, descend_into=True, active=True, sort=True + ) + ) self._add_vars(var_seq) - for sub_block in block.block_data_objects(descend_into=True, - active=True): + for sub_block in block.block_data_objects(descend_into=True, active=True): con_list = [] for con in sub_block.component_data_objects( - ctype=pyomo.core.base.constraint.Constraint, - descend_into=False, - active=True, - sort=True): - if (not con.has_lb()) and \ - (not con.has_ub()): + ctype=pyomo.core.base.constraint.Constraint, + descend_into=False, + active=True, + sort=True, + ): + if (not con.has_lb()) and (not con.has_ub()): assert not con.equality continue # non-binding, so skip con_list.append(con) self._add_constraints(con_list) for con in sub_block.component_data_objects( - ctype=pyomo.core.base.sos.SOSConstraint, - descend_into=False, - active=True, - sort=True): + ctype=pyomo.core.base.sos.SOSConstraint, + descend_into=False, + active=True, + sort=True, + ): self._add_sos_constraint(con) obj_counter = 0 for obj in sub_block.component_data_objects( - ctype=pyomo.core.base.objective.Objective, - descend_into=False, - active=True): + ctype=pyomo.core.base.objective.Objective, + descend_into=False, + active=True, + ): obj_counter += 1 if obj_counter > 1: - raise ValueError("Solver interface does not " - "support multiple objectives.") + raise ValueError( + "Solver interface does not " "support multiple objectives." + ) self._set_objective(obj) def _set_whichsol(self): - itr_soltypes = [mosek.problemtype.qo, mosek.problemtype.qcqo, - mosek.problemtype.conic] - if (self._solver_model.getnumintvar() >= 1): + itr_soltypes = [ + mosek.problemtype.qo, + mosek.problemtype.qcqo, + mosek.problemtype.conic, + ] + if self._solver_model.getnumintvar() >= 1: self._whichsol = mosek.soltype.itg - elif (self._solver_model.getprobtype() in itr_soltypes): + elif self._solver_model.getprobtype() in itr_soltypes: self._whichsol = mosek.soltype.itr - elif (self._solver_model.getprobtype() == mosek.problemtype.lo): + elif self._solver_model.getprobtype() == mosek.problemtype.lo: self._whichsol = mosek.soltype.bas def _postsolve(self): @@ -593,14 +626,14 @@ def _postsolve(self): flag = True if not flag: raise RuntimeError( - "***MOSEK solver plugin cannot extract solution suffix = " - + suffix) + "***MOSEK solver plugin cannot extract solution suffix = " + suffix + ) msk_task = self._solver_model self._set_whichsol() - if (self._whichsol == mosek.soltype.itg): + if self._whichsol == mosek.soltype.itg: if extract_reduced_costs: logger.warning("Cannot get reduced costs for MIP.") if extract_duals: @@ -617,7 +650,8 @@ def _postsolve(self): self.results.solver.name = self._name self.results.solver.wallclock_time = msk_task.getdouinf( - mosek.dinfitem.optimizer_time) + mosek.dinfitem.optimizer_time + ) SOLSTA_MAP = { mosek.solsta.unknown: 'unknown', @@ -629,7 +663,7 @@ def _postsolve(self): mosek.solsta.dual_infeas_cer: 'd_infeas', mosek.solsta.prim_illposed_cer: 'p_illposed', mosek.solsta.dual_illposed_cer: 'd_illposed', - mosek.solsta.integer_optimal: 'optimal' + mosek.solsta.integer_optimal: 'optimal', } PROSTA_MAP = { mosek.prosta.unknown: 'unknown', @@ -640,7 +674,7 @@ def _postsolve(self): mosek.prosta.dual_infeas: 'd_infeas', mosek.prosta.prim_and_dual_infeas: 'pd_infeas', mosek.prosta.ill_posed: 'illposed', - mosek.prosta.prim_infeas_or_unbounded: 'p_inf_unb' + mosek.prosta.prim_infeas_or_unbounded: 'p_inf_unb', } if self._version[0] < 9: @@ -651,12 +685,12 @@ def _postsolve(self): mosek.solsta.near_dual_feas: 'd_feas', mosek.solsta.near_prim_and_dual_feas: 'pd_feas', mosek.solsta.near_prim_infeas_cer: 'p_infeas', - mosek.solsta.near_dual_infeas_cer: 'd_infeas' + mosek.solsta.near_dual_infeas_cer: 'd_infeas', } PROSTA_OLD = { mosek.prosta.near_prim_and_dual_feas: 'pd_feas', mosek.prosta.near_prim_feas: 'p_feas', - mosek.prosta.near_dual_feas: 'd_feas' + mosek.prosta.near_dual_feas: 'd_feas', } SOLSTA_MAP.update(SOLSTA_OLD) PROSTA_MAP.update(PROSTA_OLD) @@ -667,40 +701,63 @@ def _postsolve(self): elif self._termcode == mosek.rescode.trm_max_iterations: self.results.solver.status = SolverStatus.ok - self.results.solver.termination_message = "Optimizer terminated at the maximum number of iterations." - self.results.solver.termination_condition = TerminationCondition.maxIterations + self.results.solver.termination_message = ( + "Optimizer terminated at the maximum number of iterations." + ) + self.results.solver.termination_condition = ( + TerminationCondition.maxIterations + ) soln.status = SolutionStatus.stoppedByLimit elif self._termcode == mosek.rescode.trm_max_time: self.results.solver.status = SolverStatus.ok - self.results.solver.termination_message = "Optimizer terminated at the maximum amount of time." - self.results.solver.termination_condition = TerminationCondition.maxTimeLimit + self.results.solver.termination_message = ( + "Optimizer terminated at the maximum amount of time." + ) + self.results.solver.termination_condition = ( + TerminationCondition.maxTimeLimit + ) soln.status = SolutionStatus.stoppedByLimit elif self._termcode == mosek.rescode.trm_user_callback: self.results.solver.status = SolverStatus.aborted - self.results.solver.termination_message = "Optimizer terminated due to the return of the "\ + self.results.solver.termination_message = ( + "Optimizer terminated due to the return of the " "user-defined callback function." - self.results.solver.termination_condition = TerminationCondition.userInterrupt + ) + self.results.solver.termination_condition = ( + TerminationCondition.userInterrupt + ) soln.status = SolutionStatus.unknown - elif self._termcode in [mosek.rescode.trm_mio_num_relaxs, - mosek.rescode.trm_mio_num_branches, - mosek.rescode.trm_num_max_num_int_solutions]: + elif self._termcode in [ + mosek.rescode.trm_mio_num_relaxs, + mosek.rescode.trm_mio_num_branches, + mosek.rescode.trm_num_max_num_int_solutions, + ]: self.results.solver.status = SolverStatus.ok - self.results.solver.termination_message = "The mixed-integer optimizer terminated as the maximum number "\ + self.results.solver.termination_message = ( + "The mixed-integer optimizer terminated as the maximum number " "of relaxations/branches/feasible solutions was reached." - self.results.solver.termination_condition = TerminationCondition.maxEvaluations + ) + self.results.solver.termination_condition = ( + TerminationCondition.maxEvaluations + ) soln.status = SolutionStatus.stoppedByLimit else: - self.results.solver.termination_message = " Optimization terminated with {} response code." \ + self.results.solver.termination_message = ( + " Optimization terminated with {} response code." "Check MOSEK response code documentation for more information.".format( - self._termcode) + self._termcode + ) + ) self.results.solver.termination_condition = TerminationCondition.unknown if SOLSTA_MAP[sol_status] == 'unknown': self.results.solver.status = SolverStatus.warning - self.results.solver.termination_message += " The solution status is unknown." + self.results.solver.termination_message += ( + " The solution status is unknown." + ) self.results.solver.Message = self.results.solver.termination_message self.results.solver.termination_condition = TerminationCondition.unknown soln.status = SolutionStatus.unknown @@ -721,34 +778,46 @@ def _postsolve(self): elif PROSTA_MAP[pro_status] == 'pd_infeas': self.results.solver.status = SolverStatus.warning - self.results.solver.termination_message += " Problem is primal and dual infeasible." + self.results.solver.termination_message += ( + " Problem is primal and dual infeasible." + ) self.results.solver.Message = self.results.solver.termination_message self.results.solver.termination_condition = TerminationCondition.infeasible soln.status = SolutionStatus.infeasible elif PROSTA_MAP[pro_status] == 'p_inf_unb': self.results.solver.status = SolverStatus.warning - self.results.solver.termination_message += " Problem is either primal infeasible or unbounded."\ + self.results.solver.termination_message += ( + " Problem is either primal infeasible or unbounded." " This may happen for MIPs." + ) self.results.solver.Message = self.results.solver.termination_message - self.results.solver.termination_condition = TerminationCondition.infeasibleOrUnbounded + self.results.solver.termination_condition = ( + TerminationCondition.infeasibleOrUnbounded + ) soln.status = SolutionStatus.unsure if SOLSTA_MAP[sol_status] == 'optimal': self.results.solver.status = SolverStatus.ok - self.results.solver.termination_message += " Model was solved to optimality and an optimal solution is available." + self.results.solver.termination_message += ( + " Model was solved to optimality and an optimal solution is available." + ) self.results.solver.termination_condition = TerminationCondition.optimal soln.status = SolutionStatus.optimal elif SOLSTA_MAP[sol_status] == 'pd_feas': self.results.solver.status = SolverStatus.ok - self.results.solver.termination_message += " The solution is both primal and dual feasible." + self.results.solver.termination_message += ( + " The solution is both primal and dual feasible." + ) self.results.solver.termination_condition = TerminationCondition.feasible soln.status = SolutionStatus.feasible elif SOLSTA_MAP[sol_status] == 'p_feas': self.results.solver.status = SolverStatus.ok - self.results.solver.termination_message += " The solution is primal feasible." + self.results.solver.termination_message += ( + " The solution is primal feasible." + ) self.results.solver.termination_condition = TerminationCondition.feasible soln.status = SolutionStatus.feasible @@ -760,14 +829,18 @@ def _postsolve(self): elif SOLSTA_MAP[sol_status] == 'd_infeas': self.results.solver.status = SolverStatus.warning - self.results.solver.termination_message += " The solution is a certificate of dual infeasibility." + self.results.solver.termination_message += ( + " The solution is a certificate of dual infeasibility." + ) self.results.solver.Message = self.results.solver.termination_message self.results.solver.termination_condition = TerminationCondition.unbounded soln.status = SolutionStatus.infeasible elif SOLSTA_MAP[sol_status] == 'p_infeas': self.results.solver.status = SolverStatus.warning - self.results.solver.termination_message += " The solution is a certificate of primal infeasibility." + self.results.solver.termination_message += ( + " The solution is a certificate of primal infeasibility." + ) self.results.solver.Message = self.results.solver.termination_message self.results.solver.termination_condition = TerminationCondition.infeasible soln.status = SolutionStatus.infeasible @@ -780,8 +853,8 @@ def _postsolve(self): self.results.problem.sense = maximize else: raise RuntimeError( - 'Unrecognized Mosek objective sense: {0}'.format( - msk_task.getobjname())) + 'Unrecognized Mosek objective sense: {0}'.format(msk_task.getobjname()) + ) self.results.problem.upper_bound = None self.results.problem.lower_bound = None @@ -789,47 +862,45 @@ def _postsolve(self): if msk_task.getnumintvar() == 0: try: if msk_task.getobjsense() == mosek.objsense.minimize: - self.results.problem.upper_bound = msk_task.getprimalobj( - whichsol) - self.results.problem.lower_bound = msk_task.getdualobj( - whichsol) + self.results.problem.upper_bound = msk_task.getprimalobj(whichsol) + self.results.problem.lower_bound = msk_task.getdualobj(whichsol) elif msk_task.getobjsense() == mosek.objsense.maximize: - self.results.problem.upper_bound = msk_task.getprimalobj( - whichsol) - self.results.problem.lower_bound = msk_task.getdualobj( - whichsol) + self.results.problem.upper_bound = msk_task.getprimalobj(whichsol) + self.results.problem.lower_bound = msk_task.getdualobj(whichsol) except (mosek.MosekException, AttributeError): pass elif msk_task.getobjsense() == mosek.objsense.minimize: # minimizing try: - self.results.problem.upper_bound = msk_task.getprimalobj( - whichsol) + self.results.problem.upper_bound = msk_task.getprimalobj(whichsol) except (mosek.MosekException, AttributeError): pass try: self.results.problem.lower_bound = msk_task.getdouinf( - mosek.dinfitem.mio_obj_bound) + mosek.dinfitem.mio_obj_bound + ) except (mosek.MosekException, AttributeError): pass elif msk_task.getobjsense() == mosek.objsense.maximize: # maximizing try: self.results.problem.upper_bound = msk_task.getdouinf( - mosek.dinfitem.mio_obj_bound) + mosek.dinfitem.mio_obj_bound + ) except (mosek.MosekException, AttributeError): pass try: - self.results.problem.lower_bound = msk_task.getprimalobj( - whichsol) + self.results.problem.lower_bound = msk_task.getprimalobj(whichsol) except (mosek.MosekException, AttributeError): pass else: raise RuntimeError( - 'Unrecognized Mosek objective sense: {0}'.format( - msk_task.getobjsense())) + 'Unrecognized Mosek objective sense: {0}'.format(msk_task.getobjsense()) + ) try: - soln.gap = self.results.problem.upper_bound - self.results.problem.lower_bound + soln.gap = ( + self.results.problem.upper_bound - self.results.problem.lower_bound + ) except TypeError: soln.gap = None @@ -837,8 +908,9 @@ def _postsolve(self): self.results.problem.number_of_nonzeros = msk_task.getnumanz() self.results.problem.number_of_variables = msk_task.getnumvar() self.results.problem.number_of_integer_variables = msk_task.getnumintvar() - self.results.problem.number_of_continuous_variables = msk_task.getnumvar( - ) - msk_task.getnumintvar() + self.results.problem.number_of_continuous_variables = ( + msk_task.getnumvar() - msk_task.getnumintvar() + ) self.results.problem.number_of_objectives = 1 self.results.problem.number_of_solutions = 1 @@ -852,8 +924,11 @@ def _postsolve(self): soln_constraints = soln.constraint mosek_vars = list(range(msk_task.getnumvar())) - mosek_vars = list(set(mosek_vars).intersection( - set(self._pyomo_var_to_solver_var_map.values()))) + mosek_vars = list( + set(mosek_vars).intersection( + set(self._pyomo_var_to_solver_var_map.values()) + ) + ) var_vals = [0.0] * len(mosek_vars) self._solver_model.getxx(whichsol, var_vals) names = list(map(msk_task.getvarname, mosek_vars)) @@ -864,9 +939,8 @@ def _postsolve(self): soln_variables[name] = {"Value": val} if extract_reduced_costs: - vals = [0.0]*len(mosek_vars) - msk_task.getreducedcosts( - whichsol, 0, len(mosek_vars), vals) + vals = [0.0] * len(mosek_vars) + msk_task.getreducedcosts(whichsol, 0, len(mosek_vars), vals) for mosek_var, val, name in zip(mosek_vars, vals, names): pyomo_var = self._solver_var_to_pyomo_var_map[mosek_var] if self._referenced_variables[pyomo_var] > 0: @@ -897,7 +971,7 @@ def _postsolve(self): if extract_duals: ncon = msk_task.getnumcon() if ncon > 0: - vals = [0.0]*ncon + vals = [0.0] * ncon msk_task.gety(whichsol, vals) for val, name in zip(vals, con_names): soln_constraints[name]["Dual"] = val @@ -910,41 +984,50 @@ def _postsolve(self): if ncone > 0: mosek_cones = list(range(ncone)) cone_duals = list(range(msk_task.getnumvar())) - vals = [0]*len(cone_duals) + vals = [0] * len(cone_duals) self._solver_model.getsnx(whichsol, vals) for name, cone in zip(cone_names, mosek_cones): dim = msk_task.getnumconemem(cone) # Indices of cone members - members = [0]*dim + members = [0] * dim msk_task.getcone(cone, members) # Save dual info soln_constraints[name]["Dual"] = tuple( - vals[i] for i in members) + vals[i] for i in members + ) # MOSEK >= 10, i.e. affine conic constraints else: ncone = msk_task.getnumacc() if ncone > 0: mosek_cones = range(msk_task.getnumacc()) - cone_dims = [msk_task.getaccn( - i) for i in mosek_cones] + cone_dims = [msk_task.getaccn(i) for i in mosek_cones] vals = self._solver_model.getaccdotys(whichsol) dim = 0 for name, cone in zip(cone_names, mosek_cones): soln_constraints[name]['Dual'] = tuple( - vals[dim:dim+cone_dims[cone]]) + vals[dim : dim + cone_dims[cone]] + ) dim += cone_dims[cone] if extract_slacks: - Ax = [0]*len(mosek_cons) + Ax = [0] * len(mosek_cons) msk_task.getxc(self._whichsol, Ax) for con, name in zip(mosek_cons, con_names): Us = Ls = 0 bk, lb, ub = msk_task.getconbound(con) - if bk in [mosek.boundkey.fx, mosek.boundkey.ra, mosek.boundkey.up]: + if bk in [ + mosek.boundkey.fx, + mosek.boundkey.ra, + mosek.boundkey.up, + ]: Us = ub - Ax[con] - if bk in [mosek.boundkey.fx, mosek.boundkey.ra, mosek.boundkey.lo]: + if bk in [ + mosek.boundkey.fx, + mosek.boundkey.ra, + mosek.boundkey.lo, + ]: Ls = Ax[con] - lb if Us > Ls: @@ -984,11 +1067,13 @@ def _warm_start(self): for pyomo_var, mosek_var in self._pyomo_var_to_solver_var_map.items(): if pyomo_var.value is not None: self._solver_model.putxxslice( - self._whichsol, mosek_var, mosek_var + 1, [(pyomo_var.value)]) + self._whichsol, mosek_var, mosek_var + 1, [(pyomo_var.value)] + ) if (self._version[0] > 9) & (self._whichsol == mosek.soltype.itg): self._solver_model.putintparam( - mosek.iparam.mio_construct_sol, mosek.onoffkey.on) + mosek.iparam.mio_construct_sol, mosek.onoffkey.on + ) def _load_vars(self, vars_to_load=None): var_map = self._pyomo_var_to_solver_var_map @@ -1014,9 +1099,10 @@ def _load_rc(self, vars_to_load=None): vars_to_load = var_map.keys() mosek_vars_to_load = [var_map[pyomo_var] for pyomo_var in vars_to_load] - vals = [0.0]*len(mosek_vars_to_load) + vals = [0.0] * len(mosek_vars_to_load) self._solver_model.getreducedcosts( - self._whichsol, 0, len(mosek_vars_to_load), vals) + self._whichsol, 0, len(mosek_vars_to_load), vals + ) for var, val in zip(vars_to_load, vals): if ref_vars[var] > 0: @@ -1034,7 +1120,7 @@ def _load_duals(self, objs_to_load=None): if objs_to_load is None: # constraints mosek_cons_to_load = range(self._solver_model.getnumcon()) - vals = [0.0]*len(mosek_cons_to_load) + vals = [0.0] * len(mosek_cons_to_load) self._solver_model.gety(self._whichsol, vals) for mosek_con, val in zip(mosek_cons_to_load, vals): pyomo_con = reverse_con_map[mosek_con] @@ -1052,13 +1138,13 @@ def _load_duals(self, objs_to_load=None): """ # cones (MOSEK <= 9) if self._version[0] <= 9: - vals = [0.0]*self._solver_model.getnumvar() + vals = [0.0] * self._solver_model.getnumvar() self._solver_model.getsnx(self._whichsol, vals) for mosek_cone in range(self._solver_model.getnumcone()): dim = self._solver_model.getnumconemem(mosek_cone) # Indices of cone members - members = [0]*dim + members = [0] * dim self._solver_model.getcone(mosek_cone, members) # Save dual info pyomo_cone = reverse_cone_map[mosek_cone] @@ -1066,14 +1152,16 @@ def _load_duals(self, objs_to_load=None): # cones (MOSEK >= 10, i.e. affine conic constraints) else: mosek_cones_to_load = range(self._solver_model.getnumacc()) - mosek_cone_dims = [self._solver_model.getaccn( - i) for i in mosek_cones_to_load] + mosek_cone_dims = [ + self._solver_model.getaccn(i) for i in mosek_cones_to_load + ] vals = self._solver_model.getaccdotys(self._whichsol) dim = 0 for mosek_cone in mosek_cones_to_load: pyomo_cone = reverse_cone_map[mosek_cone] dual[pyomo_cone] = tuple( - vals[dim:dim+mosek_cone_dims[mosek_cone]]) + vals[dim : dim + mosek_cone_dims[mosek_cone]] + ) dim += mosek_cone_dims[mosek_cone] else: mosek_cons_to_load = [] @@ -1088,11 +1176,10 @@ def _load_duals(self, objs_to_load=None): if len(mosek_cons_to_load) > 0: mosek_cons_first = min(mosek_cons_to_load) mosek_cons_last = max(mosek_cons_to_load) - vals = [0.0]*(mosek_cons_last - mosek_cons_first + 1) - self._solver_model.getyslice(self._whichsol, - mosek_cons_first, - mosek_cons_last, - vals) + vals = [0.0] * (mosek_cons_last - mosek_cons_first + 1) + self._solver_model.getyslice( + self._whichsol, mosek_cons_first, mosek_cons_last, vals + ) for mosek_con in mosek_cons_to_load: slice_index = mosek_con - mosek_cons_first val = vals[slice_index] @@ -1116,11 +1203,11 @@ def _load_duals(self, objs_to_load=None): # cones (MOSEK <= 9) if len(mosek_cones_to_load) > 0: if self._version[0] <= 9: - vals = [0]*self._solver_model.getnumvar() + vals = [0] * self._solver_model.getnumvar() self._solver_model.getsnx(self._whichsol, vals) for mosek_cone in mosek_cones_to_load: dim = self._solver_model.getnumconemem(mosek_cone) - members = [0]*dim + members = [0] * dim self._solver_model.getcone(mosek_cone, members) pyomo_cone = reverse_cone_map[mosek_cone] dual[pyomo_cone] = tuple(vals[i] for i in members) @@ -1129,7 +1216,8 @@ def _load_duals(self, objs_to_load=None): for mosek_cone in mosek_cones_to_load: pyomo_cone = reverse_cone_map[mosek_cone] dual[pyomo_cone] = tuple( - self._solver_model.getaccdoty(self._whichsol, mosek_cone)) + self._solver_model.getaccdoty(self._whichsol, mosek_cone) + ) def _load_slacks(self, cons_to_load=None): if not hasattr(self._pyomo_model, 'slack'): @@ -1141,10 +1229,9 @@ def _load_slacks(self, cons_to_load=None): if cons_to_load is None: mosek_cons_to_load = range(self._solver_model.getnumcon()) else: - mosek_cons_to_load = set([con_map[pyomo_con] - for pyomo_con in cons_to_load]) + mosek_cons_to_load = set([con_map[pyomo_con] for pyomo_con in cons_to_load]) - Ax = [0]*len(mosek_cons_to_load) + Ax = [0] * len(mosek_cons_to_load) self._solver_model.getxc(self._whichsol, Ax) for con in mosek_cons_to_load: pyomo_con = reverse_con_map[con] diff --git a/pyomo/solvers/plugins/solvers/mosek_persistent.py b/pyomo/solvers/plugins/solvers/mosek_persistent.py index 2c801dc06ba..4e2aa97b379 100644 --- a/pyomo/solvers/plugins/solvers/mosek_persistent.py +++ b/pyomo/solvers/plugins/solvers/mosek_persistent.py @@ -20,8 +20,9 @@ from pyomo.solvers.plugins.solvers.mosek_direct import MOSEKDirect from pyomo.solvers.plugins.solvers.persistent_solver import PersistentSolver from pyomo.solvers.plugins.solvers.direct_solver import DirectSolver -from pyomo.solvers.plugins.solvers.direct_or_persistent_solver import \ - DirectOrPersistentSolver +from pyomo.solvers.plugins.solvers.direct_or_persistent_solver import ( + DirectOrPersistentSolver, +) from pyomo.opt.base import SolverFactory from pyomo.core.kernel.conic import _ConicBase from pyomo.core.kernel.block import block @@ -31,11 +32,11 @@ class MOSEKPersistent(PersistentSolver, MOSEKDirect): """ This class provides a persistent interface between pyomo and MOSEK's Optimizer API. - As a child to the MOSEKDirect class, this interface does not need any file IO. - Furthermore, the persistent interface preserves the MOSEK task object, allowing + As a child to the MOSEKDirect class, this interface does not need any file IO. + Furthermore, the persistent interface preserves the MOSEK task object, allowing users to make incremental changes (such as removing variables/constraints, modifying - variables, adding columns etc.) to their models. Note that users are responsible for - informing the persistent interface of any incremental change. For instance, if a new + variables, adding columns etc.) to their models. Note that users are responsible for + informing the persistent interface of any incremental change. For instance, if a new variable is defined, then it would need to be added explicitly by calling the add_var method, before the solver knows of its existence. Keyword Arguments @@ -79,7 +80,7 @@ def add_constraints(self, con_seq): This will keep any existing model components intact. - NOTE: If this method is used to add cones, then the cones should be + NOTE: If this method is used to add cones, then the cones should be passed as constraints. Use the add_block method for conic_domains. Parameters @@ -118,16 +119,18 @@ def remove_vars(self, *solver_vars): except KeyError: v_name = self._symbol_map.getSymbol(v, self._labeler) raise ValueError( - "Variable {} needs to be added before removal.".format(v_name)) + "Variable {} needs to be added before removal.".format(v_name) + ) var_num = self._solver_model.getnumvar() for i, v in enumerate(self._pyomo_var_to_solver_var_map): self._pyomo_var_to_solver_var_map[v] = i - self._solver_var_to_pyomo_var_map = dict(zip(( - range(var_num)), self._pyomo_var_to_solver_var_map.keys())) + self._solver_var_to_pyomo_var_map = dict( + zip((range(var_num)), self._pyomo_var_to_solver_var_map.keys()) + ) def remove_constraint(self, solver_con): """ - Remove a single constraint from the model as well as the MOSEK task. + Remove a single constraint from the model as well as the MOSEK task. This will keep any other model components intact. @@ -141,7 +144,7 @@ def remove_constraint(self, solver_con): def remove_constraints(self, *solver_cons): """ Remove multiple constraints from the model as well as the MOSEK task in one - method call. + method call. This will keep any other model components intact. To remove conic-domains, use the remove_block method. @@ -150,10 +153,10 @@ def remove_constraints(self, *solver_cons): ---------- *solver_cons: Constraint (scalar Constraint or single _ConstraintData) """ - lq_cons = tuple(itertools.filterfalse( - lambda x: isinstance(x, _ConicBase), solver_cons)) - cone_cons = tuple( - filter(lambda x: isinstance(x, _ConicBase), solver_cons)) + lq_cons = tuple( + itertools.filterfalse(lambda x: isinstance(x, _ConicBase), solver_cons) + ) + cone_cons = tuple(filter(lambda x: isinstance(x, _ConicBase), solver_cons)) try: lq = [] cones = [] @@ -172,11 +175,14 @@ def remove_constraints(self, *solver_cons): except KeyError: c_name = self._symbol_map.getSymbol(c, self._labeler) raise ValueError( - "Constraint/Cone {} needs to be added before removal.".format(c_name)) - self._solver_con_to_pyomo_con_map = dict(zip( - range(lq_num), self._pyomo_con_to_solver_con_map.keys())) - self._solver_cone_to_pyomo_cone_map = dict(zip( - range(cone_num), self._pyomo_cone_to_solver_cone_map.keys())) + "Constraint/Cone {} needs to be added before removal.".format(c_name) + ) + self._solver_con_to_pyomo_con_map = dict( + zip(range(lq_num), self._pyomo_con_to_solver_con_map.keys()) + ) + self._solver_cone_to_pyomo_cone_map = dict( + zip(range(cone_num), self._pyomo_cone_to_solver_cone_map.keys()) + ) for i, c in enumerate(self._pyomo_con_to_solver_con_map): self._pyomo_con_to_solver_con_map[c] = i for i, c in enumerate(self._pyomo_cone_to_solver_cone_map): @@ -206,16 +212,22 @@ def update_vars(self, *solver_vars): for v in solver_vars: var_ids.append(self._pyomo_var_to_solver_var_map[v]) vtypes = tuple(map(self._mosek_vartype_from_var, solver_vars)) - lbs = tuple(value(v) if v.fixed - else -float('inf') if value(v.lb) is None - else value(v.lb) - for v in solver_vars - ) - ubs = tuple(value(v) if v.fixed - else float('inf') if value(v.ub) is None - else value(v.ub) - for v in solver_vars - ) + lbs = tuple( + value(v) + if v.fixed + else -float('inf') + if value(v.lb) is None + else value(v.lb) + for v in solver_vars + ) + ubs = tuple( + value(v) + if v.fixed + else float('inf') + if value(v.ub) is None + else value(v.ub) + for v in solver_vars + ) fxs = tuple(v.is_fixed() for v in solver_vars) bound_types = tuple(map(self._mosek_bounds, lbs, ubs, fxs)) self._solver_model.putvartypelist(var_ids, vtypes) @@ -224,21 +236,22 @@ def update_vars(self, *solver_vars): v_name = self._symbol_map.getSymbol(v, self._labeler) raise ValueError( "Variable {} needs to be added before it can be modified.".format( - v_name)) + v_name + ) + ) def _add_column(self, var, obj_coef, constraints, coefficients): self.add_var(var) var_num = self._solver_model.getnumvar() - self._solver_model.putcj(var_num-1, obj_coef) - self._solver_model.putacol( - var_num-1, constraints, coefficients) + self._solver_model.putcj(var_num - 1, obj_coef) + self._solver_model.putacol(var_num - 1, constraints, coefficients) self._referenced_variables[var] = len(constraints) def write(self, filename): """ Write the model to a file. MOSEK can write files in various popular formats such as: lp, mps, ptf, cbf etc. - In addition to the file formats mentioned above, MOSEK can + In addition to the file formats mentioned above, MOSEK can also write files to native formats such as : opf, task and jtask. The task format is binary, and is the preferred format for sharing with the MOSEK staff in case of queries, since it saves diff --git a/pyomo/solvers/plugins/solvers/persistent_solver.py b/pyomo/solvers/plugins/solvers/persistent_solver.py index be8e8b6611b..bba9a75f94a 100644 --- a/pyomo/solvers/plugins/solvers/persistent_solver.py +++ b/pyomo/solvers/plugins/solvers/persistent_solver.py @@ -9,7 +9,9 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.solvers.plugins.solvers.direct_or_persistent_solver import DirectOrPersistentSolver +from pyomo.solvers.plugins.solvers.direct_or_persistent_solver import ( + DirectOrPersistentSolver, +) from pyomo.core.base.block import _BlockData from pyomo.core.kernel.block import IBlock from pyomo.core.base.suffix import active_import_suffix_generator @@ -28,6 +30,7 @@ logger = logging.getLogger('pyomo.solvers') + def _convert_to_const(val): if val.__class__ in native_numeric_types: return val @@ -36,6 +39,7 @@ def _convert_to_const(val): else: return value(val) + class PersistentSolver(DirectOrPersistentSolver): """ A base class for persistent solvers. Direct solver interfaces do not use any file io. @@ -100,7 +104,7 @@ def add_block(self, block): # see PR #366 for discussion about handling indexed # objects and keeping compatibility with the # pyomo.kernel objects - #if block.is_indexed(): + # if block.is_indexed(): # for sub_block in block.values(): # self._add_block(block) # return @@ -116,7 +120,9 @@ def set_objective(self, obj): obj: Objective """ if self._pyomo_model is None: - raise RuntimeError('You must call set_instance before calling set_objective.') + raise RuntimeError( + 'You must call set_instance before calling set_objective.' + ) return self._set_objective(obj) def add_constraint(self, con): @@ -130,14 +136,16 @@ def add_constraint(self, con): """ if self._pyomo_model is None: - raise RuntimeError('You must call set_instance before calling add_constraint.') + raise RuntimeError( + 'You must call set_instance before calling add_constraint.' + ) # see PR #366 for discussion about handling indexed # objects and keeping compatibility with the # pyomo.kernel objects - #if con.is_indexed(): + # if con.is_indexed(): # for child_con in con.values(): # self._add_constraint(child_con) - #else: + # else: self._add_constraint(con) def add_var(self, var): @@ -157,10 +165,10 @@ def add_var(self, var): # see PR #366 for discussion about handling indexed # objects and keeping compatibility with the # pyomo.kernel objects - #if var.is_indexed(): + # if var.is_indexed(): # for child_var in var.values(): # self._add_var(child_var) - #else: + # else: self._add_var(var) def add_sos_constraint(self, con): @@ -174,23 +182,25 @@ def add_sos_constraint(self, con): """ if self._pyomo_model is None: - raise RuntimeError('You must call set_instance before calling add_sos_constraint.') + raise RuntimeError( + 'You must call set_instance before calling add_sos_constraint.' + ) # see PR #366 for discussion about handling indexed # objects and keeping compatibility with the # pyomo.kernel objects - #if con.is_indexed(): + # if con.is_indexed(): # for child_con in con.values(): # self._add_sos_constraint(child_con) - #else: + # else: self._add_sos_constraint(con) def add_column(self, model, var, obj_coef, constraints, coefficients): """Add a column to the solver's and Pyomo model This will add the Pyomo variable var to the solver's - model, and put the coefficients on the associated + model, and put the coefficients on the associated constraints in the solver model. If the obj_coef is - not zero, it will add obj_coef*var to the objective + not zero, it will add obj_coef*var to the objective of both the Pyomo and solver's model. Parameters @@ -198,30 +208,37 @@ def add_column(self, model, var, obj_coef, constraints, coefficients): model: pyomo ConcreteModel to which the column will be added var: Var (scalar Var or single _VarData) obj_coef: float, pyo.Param - constraints: list of scalar Constraints of single _ConstraintDatas + constraints: list of scalar Constraints of single _ConstraintDatas coefficients: list of the coefficient to put on var in the associated constraint """ if self._pyomo_model is None: raise RuntimeError('You must call set_instance before calling add_column.') if id(self._pyomo_model) != id(model): - raise RuntimeError('The pyomo model which the column is being added to ' - 'must be the same as the pyomo model attached to this ' - 'PersistentSolver instance; i.e., the same pyomo model ' - 'used in set_instance.') + raise RuntimeError( + 'The pyomo model which the column is being added to ' + 'must be the same as the pyomo model attached to this ' + 'PersistentSolver instance; i.e., the same pyomo model ' + 'used in set_instance.' + ) if id(self._pyomo_model) != id(var.model()): raise RuntimeError('The pyomo var must be attached to the solver model') if var in self._pyomo_var_to_solver_var_map: - raise RuntimeError('The pyomo var must not have been already added to ' - 'the solver model') + raise RuntimeError( + 'The pyomo var must not have been already added to ' 'the solver model' + ) if len(constraints) != len(coefficients): - raise RuntimeError('The list of constraints and the list of coefficents ' - 'be of equal length') + raise RuntimeError( + 'The list of constraints and the list of coefficents ' + 'be of equal length' + ) obj_coef, constraints, coefficients = self._add_and_collect_column_data( - var, obj_coef, constraints, coefficients) + var, obj_coef, constraints, coefficients + ) self._add_column(var, obj_coef, constraints, coefficients) """ This method should be implemented by subclasses.""" + def _add_column(self, var, obj_coef, constraints, coefficients): raise NotImplementedError('This method should be implemented by subclasses.') @@ -233,10 +250,10 @@ def _add_and_collect_column_data(self, var, obj_coef, constraints, coefficients) Returns the column and objective coefficient data to pass to the solver """ ## process the objective - if obj_coef.__class__ in native_numeric_types and obj_coef == 0.: - pass ## nothing to do + if obj_coef.__class__ in native_numeric_types and obj_coef == 0.0: + pass ## nothing to do else: - self._objective.expr += obj_coef*var + self._objective.expr += obj_coef * var self._vars_referenced_by_obj.add(var) obj_coef = _convert_to_const(obj_coef) @@ -244,8 +261,8 @@ def _add_and_collect_column_data(self, var, obj_coef, constraints, coefficients) ## column information coeff_list = list() constr_list = list() - for val,c in zip(coefficients,constraints): - c._body += val*var + for val, c in zip(coefficients, constraints): + c._body += val * var self._vars_referenced_by_con[c].add(var) cval = _convert_to_const(val) @@ -255,14 +272,17 @@ def _add_and_collect_column_data(self, var, obj_coef, constraints, coefficients) return obj_coef, constr_list, coeff_list """ This method should be implemented by subclasses.""" + def _remove_constraint(self, solver_con): raise NotImplementedError('This method should be implemented by subclasses.') """ This method should be implemented by subclasses.""" + def _remove_sos_constraint(self, solver_sos_con): raise NotImplementedError('This method should be implemented by subclasses.') """ This method should be implemented by subclasses.""" + def _remove_var(self, solver_var): raise NotImplementedError('This method should be implemented by subclasses.') @@ -281,18 +301,24 @@ def remove_block(self, block): # see PR #366 for discussion about handling indexed # objects and keeping compatibility with the # pyomo.kernel objects - #if block.is_indexed(): + # if block.is_indexed(): # for sub_block in block.values(): # self.remove_block(sub_block) # return for sub_block in block.block_data_objects(descend_into=True, active=True): - for con in sub_block.component_data_objects(ctype=Constraint, descend_into=False, active=True): + for con in sub_block.component_data_objects( + ctype=Constraint, descend_into=False, active=True + ): self.remove_constraint(con) - for con in sub_block.component_data_objects(ctype=SOSConstraint, descend_into=False, active=True): + for con in sub_block.component_data_objects( + ctype=SOSConstraint, descend_into=False, active=True + ): self.remove_sos_constraint(con) - for var in block.component_data_objects(ctype=Var, descend_into=True, active=True): + for var in block.component_data_objects( + ctype=Var, descend_into=True, active=True + ): self.remove_var(var) def remove_constraint(self, con): @@ -308,7 +334,7 @@ def remove_constraint(self, con): # see PR #366 for discussion about handling indexed # objects and keeping compatibility with the # pyomo.kernel objects - #if con.is_indexed(): + # if con.is_indexed(): # for child_con in con.values(): # self.remove_constraint(child_con) # return @@ -334,7 +360,7 @@ def remove_sos_constraint(self, con): # see PR #366 for discussion about handling indexed # objects and keeping compatibility with the # pyomo.kernel objects - #if con.is_indexed(): + # if con.is_indexed(): # for child_con in con.values(): # self.remove_sos_constraint(child_con) # return @@ -360,13 +386,17 @@ def remove_var(self, var): # see PR #366 for discussion about handling indexed # objects and keeping compatibility with the # pyomo.kernel objects - #if var.is_indexed(): + # if var.is_indexed(): # for child_var in var.values(): # self.remove_var(child_var) # return if self._referenced_variables[var] != 0: - raise ValueError('Cannot remove Var {0} because it is still referenced by the '.format(var) + - 'objective or one or more constraints') + raise ValueError( + 'Cannot remove Var {0} because it is still referenced by the '.format( + var + ) + + 'objective or one or more constraints' + ) solver_var = self._pyomo_var_to_solver_var_map[var] self._remove_var(solver_var) self._symbol_map.removeSymbol(var) @@ -375,6 +405,7 @@ def remove_var(self, var): del self._solver_var_to_pyomo_var_map[solver_var] """ This method should be implemented by subclasses.""" + def update_var(self, var): """ Update a variable in the solver's model. This will update bounds, fix/unfix the variable as needed, and update @@ -416,21 +447,28 @@ def solve(self, *args, **kwds): if len(args) != 0: if self._pyomo_model is not args[0]: msg = 'The problem instance provided to the solve method is not the same as the instance provided' - msg += ' to the set_instance method in the persistent solver interface. ' + msg += ( + ' to the set_instance method in the persistent solver interface. ' + ) raise ValueError(msg) self.available(exception_flag=True) # Collect suffix names to try and import from solution. if isinstance(self._pyomo_model, _BlockData): - model_suffixes = list(name for (name, comp) in active_import_suffix_generator(self._pyomo_model)) + model_suffixes = list( + name + for (name, comp) in active_import_suffix_generator(self._pyomo_model) + ) else: assert isinstance(self._pyomo_model, IBlock) - model_suffixes = list(comp.storage_key for comp in - import_suffix_generator(self._pyomo_model, - active=True, - descend_into=False)) + model_suffixes = list( + comp.storage_key + for comp in import_suffix_generator( + self._pyomo_model, active=True, descend_into=False + ) + ) if len(model_suffixes) > 0: kwds_suffixes = kwds.setdefault('suffixes', []) @@ -450,7 +488,9 @@ def solve(self, *args, **kwds): self.options = Bunch() self.options.update(orig_options) self.options.update(kwds.pop('options', {})) - self.options.update(self._options_string_to_dict(kwds.pop('options_string', ''))) + self.options.update( + self._options_string_to_dict(kwds.pop('options_string', '')) + ) try: # we're good to go. @@ -460,7 +500,10 @@ def solve(self, *args, **kwds): presolve_completion_time = time.time() if self._report_timing: - print(" %6.2f seconds required for presolve" % (presolve_completion_time - initial_time)) + print( + " %6.2f seconds required for presolve" + % (presolve_completion_time - initial_time) + ) if self._pyomo_model is not None: self._initialize_callbacks(self._pyomo_model) @@ -472,21 +515,24 @@ def solve(self, *args, **kwds): logger.warning( "Solver (%s) did not return a solver status code.\n" "This is indicative of an internal solver plugin error.\n" - "Please report this to the Pyomo developers.") + "Please report this to the Pyomo developers." + ) elif _status.rc: logger.error( "Solver (%s) returned non-zero return code (%s)" - % (self.name, _status.rc,)) + % (self.name, _status.rc) + ) if self._tee: - logger.error( - "See the solver log above for diagnostic information.") + logger.error("See the solver log above for diagnostic information.") elif hasattr(_status, 'log') and _status.log: logger.error("Solver log:\n" + str(_status.log)) - raise ApplicationError( - "Solver (%s) did not exit normally" % self.name) + raise ApplicationError("Solver (%s) did not exit normally" % self.name) solve_completion_time = time.time() if self._report_timing: - print(" %6.2f seconds required for solver" % (solve_completion_time - presolve_completion_time)) + print( + " %6.2f seconds required for solver" + % (solve_completion_time - presolve_completion_time) + ) result = self._postsolve() # *********************************************************** @@ -499,10 +545,12 @@ def solve(self, *args, **kwds): if _model: if isinstance(_model, IBlock): if len(result.solution) == 1: - result.solution(0).symbol_map = \ - getattr(_model, "._symbol_maps")[result._smap_id] - result.solution(0).default_variable_value = \ - self._default_variable_value + result.solution(0).symbol_map = getattr( + _model, "._symbol_maps" + )[result._smap_id] + result.solution( + 0 + ).default_variable_value = self._default_variable_value if self._load_solutions: _model.load_solution(result.solution(0)) else: @@ -513,15 +561,15 @@ def solve(self, *args, **kwds): assert len(getattr(_model, "._symbol_maps")) == 1 delattr(_model, "._symbol_maps") del result._smap_id - if self._load_solutions and \ - (len(result.solution) == 0): + if self._load_solutions and (len(result.solution) == 0): logger.error("No solution is available") else: if self._load_solutions: _model.solutions.load_from( result, select=self._select_index, - default_variable_value=self._default_variable_value) + default_variable_value=self._default_variable_value, + ) result._smap_id = None result.solution.clear() else: @@ -531,8 +579,10 @@ def solve(self, *args, **kwds): postsolve_completion_time = time.time() if self._report_timing: - print(" %6.2f seconds required for postsolve" % (postsolve_completion_time - - solve_completion_time)) + print( + " %6.2f seconds required for postsolve" + % (postsolve_completion_time - solve_completion_time) + ) finally: # diff --git a/pyomo/solvers/plugins/solvers/pywrapper.py b/pyomo/solvers/plugins/solvers/pywrapper.py index 839cd84594b..8f72e630a3d 100644 --- a/pyomo/solvers/plugins/solvers/pywrapper.py +++ b/pyomo/solvers/plugins/solvers/pywrapper.py @@ -19,22 +19,24 @@ @SolverFactory.register('py', doc='Direct python solver interfaces') class pywrapper(OptSolver): - """Direct python solver interface - """ + """Direct python solver interface""" def __new__(cls, *args, **kwds): mode = kwds.get('solver_io', 'python') if mode is None: mode = 'python' if mode != 'python': - logging.getLogger('pyomo.solvers').error("Cannot specify IO mode '%s' for direct python solver interface" % mode) + logging.getLogger('pyomo.solvers').error( + "Cannot specify IO mode '%s' for direct python solver interface" % mode + ) return None # if not 'solver' in kwds: - logging.getLogger('pyomo.solvers').warning("No solver specified for direct python solver interface") + logging.getLogger('pyomo.solvers').warning( + "No solver specified for direct python solver interface" + ) return None kwds['solver_io'] = 'python' solver = kwds['solver'] del kwds['solver'] return SolverFactory(solver, **kwds) - diff --git a/pyomo/solvers/plugins/solvers/xpress_direct.py b/pyomo/solvers/plugins/solvers/xpress_direct.py index 8e7975f784a..b2a89580860 100644 --- a/pyomo/solvers/plugins/solvers/xpress_direct.py +++ b/pyomo/solvers/plugins/solvers/xpress_direct.py @@ -25,7 +25,9 @@ from pyomo.core.staleflag import StaleFlagManager from pyomo.repn import generate_standard_repn from pyomo.solvers.plugins.solvers.direct_solver import DirectSolver -from pyomo.solvers.plugins.solvers.direct_or_persistent_solver import DirectOrPersistentSolver +from pyomo.solvers.plugins.solvers.direct_or_persistent_solver import ( + DirectOrPersistentSolver, +) from pyomo.core.kernel.objective import minimize, maximize from pyomo.opt.results.results_ import SolverResults from pyomo.opt.results.solution import Solution, SolutionStatus @@ -37,26 +39,29 @@ logger = logging.getLogger('pyomo.solvers') + class DegreeError(ValueError): pass -def _is_convertable(conv_type,x): + +def _is_convertable(conv_type, x): try: conv_type(x) except ValueError: return False return True + def _print_message(xp_prob, _, msg, *args): if msg is not None: - sys.stdout.write(msg+'\n') + sys.stdout.write(msg + '\n') sys.stdout.flush() + def _finalize_xpress_import(xpress, avail): if not avail: return - XpressDirect._version = tuple( - int(k) for k in xpress.getversion().split('.')) + XpressDirect._version = tuple(int(k) for k in xpress.getversion().split('.')) XpressDirect._name = "Xpress %s.%s.%s" % XpressDirect._version # in versions prior to 34, xpress raised a RuntimeError, but # in more recent versions it raises a @@ -70,6 +75,7 @@ def _finalize_xpress_import(xpress, avail): if not hasattr(xpress, 'rng'): xpress.rng = xpress.range + class _xpress_importer_class(object): # We want to be able to *update* the message that the deferred # import generates using the stdout recorded during the actual @@ -96,6 +102,7 @@ def __call__(self): self.import_message += OUT.getvalue() return xpress + _xpress_importer = _xpress_importer_class() xpress, xpress_available = attempt_import( 'xpress', @@ -160,8 +167,8 @@ def available(self, exception_flag=True): if exception_flag and not xpress_available: xpress.log_import_warning(logger=__name__) raise ApplicationError( - "No Python bindings available for %s solver plugin" - % (type(self),)) + "No Python bindings available for %s solver plugin" % (type(self),) + ) return bool(xpress_available) def _apply_solver(self): @@ -169,7 +176,7 @@ def _apply_solver(self): self._solver_model.setlogfile(self._log_file) if self._keepfiles: - print("Solver log file: "+self._log_file) + print("Solver log file: " + self._log_file) # Setting a log file in xpress disables all output # in xpress versions less than 36. @@ -190,7 +197,7 @@ def _apply_solver(self): # get the xpress valid controls xp_controls = xpress.controls for key, option in self.options.items(): - if key == 'mipgap': # handled above + if key == 'mipgap': # handled above continue try: self._solver_model.setControl(key, option) @@ -235,50 +242,64 @@ def _get_mip_results(self, results, soln): mip_sols = xprob_attrs.mipsols if status == xp.mip_not_loaded: results.solver.status = SolverStatus.aborted - results.solver.termination_message = "Model is not loaded; no solution information is available." + results.solver.termination_message = ( + "Model is not loaded; no solution information is available." + ) results.solver.termination_condition = TerminationCondition.error soln.status = SolutionStatus.unknown - #no MIP solution, first LP did not solve, second LP did, third search started but incomplete - elif status == xp.mip_lp_not_optimal \ - or status == xp.mip_lp_optimal \ - or status == xp.mip_no_sol_found: + # no MIP solution, first LP did not solve, second LP did, third search started but incomplete + elif ( + status == xp.mip_lp_not_optimal + or status == xp.mip_lp_optimal + or status == xp.mip_no_sol_found + ): results.solver.status = SolverStatus.aborted - results.solver.termination_message = "Model is loaded, but no solution information is available." + results.solver.termination_message = ( + "Model is loaded, but no solution information is available." + ) results.solver.termination_condition = TerminationCondition.error soln.status = SolutionStatus.unknown - elif status == xp.mip_solution: # some solution available + elif status == xp.mip_solution: # some solution available results.solver.status = SolverStatus.warning - results.solver.termination_message = "Unable to satisfy optimality tolerances; a sub-optimal " \ - "solution is available." + results.solver.termination_message = ( + "Unable to satisfy optimality tolerances; a sub-optimal " + "solution is available." + ) results.solver.termination_condition = TerminationCondition.other soln.status = SolutionStatus.feasible - elif status == xp.mip_infeas: # MIP proven infeasible + elif status == xp.mip_infeas: # MIP proven infeasible results.solver.status = SolverStatus.warning results.solver.termination_message = "Model was proven to be infeasible" results.solver.termination_condition = TerminationCondition.infeasible soln.status = SolutionStatus.infeasible - elif status == xp.mip_optimal: # optimal + elif status == xp.mip_optimal: # optimal results.solver.status = SolverStatus.ok - results.solver.termination_message = "Model was solved to optimality (subject to tolerances), " \ + results.solver.termination_message = ( + "Model was solved to optimality (subject to tolerances), " "and an optimal solution is available." + ) results.solver.termination_condition = TerminationCondition.optimal soln.status = SolutionStatus.optimal elif status == xp.mip_unbounded and mip_sols > 0: results.solver.status = SolverStatus.warning - results.solver.termination_message = "LP relaxation was proven to be unbounded, " \ + results.solver.termination_message = ( + "LP relaxation was proven to be unbounded, " "but a solution is available." + ) results.solver.termination_condition = TerminationCondition.unbounded soln.status = SolutionStatus.unbounded elif status == xp.mip_unbounded and mip_sols <= 0: results.solver.status = SolverStatus.warning - results.solver.termination_message = "LP relaxation was proven to be unbounded." + results.solver.termination_message = ( + "LP relaxation was proven to be unbounded." + ) results.solver.termination_condition = TerminationCondition.unbounded soln.status = SolutionStatus.unbounded else: results.solver.status = SolverStatus.error - results.solver.termination_message = \ - ("Unhandled Xpress solve status " - "("+str(status)+")") + results.solver.termination_message = ( + "Unhandled Xpress solve status " "(" + str(status) + ")" + ) results.solver.termination_condition = TerminationCondition.error soln.status = SolutionStatus.error @@ -302,7 +323,7 @@ def _get_mip_results(self, results, soln): results.problem.lower_bound = xprob_attrs.mipbestobjval except (XpressDirect.XpressException, AttributeError): pass - + return mip_sols > 0 def _get_lp_results(self, results, soln): @@ -316,13 +337,17 @@ def _get_lp_results(self, results, soln): status = xprob_attrs.lpstatus if status == xp.lp_unstarted: results.solver.status = SolverStatus.aborted - results.solver.termination_message = "Model is not loaded; no solution information is available." + results.solver.termination_message = ( + "Model is not loaded; no solution information is available." + ) results.solver.termination_condition = TerminationCondition.error soln.status = SolutionStatus.unknown elif status == xp.lp_optimal: results.solver.status = SolverStatus.ok - results.solver.termination_message = "Model was solved to optimality (subject to tolerances), " \ + results.solver.termination_message = ( + "Model was solved to optimality (subject to tolerances), " "and an optimal solution is available." + ) results.solver.termination_condition = TerminationCondition.optimal soln.status = SolutionStatus.optimal elif status == xp.lp_infeas: @@ -332,13 +357,17 @@ def _get_lp_results(self, results, soln): soln.status = SolutionStatus.infeasible elif status == xp.lp_cutoff: results.solver.status = SolverStatus.ok - results.solver.termination_message = "Optimal objective for model was proven to be worse than the " \ + results.solver.termination_message = ( + "Optimal objective for model was proven to be worse than the " "cutoff value specified; a solution is available." + ) results.solver.termination_condition = TerminationCondition.minFunctionValue soln.status = SolutionStatus.optimal elif status == xp.lp_unfinished: results.solver.status = SolverStatus.aborted - results.solver.termination_message = "Optimization was terminated by the user." + results.solver.termination_message = ( + "Optimization was terminated by the user." + ) results.solver.termination_condition = TerminationCondition.error soln.status = SolutionStatus.error elif status == xp.lp_unbounded: @@ -348,26 +377,32 @@ def _get_lp_results(self, results, soln): soln.status = SolutionStatus.unbounded elif status == xp.lp_cutoff_in_dual: results.solver.status = SolverStatus.ok - results.solver.termination_message = "Xpress reported the LP was cutoff in the dual." + results.solver.termination_message = ( + "Xpress reported the LP was cutoff in the dual." + ) results.solver.termination_condition = TerminationCondition.minFunctionValue soln.status = SolutionStatus.optimal elif status == xp.lp_unsolved: results.solver.status = SolverStatus.error - results.solver.termination_message = "Optimization was terminated due to unrecoverable numerical " \ + results.solver.termination_message = ( + "Optimization was terminated due to unrecoverable numerical " "difficulties." + ) results.solver.termination_condition = TerminationCondition.error soln.status = SolutionStatus.error elif status == xp.lp_nonconvex: results.solver.status = SolverStatus.error - results.solver.termination_message = "Optimization was terminated because nonconvex quadratic data " \ + results.solver.termination_message = ( + "Optimization was terminated because nonconvex quadratic data " "were found." + ) results.solver.termination_condition = TerminationCondition.error soln.status = SolutionStatus.error else: results.solver.status = SolverStatus.error - results.solver.termination_message = \ - ("Unhandled Xpress solve status " - "("+str(status)+")") + results.solver.termination_message = ( + "Unhandled Xpress solve status " "(" + str(status) + ")" + ) results.solver.termination_condition = TerminationCondition.error soln.status = SolutionStatus.error @@ -382,8 +417,11 @@ def _get_lp_results(self, results, soln): # Not all solution information will be available in all cases, it is # up to the caller/user to check the actual status and figure which # of x, slack, duals, reduced costs are valid. - return xprob_attrs.lpstatus in [xp.lp_optimal, xp.lp_cutoff, - xp.lp_cutoff_in_dual] + return xprob_attrs.lpstatus in [ + xp.lp_optimal, + xp.lp_cutoff, + xp.lp_cutoff_in_dual, + ] def _get_nlp_results(self, results, soln): """Sets up `results` and `soln` and returns whether there is a solution @@ -406,10 +444,12 @@ def _get_nlp_results(self, results, soln): status = xprob_attrs.xslp_nlpstatus solstatus = xprob_attrs.xslp_solstatus have_soln = False - optimal = False # *globally* optimal? + optimal = False # *globally* optimal? if status == xp.nlp_unstarted: results.solver.status = SolverStatus.unknown - results.solver.termination_message = "Non-convex model solve was not start" + results.solver.termination_message = ( + "Non-convex model solve was not start" + ) results.solver.termination_condition = TerminationCondition.unknown soln.status = SolutionStatus.unknown elif status == xp.nlp_locally_optimal: @@ -417,46 +457,64 @@ def _get_nlp_results(self, results, soln): # we must look at the solstatus to figure out which if solstatus in [2, 3]: results.solver.status = SolverStatus.ok - results.solver.termination_message = "Non-convex model was solved to local optimality" - results.solver.termination_condition = TerminationCondition.locallyOptimal + results.solver.termination_message = ( + "Non-convex model was solved to local optimality" + ) + results.solver.termination_condition = ( + TerminationCondition.locallyOptimal + ) soln.status = SolutionStatus.locallyOptimal else: results.solver.status = SolverStatus.ok - results.solver.termination_message = "Feasible solution found for non-convex model" + results.solver.termination_message = ( + "Feasible solution found for non-convex model" + ) results.solver.termination_condition = TerminationCondition.feasible soln.status = SolutionStatus.feasible have_soln = True elif status == xp.nlp_globally_optimal: results.solver.status = SolverStatus.ok - results.solver.termination_message = "Non-convex model was solved to global optimality" + results.solver.termination_message = ( + "Non-convex model was solved to global optimality" + ) results.solver.termination_condition = TerminationCondition.optimal soln.status = SolutionStatus.optimal have_soln = True optimal = True elif status == xp.nlp_locally_infeasible: results.solver.status = SolverStatus.ok - results.solver.termination_message = "Non-convex model was proven to be locally infeasible" + results.solver.termination_message = ( + "Non-convex model was proven to be locally infeasible" + ) results.solver.termination_condition = TerminationCondition.noSolution soln.status = SolutionStatus.unknown elif status == xp.nlp_infeasible: results.solver.status = SolverStatus.ok - results.solver.termination_message = "Non-conex model was proven to be infeasible" + results.solver.termination_message = ( + "Non-conex model was proven to be infeasible" + ) results.solver.termination_condition = TerminationCondition.infeasible soln.status = SolutionStatus.infeasible - elif status == xp.nlp_unbounded: # locally unbounded! + elif status == xp.nlp_unbounded: # locally unbounded! results.solver.status = SolverStatus.ok - results.solver.termination_message = "Non-convex model is locally unbounded" + results.solver.termination_message = ( + "Non-convex model is locally unbounded" + ) results.solver.termination_condition = TerminationCondition.unbounded soln.status = SolutionStatus.unbounded elif status == xp.nlp_unfinished: results.solver.status = SolverStatus.ok - results.solver.termination_message = "Non-convex solve not finished (numerical issues?)" + results.solver.termination_message = ( + "Non-convex solve not finished (numerical issues?)" + ) results.solver.termination_condition = TerminationCondition.unknown soln.status = SolutionStatus.unknown have_soln = True else: results.solver.status = SolverStatus.error - results.solver.termination_message = "Error for non-convex model: " + str(status) + results.solver.termination_message = ( + "Error for non-convex model: " + str(status) + ) results.solver.termination_condition = TerminationCondition.error soln.status = SolutionStatus.error @@ -469,7 +527,7 @@ def _get_nlp_results(self, results, soln): results.problem.lower_bound = xprob_attrs.xslp_objval except (XpressDirect.XpressException, AttributeError): pass - + return have_soln def _solve_model(self): @@ -489,7 +547,7 @@ def _solve_model(self): else: xprob.lpoptimize() self._get_results = self._get_lp_results - + self._solver_model.postsolve() def _get_expr_from_pyomo_repn(self, repn, max_degree=2): @@ -497,19 +555,30 @@ def _get_expr_from_pyomo_repn(self, repn, max_degree=2): degree = repn.polynomial_degree() if (degree is None) or (degree > max_degree): - raise DegreeError('XpressDirect does not support expressions of degree {0}.'.format(degree)) + raise DegreeError( + 'XpressDirect does not support expressions of degree {0}.'.format( + degree + ) + ) # NOTE: xpress's python interface only allows for expresions # with native numeric types. Others, like numpy.float64, # will cause an exception when constructing expressions if len(repn.linear_vars) > 0: referenced_vars.update(repn.linear_vars) - new_expr = xpress.Sum(float(coef)*self._pyomo_var_to_solver_var_map[var] for coef,var in zip(repn.linear_coefs, repn.linear_vars)) + new_expr = xpress.Sum( + float(coef) * self._pyomo_var_to_solver_var_map[var] + for coef, var in zip(repn.linear_coefs, repn.linear_vars) + ) else: new_expr = 0.0 - for coef,(x,y) in zip(repn.quadratic_coefs,repn.quadratic_vars): - new_expr += float(coef) * self._pyomo_var_to_solver_var_map[x] * self._pyomo_var_to_solver_var_map[y] + for coef, (x, y) in zip(repn.quadratic_coefs, repn.quadratic_vars): + new_expr += ( + float(coef) + * self._pyomo_var_to_solver_var_map[x] + * self._pyomo_var_to_solver_var_map[y] + ) referenced_vars.add(x) referenced_vars.add(y) @@ -524,7 +593,9 @@ def _get_expr_from_pyomo_expr(self, expr, max_degree=2): repn = generate_standard_repn(expr, quadratic=False) try: - xpress_expr, referenced_vars = self._get_expr_from_pyomo_repn(repn, max_degree) + xpress_expr, referenced_vars = self._get_expr_from_pyomo_repn( + repn, max_degree + ) except DegreeError as e: msg = e.args[0] msg += '\nexpr: {0}'.format(expr) @@ -560,7 +631,9 @@ def _add_var(self, var): if lb == ub: self._solver_model.chgbounds([xpress_var], ['B'], [lb]) else: - self._solver_model.chgbounds([xpress_var, xpress_var], ['L', 'U'], [lb,ub]) + self._solver_model.chgbounds( + [xpress_var, xpress_var], ['L', 'U'], [lb, ub] + ) self._pyomo_var_to_solver_var_map[var] = xpress_var self._solver_var_to_pyomo_var_map[xpress_var] = var @@ -580,10 +653,11 @@ def _set_instance(self, model, kwds={}): self._solver_model = xpress.problem() except Exception: e = sys.exc_info()[1] - msg = ("Unable to create Xpress model. " - "Have you installed the Python " - "bindings for Xpress?\n\n\t" + - "Error message: {0}".format(e)) + msg = ( + "Unable to create Xpress model. " + "Have you installed the Python " + "bindings for Xpress?\n\n\t" + "Error message: {0}".format(e) + ) raise Exception(msg) self._add_block(model) @@ -602,47 +676,50 @@ def _add_constraint(self, con): if con._linear_canonical_form: xpress_expr, referenced_vars = self._get_expr_from_pyomo_repn( - con.canonical_form(), - self._max_constraint_degree) + con.canonical_form(), self._max_constraint_degree + ) else: xpress_expr, referenced_vars = self._get_expr_from_pyomo_expr( - con.body, - self._max_constraint_degree) + con.body, self._max_constraint_degree + ) if con.has_lb(): if not is_fixed(con.lower): - raise ValueError("Lower bound of constraint {0} " - "is not constant.".format(con)) + raise ValueError( + "Lower bound of constraint {0} " "is not constant.".format(con) + ) if con.has_ub(): if not is_fixed(con.upper): - raise ValueError("Upper bound of constraint {0} " - "is not constant.".format(con)) + raise ValueError( + "Upper bound of constraint {0} " "is not constant.".format(con) + ) if con.equality: - xpress_con = xpress.constraint(body=xpress_expr, - sense=xpress.eq, - rhs=value(con.lower), - name=conname) + xpress_con = xpress.constraint( + body=xpress_expr, sense=xpress.eq, rhs=value(con.lower), name=conname + ) elif con.has_lb() and con.has_ub(): - xpress_con = xpress.constraint(body=xpress_expr, - sense=xpress.rng, - lb=value(con.lower), - ub=value(con.upper), - name=conname) + xpress_con = xpress.constraint( + body=xpress_expr, + sense=xpress.rng, + lb=value(con.lower), + ub=value(con.upper), + name=conname, + ) self._range_constraints.add(xpress_con) elif con.has_lb(): - xpress_con = xpress.constraint(body=xpress_expr, - sense=xpress.geq, - rhs=value(con.lower), - name=conname) + xpress_con = xpress.constraint( + body=xpress_expr, sense=xpress.geq, rhs=value(con.lower), name=conname + ) elif con.has_ub(): - xpress_con = xpress.constraint(body=xpress_expr, - sense=xpress.leq, - rhs=value(con.upper), - name=conname) + xpress_con = xpress.constraint( + body=xpress_expr, sense=xpress.leq, rhs=value(con.upper), name=conname + ) else: - raise ValueError("Constraint does not have a lower " - "or an upper bound: {0} \n".format(con)) + raise ValueError( + "Constraint does not have a lower " + "or an upper bound: {0} \n".format(con) + ) self._solver_model.addConstraint(xpress_con) @@ -658,9 +735,10 @@ def _add_sos_constraint(self, con): conname = self._symbol_map.getSymbol(con, self._labeler) level = con.level - if level not in [1,2]: - raise ValueError("Solver does not support SOS " - "level {0} constraints".format(level)) + if level not in [1, 2]: + raise ValueError( + "Solver does not support SOS " "level {0} constraints".format(level) + ) xpress_vars = [] weights = [] @@ -698,7 +776,9 @@ def _xpress_vartype_from_var(self, var): elif var.is_continuous(): vartype = xpress.continuous else: - raise ValueError('Variable domain type is not recognized for {0}'.format(var.domain)) + raise ValueError( + 'Variable domain type is not recognized for {0}'.format(var.domain) + ) return vartype def _set_objective(self, obj): @@ -718,7 +798,9 @@ def _set_objective(self, obj): else: raise ValueError('Objective sense is not recognized: {0}'.format(obj.sense)) - xpress_expr, referenced_vars = self._get_expr_from_pyomo_expr(obj.expr, self._max_obj_degree) + xpress_expr, referenced_vars = self._get_expr_from_pyomo_expr( + obj.expr, self._max_obj_degree + ) for var in referenced_vars: self._referenced_variables[var] += 1 @@ -748,7 +830,10 @@ def _postsolve(self): extract_reduced_costs = True flag = True if not flag: - raise RuntimeError("***The xpress_direct solver plugin cannot extract solution suffix="+suffix) + raise RuntimeError( + "***The xpress_direct solver plugin cannot extract solution suffix=" + + suffix + ) xprob = self._solver_model xp = xpress @@ -773,7 +858,9 @@ def _postsolve(self): self.results.solver.wallclock_time = self._opt_time if not hasattr(self, '_get_results'): - raise RuntimeError('Model was solved but `_get_results` property is not set') + raise RuntimeError( + 'Model was solved but `_get_results` property is not set' + ) have_soln = self._get_results(self.results, soln) self.results.problem.name = xprob_attrs.matrixname @@ -782,18 +869,26 @@ def _postsolve(self): elif xprob_attrs.objsense == -1.0: self.results.problem.sense = maximize else: - raise RuntimeError('Unrecognized Xpress objective sense: {0}'.format(xprob_attrs.objsense)) + raise RuntimeError( + 'Unrecognized Xpress objective sense: {0}'.format(xprob_attrs.objsense) + ) try: - soln.gap = self.results.problem.upper_bound - self.results.problem.lower_bound + soln.gap = ( + self.results.problem.upper_bound - self.results.problem.lower_bound + ) except TypeError: soln.gap = None - self.results.problem.number_of_constraints = xprob_attrs.rows + xprob_attrs.sets + xprob_attrs.qconstraints + self.results.problem.number_of_constraints = ( + xprob_attrs.rows + xprob_attrs.sets + xprob_attrs.qconstraints + ) self.results.problem.number_of_nonzeros = xprob_attrs.elems self.results.problem.number_of_variables = xprob_attrs.cols self.results.problem.number_of_integer_variables = xprob_attrs.mipents - self.results.problem.number_of_continuous_variables = xprob_attrs.cols - xprob_attrs.mipents + self.results.problem.number_of_continuous_variables = ( + xprob_attrs.cols - xprob_attrs.mipents + ) self.results.problem.number_of_objectives = 1 self.results.problem.number_of_solutions = xprob_attrs.mipsols if is_mip else 1 @@ -842,8 +937,8 @@ def _postsolve(self): lb = con.lb ub = con.ub ub_s = val - expr_val = ub-ub_s - lb_s = lb-expr_val + expr_val = ub - ub_s + lb_s = lb - expr_val if abs(ub_s) > abs(lb_s): soln_constraints[con.name]["Slack"] = ub_s else: @@ -947,8 +1042,8 @@ def _load_slacks(self, cons_to_load=None): lb = con.lb ub = con.ub ub_s = val - expr_val = ub-ub_s - lb_s = lb-expr_val + expr_val = ub - ub_s + lb_s = lb - expr_val if abs(ub_s) > abs(lb_s): slack[pyomo_con] = ub_s else: diff --git a/pyomo/solvers/plugins/solvers/xpress_persistent.py b/pyomo/solvers/plugins/solvers/xpress_persistent.py index 54191da98df..da1f09eb35e 100644 --- a/pyomo/solvers/plugins/solvers/xpress_persistent.py +++ b/pyomo/solvers/plugins/solvers/xpress_persistent.py @@ -18,7 +18,9 @@ import collections -@SolverFactory.register('xpress_persistent', doc='Persistent python interface to Xpress') +@SolverFactory.register( + 'xpress_persistent', doc='Persistent python interface to Xpress' +) class XpressPersistent(PersistentSolver, XpressDirect): """ A class that provides a persistent interface to Xpress. Direct solver interfaces do not use any file io. @@ -75,7 +77,9 @@ def _xpress_chgcoltype_from_var(self, var): elif var.is_continuous(): vartype = 'C' else: - raise ValueError('Variable domain type is not recognized for {0}'.format(var.domain)) + raise ValueError( + 'Variable domain type is not recognized for {0}'.format(var.domain) + ) return vartype def update_var(self, var): @@ -92,12 +96,16 @@ def update_var(self, var): # see PR #366 for discussion about handling indexed # objects and keeping compatibility with the # pyomo.kernel objects - #if var.is_indexed(): + # if var.is_indexed(): # for child_var in var.values(): # self.update_var(child_var) # return if var not in self._pyomo_var_to_solver_var_map: - raise ValueError('The Var provided to update_var needs to be added first: {0}'.format(var)) + raise ValueError( + 'The Var provided to update_var needs to be added first: {0}'.format( + var + ) + ) xpress_var = self._pyomo_var_to_solver_var_map[var] qctype = self._xpress_chgcoltype_from_var(var) lb, ub = self._xpress_lb_ub_from_var(var) @@ -109,9 +117,9 @@ def _add_column(self, var, obj_coef, constraints, coefficients): """Add a column to the solver's model This will add the Pyomo variable var to the solver's - model, and put the coefficients on the associated + model, and put the coefficients on the associated constraints in the solver model. If the obj_coef is - not zero, it will add obj_coef*var to the objective + not zero, it will add obj_coef*var to the objective of the solver's model. Parameters @@ -127,13 +135,20 @@ def _add_column(self, var, obj_coef, constraints, coefficients): vartype = self._xpress_chgcoltype_from_var(var) lb, ub = self._xpress_lb_ub_from_var(var) - self._solver_model.addcols(objx=[obj_coef], mstart=[0,len(coefficients)], - mrwind=constraints, dmatval=coefficients, - bdl=[lb], bdu=[ub], names=[varname], - types=[vartype]) + self._solver_model.addcols( + objx=[obj_coef], + mstart=[0, len(coefficients)], + mrwind=constraints, + dmatval=coefficients, + bdl=[lb], + bdu=[ub], + names=[varname], + types=[vartype], + ) xpress_var = self._solver_model.getVariable( - index=self._solver_model.getIndexFromName(type=2, name=varname)) + index=self._solver_model.getIndexFromName(type=2, name=varname) + ) self._pyomo_var_to_solver_var_map[var] = xpress_var self._solver_var_to_pyomo_var_map[xpress_var] = var diff --git a/pyomo/solvers/tests/checks/test_BARON.py b/pyomo/solvers/tests/checks/test_BARON.py index fbae14797f4..9cfe928164a 100644 --- a/pyomo/solvers/tests/checks/test_BARON.py +++ b/pyomo/solvers/tests/checks/test_BARON.py @@ -16,18 +16,16 @@ import pyomo.common.unittest as unittest from pyomo.common.log import LoggingIntercept -from pyomo.environ import ( - ConcreteModel, Constraint, Objective, Var, log10, minimize, -) +from pyomo.environ import ConcreteModel, Constraint, Objective, Var, log10, minimize from pyomo.opt import SolverFactory, TerminationCondition # check if BARON is available from pyomo.solvers.tests.solvers import test_solver_cases + baron_available = test_solver_cases('baron', 'bar').available -@unittest.skipIf(not baron_available, - "The 'BARON' solver is not available") +@unittest.skipIf(not baron_available, "The 'BARON' solver is not available") class BaronTest(unittest.TestCase): """Test the BARON interface.""" @@ -42,37 +40,40 @@ def test_log10(self): results = opt.solve(m) - self.assertEqual(results.solver.termination_condition, - TerminationCondition.optimal) + self.assertEqual( + results.solver.termination_condition, TerminationCondition.optimal + ) def test_abs(self): # Tests the special transformation for abs with SolverFactory("baron") as opt: m = ConcreteModel() - m.x = Var(bounds=(-100,1)) + m.x = Var(bounds=(-100, 1)) m.c = Constraint(expr=abs(m.x) >= 2) m.obj = Objective(expr=m.x, sense=minimize) results = opt.solve(m) - self.assertEqual(results.solver.termination_condition, - TerminationCondition.optimal) + self.assertEqual( + results.solver.termination_condition, TerminationCondition.optimal + ) def test_pow(self): # Tests the special transformation for x ^ y (both variables) with SolverFactory("baron") as opt: m = ConcreteModel() - m.x = Var(bounds=(10,100)) - m.y = Var(bounds=(1,10)) - m.c = Constraint(expr=m.x ** m.y >= 20) + m.x = Var(bounds=(10, 100)) + m.y = Var(bounds=(1, 10)) + m.c = Constraint(expr=m.x**m.y >= 20) m.obj = Objective(expr=m.x, sense=minimize) results = opt.solve(m) - self.assertEqual(results.solver.termination_condition, - TerminationCondition.optimal) + self.assertEqual( + results.solver.termination_condition, TerminationCondition.optimal + ) def test_BARON_option_warnings(self): os = StringIO() @@ -82,15 +83,20 @@ def test_BARON_option_warnings(self): m.obj = Objective(expr=m.x**2) with SolverFactory("baron") as opt: - results = opt.solve(m, options={'ResName': 'results.lst', - 'TimName': 'results.tim'}) - - self.assertEqual(results.solver.termination_condition, - TerminationCondition.optimal) - self.assertIn('Ignoring user-specified option "ResName=results.lst"', - os.getvalue()) - self.assertIn('Ignoring user-specified option "TimName=results.tim"', - os.getvalue()) + results = opt.solve( + m, options={'ResName': 'results.lst', 'TimName': 'results.tim'} + ) + + self.assertEqual( + results.solver.termination_condition, TerminationCondition.optimal + ) + self.assertIn( + 'Ignoring user-specified option "ResName=results.lst"', os.getvalue() + ) + self.assertIn( + 'Ignoring user-specified option "TimName=results.tim"', os.getvalue() + ) + if __name__ == '__main__': unittest.main() diff --git a/pyomo/solvers/tests/checks/test_CBCplugin.py b/pyomo/solvers/tests/checks/test_CBCplugin.py index f1386bd2431..fe01a89bb53 100644 --- a/pyomo/solvers/tests/checks/test_CBCplugin.py +++ b/pyomo/solvers/tests/checks/test_CBCplugin.py @@ -15,12 +15,21 @@ import pyomo.common.unittest as unittest -from pyomo.environ import (ConcreteModel, Var, Objective, RangeSet, - Constraint, Reals, NonNegativeIntegers, - NonNegativeReals, Integers, Binary, - maximize, minimize) -from pyomo.opt import (SolverFactory, ProblemSense, - TerminationCondition, SolverStatus) +from pyomo.environ import ( + ConcreteModel, + Var, + Objective, + RangeSet, + Constraint, + Reals, + NonNegativeIntegers, + NonNegativeReals, + Integers, + Binary, + maximize, + minimize, +) +from pyomo.opt import SolverFactory, ProblemSense, TerminationCondition, SolverStatus from pyomo.solvers.plugins.solvers.CBCplugin import CBCSHELL cbc_available = SolverFactory('cbc', solver_io='lp').available(exception_flag=False) @@ -54,21 +63,31 @@ def test_infeasible_lp(self): results = self.opt.solve(self.model) self.assertEqual(ProblemSense.minimize, results.problem.sense) - self.assertEqual(TerminationCondition.infeasible, results.solver.termination_condition) - self.assertEqual('Model was proven to be infeasible.', results.solver.termination_message) + self.assertEqual( + TerminationCondition.infeasible, results.solver.termination_condition + ) + self.assertEqual( + 'Model was proven to be infeasible.', results.solver.termination_message + ) self.assertEqual(SolverStatus.warning, results.solver.status) @unittest.skipIf(not cbc_available, "The 'cbc' solver is not available") def test_unbounded_lp(self): self.model.Idx = RangeSet(2) self.model.X = Var(self.model.Idx, within=Reals) - self.model.Obj = Objective(expr=self.model.X[1] + self.model.X[2], sense=maximize) + self.model.Obj = Objective( + expr=self.model.X[1] + self.model.X[2], sense=maximize + ) results = self.opt.solve(self.model) self.assertEqual(ProblemSense.maximize, results.problem.sense) - self.assertEqual(TerminationCondition.unbounded, results.solver.termination_condition) - self.assertEqual('Model was proven to be unbounded.', results.solver.termination_message) + self.assertEqual( + TerminationCondition.unbounded, results.solver.termination_condition + ) + self.assertEqual( + 'Model was proven to be unbounded.', results.solver.termination_message + ) self.assertEqual(SolverStatus.warning, results.solver.status) @unittest.skipIf(not cbc_available, "The 'cbc' solver is not available") @@ -81,10 +100,13 @@ def test_optimal_lp(self): self.assertEqual(0.0, results.problem.lower_bound) self.assertEqual(0.0, results.problem.upper_bound) self.assertEqual(ProblemSense.minimize, results.problem.sense) - self.assertEqual(TerminationCondition.optimal, results.solver.termination_condition) + self.assertEqual( + TerminationCondition.optimal, results.solver.termination_condition + ) self.assertEqual( 'Model was solved to optimality (subject to tolerances), and an optimal solution is available.', - results.solver.termination_message) + results.solver.termination_message, + ) self.assertEqual(SolverStatus.ok, results.solver.status) @unittest.skipIf(not cbc_available, "The 'cbc' solver is not available") @@ -97,8 +119,12 @@ def test_infeasible_mip(self): results = self.opt.solve(self.model) self.assertEqual(ProblemSense.minimize, results.problem.sense) - self.assertEqual(TerminationCondition.infeasible, results.solver.termination_condition) - self.assertEqual('Model was proven to be infeasible.', results.solver.termination_message) + self.assertEqual( + TerminationCondition.infeasible, results.solver.termination_condition + ) + self.assertEqual( + 'Model was proven to be infeasible.', results.solver.termination_message + ) self.assertEqual(SolverStatus.warning, results.solver.status) @unittest.skipIf(not cbc_available, "The 'cbc' solver is not available") @@ -109,8 +135,12 @@ def test_unbounded_mip(self): results = self.opt.solve(self.model) self.assertEqual(ProblemSense.minimize, results.problem.sense) - self.assertEqual(TerminationCondition.unbounded, results.solver.termination_condition) - self.assertEqual('Model was proven to be unbounded.', results.solver.termination_message) + self.assertEqual( + TerminationCondition.unbounded, results.solver.termination_condition + ) + self.assertEqual( + 'Model was proven to be unbounded.', results.solver.termination_message + ) self.assertEqual(SolverStatus.warning, results.solver.status) @unittest.skipIf(not cbc_available, "The 'cbc' solver is not available") @@ -119,8 +149,9 @@ def test_optimal_mip(self): self.model.X = Var(self.model.Idx, within=NonNegativeIntegers) self.model.Y = Var(self.model.Idx, within=Binary) self.model.C1 = Constraint(expr=self.model.X[1] == self.model.X[2] + 1) - self.model.Obj = Objective(expr=self.model.Y[1] + self.model.Y[2] - self.model.X[1], - sense=maximize) + self.model.Obj = Objective( + expr=self.model.Y[1] + self.model.Y[2] - self.model.X[1], sense=maximize + ) results = self.opt.solve(self.model) @@ -129,10 +160,13 @@ def test_optimal_mip(self): self.assertEqual(results.problem.number_of_binary_variables, 2) self.assertEqual(results.problem.number_of_integer_variables, 4) self.assertEqual(ProblemSense.maximize, results.problem.sense) - self.assertEqual(TerminationCondition.optimal, results.solver.termination_condition) + self.assertEqual( + TerminationCondition.optimal, results.solver.termination_condition + ) self.assertEqual( 'Model was solved to optimality (subject to tolerances), and an optimal solution is available.', - results.solver.termination_message) + results.solver.termination_message, + ) self.assertEqual(SolverStatus.ok, results.solver.status) @@ -212,12 +246,19 @@ def test_optimal_mip(self): self.assertEqual(SolverStatus.ok, results.solver.status) self.assertEqual(0.34, results.solver.system_time) self.assertEqual(0.72, results.solver.wallclock_time) - self.assertEqual(TerminationCondition.optimal, results.solver.termination_condition) + self.assertEqual( + TerminationCondition.optimal, results.solver.termination_condition + ) self.assertEqual( 'Model was solved to optimality (subject to tolerances), and an optimal solution is available.', - results.solver.termination_message) - self.assertEqual(results.solver.statistics.branch_and_bound.number_of_bounded_subproblems, 2) - self.assertEqual(results.solver.statistics.branch_and_bound.number_of_created_subproblems, 2) + results.solver.termination_message, + ) + self.assertEqual( + results.solver.statistics.branch_and_bound.number_of_bounded_subproblems, 2 + ) + self.assertEqual( + results.solver.statistics.branch_and_bound.number_of_created_subproblems, 2 + ) self.assertEqual(results.solver.statistics.black_box.number_of_iterations, 625) def test_max_time_limit_mip(self): @@ -227,17 +268,26 @@ def test_max_time_limit_mip(self): lp_file = 'max_time_limit.out.lp' results = self.opt.solve(os.path.join(data_dir, lp_file)) - self.assertEqual(1.1084706, results.problem.lower_bound) # Note that we ignore the lower bound given at the end + self.assertEqual( + 1.1084706, results.problem.lower_bound + ) # Note that we ignore the lower bound given at the end self.assertEqual(1.35481947, results.problem.upper_bound) self.assertEqual(SolverStatus.aborted, results.solver.status) self.assertEqual(0.1, results.solver.system_time) self.assertEqual(0.11, results.solver.wallclock_time) - self.assertEqual(TerminationCondition.maxTimeLimit, results.solver.termination_condition) + self.assertEqual( + TerminationCondition.maxTimeLimit, results.solver.termination_condition + ) self.assertEqual( 'Optimization terminated because the time expended exceeded the value specified in the seconds parameter.', - results.solver.termination_message) - self.assertEqual(results.solver.statistics.branch_and_bound.number_of_bounded_subproblems, 0) - self.assertEqual(results.solver.statistics.branch_and_bound.number_of_created_subproblems, 0) + results.solver.termination_message, + ) + self.assertEqual( + results.solver.statistics.branch_and_bound.number_of_bounded_subproblems, 0 + ) + self.assertEqual( + results.solver.statistics.branch_and_bound.number_of_created_subproblems, 0 + ) self.assertEqual(results.solver.statistics.black_box.number_of_iterations, 82) def test_intermediate_non_integer_mip(self): @@ -251,12 +301,20 @@ def test_intermediate_non_integer_mip(self): self.assertEqual(SolverStatus.aborted, results.solver.status) self.assertEqual(0.02, results.solver.system_time) self.assertEqual(0.02, results.solver.wallclock_time) - self.assertEqual(TerminationCondition.intermediateNonInteger, results.solver.termination_condition) + self.assertEqual( + TerminationCondition.intermediateNonInteger, + results.solver.termination_condition, + ) self.assertEqual( 'Optimization terminated because a limit was hit, however it had not found an integer solution yet.', - results.solver.termination_message) - self.assertEqual(results.solver.statistics.branch_and_bound.number_of_bounded_subproblems, 0) - self.assertEqual(results.solver.statistics.branch_and_bound.number_of_created_subproblems, 0) + results.solver.termination_message, + ) + self.assertEqual( + results.solver.statistics.branch_and_bound.number_of_bounded_subproblems, 0 + ) + self.assertEqual( + results.solver.statistics.branch_and_bound.number_of_created_subproblems, 0 + ) self.assertEqual(results.solver.statistics.black_box.number_of_iterations, 0) def test_max_solutions(self): @@ -271,12 +329,20 @@ def test_max_solutions(self): self.assertEqual(SolverStatus.aborted, results.solver.status) self.assertEqual(0.03, results.solver.system_time) self.assertEqual(0.03, results.solver.wallclock_time) - self.assertEqual(TerminationCondition.other, results.solver.termination_condition) + self.assertEqual( + TerminationCondition.other, results.solver.termination_condition + ) self.assertEqual( 'Optimization terminated because the number of solutions found reached the value specified in the ' - 'maxSolutions parameter.', results.solver.termination_message) - self.assertEqual(results.solver.statistics.branch_and_bound.number_of_bounded_subproblems, 0) - self.assertEqual(results.solver.statistics.branch_and_bound.number_of_created_subproblems, 0) + 'maxSolutions parameter.', + results.solver.termination_message, + ) + self.assertEqual( + results.solver.statistics.branch_and_bound.number_of_bounded_subproblems, 0 + ) + self.assertEqual( + results.solver.statistics.branch_and_bound.number_of_created_subproblems, 0 + ) self.assertEqual(results.solver.statistics.black_box.number_of_iterations, 0) def test_within_gap_tolerance(self): @@ -291,12 +357,19 @@ def test_within_gap_tolerance(self): self.assertEqual(SolverStatus.ok, results.solver.status) self.assertEqual(0.07, results.solver.system_time) self.assertEqual(0.07, results.solver.wallclock_time) - self.assertEqual(TerminationCondition.optimal, results.solver.termination_condition) + self.assertEqual( + TerminationCondition.optimal, results.solver.termination_condition + ) self.assertEqual( 'Model was solved to optimality (subject to tolerances), and an optimal solution is available.', - results.solver.termination_message) - self.assertEqual(results.solver.statistics.branch_and_bound.number_of_bounded_subproblems, 0) - self.assertEqual(results.solver.statistics.branch_and_bound.number_of_created_subproblems, 0) + results.solver.termination_message, + ) + self.assertEqual( + results.solver.statistics.branch_and_bound.number_of_bounded_subproblems, 0 + ) + self.assertEqual( + results.solver.statistics.branch_and_bound.number_of_created_subproblems, 0 + ) self.assertEqual(results.solver.statistics.black_box.number_of_iterations, 0) def test_max_evaluations(self): @@ -311,12 +384,20 @@ def test_max_evaluations(self): self.assertEqual(SolverStatus.aborted, results.solver.status) self.assertEqual(0.16, results.solver.system_time) self.assertEqual(0.18, results.solver.wallclock_time) - self.assertEqual(TerminationCondition.maxEvaluations, results.solver.termination_condition) + self.assertEqual( + TerminationCondition.maxEvaluations, results.solver.termination_condition + ) self.assertEqual( 'Optimization terminated because the total number of branch-and-cut nodes explored exceeded the value ' - 'specified in the maxNodes parameter', results.solver.termination_message) - self.assertEqual(results.solver.statistics.branch_and_bound.number_of_bounded_subproblems, 1) - self.assertEqual(results.solver.statistics.branch_and_bound.number_of_created_subproblems, 1) + 'specified in the maxNodes parameter', + results.solver.termination_message, + ) + self.assertEqual( + results.solver.statistics.branch_and_bound.number_of_bounded_subproblems, 1 + ) + self.assertEqual( + results.solver.statistics.branch_and_bound.number_of_created_subproblems, 1 + ) self.assertEqual(results.solver.statistics.black_box.number_of_iterations, 602) def test_fix_parsing_bug(self): @@ -336,12 +417,20 @@ def test_fix_parsing_bug(self): self.assertEqual(SolverStatus.aborted, results.solver.status) self.assertEqual(0.08, results.solver.system_time) self.assertEqual(0.09, results.solver.wallclock_time) - self.assertEqual(TerminationCondition.other, results.solver.termination_condition) + self.assertEqual( + TerminationCondition.other, results.solver.termination_condition + ) self.assertEqual( 'Optimization terminated because the number of solutions found reached the value specified in the ' - 'maxSolutions parameter.', results.solver.termination_message) - self.assertEqual(results.solver.statistics.branch_and_bound.number_of_bounded_subproblems, 0) - self.assertEqual(results.solver.statistics.branch_and_bound.number_of_created_subproblems, 0) + 'maxSolutions parameter.', + results.solver.termination_message, + ) + self.assertEqual( + results.solver.statistics.branch_and_bound.number_of_bounded_subproblems, 0 + ) + self.assertEqual( + results.solver.statistics.branch_and_bound.number_of_created_subproblems, 0 + ) self.assertEqual(results.solver.statistics.black_box.number_of_iterations, 0) def test_process_logfile(self): @@ -349,18 +438,28 @@ def test_process_logfile(self): cbc_shell._log_file = os.path.join(data_dir, 'test5_timeout_cbc.txt') results = cbc_shell.process_logfile() self.assertEqual(results.solution.gap, 0.01) - self.assertEqual(results.solver.statistics.black_box.number_of_iterations, 50364) + self.assertEqual( + results.solver.statistics.black_box.number_of_iterations, 50364 + ) self.assertEqual(results.solver.system_time, 2.01) - self.assertEqual(results.solver.statistics.branch_and_bound.number_of_created_subproblems, 34776) + self.assertEqual( + results.solver.statistics.branch_and_bound.number_of_created_subproblems, + 34776, + ) def test_process_logfile_gap_inf(self): cbc_shell = CBCSHELL() cbc_shell._log_file = os.path.join(data_dir, 'test5_timeout_cbc_gap.txt') results = cbc_shell.process_logfile() self.assertEqual(results.solution.gap, float('inf')) - self.assertEqual(results.solver.statistics.black_box.number_of_iterations, 50364) + self.assertEqual( + results.solver.statistics.black_box.number_of_iterations, 50364 + ) self.assertEqual(results.solver.system_time, 2.01) - self.assertEqual(results.solver.statistics.branch_and_bound.number_of_created_subproblems, 34776) + self.assertEqual( + results.solver.statistics.branch_and_bound.number_of_created_subproblems, + 34776, + ) if __name__ == "__main__": diff --git a/pyomo/solvers/tests/checks/test_CPLEXDirect.py b/pyomo/solvers/tests/checks/test_CPLEXDirect.py index 731b5080495..a2055170cf8 100644 --- a/pyomo/solvers/tests/checks/test_CPLEXDirect.py +++ b/pyomo/solvers/tests/checks/test_CPLEXDirect.py @@ -13,25 +13,39 @@ import pyomo.common.unittest as unittest -from pyomo.environ import (ConcreteModel, AbstractModel, Var, Objective, - Block, Constraint, Suffix, NonNegativeIntegers, - NonNegativeReals, Integers, Binary, is_fixed, - value) +from pyomo.environ import ( + ConcreteModel, + AbstractModel, + Var, + Objective, + Block, + Constraint, + Suffix, + NonNegativeIntegers, + NonNegativeReals, + Integers, + Binary, + is_fixed, + value, +) from pyomo.opt import SolverFactory, TerminationCondition, SolutionStatus -from pyomo.solvers.plugins.solvers.cplex_direct import (_CplexExpr, - _LinearConstraintData, - _VariableData) +from pyomo.solvers.plugins.solvers.cplex_direct import ( + _CplexExpr, + _LinearConstraintData, + _VariableData, +) try: import cplex + cplexpy_available = True except ImportError: cplexpy_available = False diff_tol = 1e-4 -class CPLEXDirectTests(unittest.TestCase): +class CPLEXDirectTests(unittest.TestCase): def setUp(self): self.stderr = sys.stderr sys.stderr = None @@ -39,53 +53,61 @@ def setUp(self): def tearDown(self): sys.stderr = self.stderr - @unittest.skipIf(not cplexpy_available, - "The 'cplex' python bindings are not available") + @unittest.skipIf( + not cplexpy_available, "The 'cplex' python bindings are not available" + ) def test_infeasible_lp(self): with SolverFactory("cplex", solver_io="python") as opt: model = ConcreteModel() model.X = Var(within=NonNegativeReals) - model.C1 = Constraint(expr= model.X==1) - model.C2 = Constraint(expr= model.X==2) - model.O = Objective(expr= model.X) + model.C1 = Constraint(expr=model.X == 1) + model.C2 = Constraint(expr=model.X == 2) + model.O = Objective(expr=model.X) results = opt.solve(model) - self.assertEqual(results.solver.termination_condition, - TerminationCondition.infeasible) + self.assertEqual( + results.solver.termination_condition, TerminationCondition.infeasible + ) - @unittest.skipIf(not cplexpy_available, - "The 'cplex' python bindings are not available") + @unittest.skipIf( + not cplexpy_available, "The 'cplex' python bindings are not available" + ) def test_unbounded_lp(self): with SolverFactory("cplex", solver_io="python") as opt: model = ConcreteModel() model.X = Var() - model.O = Objective(expr= model.X) + model.O = Objective(expr=model.X) results = opt.solve(model) - self.assertIn(results.solver.termination_condition, - (TerminationCondition.unbounded, - TerminationCondition.infeasibleOrUnbounded)) + self.assertIn( + results.solver.termination_condition, + ( + TerminationCondition.unbounded, + TerminationCondition.infeasibleOrUnbounded, + ), + ) - @unittest.skipIf(not cplexpy_available, - "The 'cplex' python bindings are not available") + @unittest.skipIf( + not cplexpy_available, "The 'cplex' python bindings are not available" + ) def test_optimal_lp(self): with SolverFactory("cplex", solver_io="python") as opt: model = ConcreteModel() model.X = Var(within=NonNegativeReals) - model.O = Objective(expr= model.X) + model.O = Objective(expr=model.X) results = opt.solve(model, load_solutions=False) - self.assertEqual(results.solution.status, - SolutionStatus.optimal) + self.assertEqual(results.solution.status, SolutionStatus.optimal) - @unittest.skipIf(not cplexpy_available, - "The 'cplex' python bindings are not available") + @unittest.skipIf( + not cplexpy_available, "The 'cplex' python bindings are not available" + ) def test_get_duals_lp(self): with SolverFactory("cplex", solver_io="python") as opt: @@ -93,10 +115,10 @@ def test_get_duals_lp(self): model.X = Var(within=NonNegativeReals) model.Y = Var(within=NonNegativeReals) - model.C1 = Constraint(expr= 2*model.X + model.Y >= 8 ) - model.C2 = Constraint(expr= model.X + 3*model.Y >= 6 ) + model.C1 = Constraint(expr=2 * model.X + model.Y >= 8) + model.C2 = Constraint(expr=model.X + 3 * model.Y >= 6) - model.O = Objective(expr= model.X + model.Y) + model.O = Objective(expr=model.X + model.Y) results = opt.solve(model, suffixes=['dual'], load_solutions=False) @@ -106,56 +128,63 @@ def test_get_duals_lp(self): self.assertAlmostEqual(model.dual[model.C1], 0.4) self.assertAlmostEqual(model.dual[model.C2], 0.2) - @unittest.skipIf(not cplexpy_available, - "The 'cplex' python bindings are not available") + @unittest.skipIf( + not cplexpy_available, "The 'cplex' python bindings are not available" + ) def test_infeasible_mip(self): with SolverFactory("cplex", solver_io="python") as opt: model = ConcreteModel() model.X = Var(within=NonNegativeIntegers) - model.C1 = Constraint(expr= model.X==1) - model.C2 = Constraint(expr= model.X==2) - model.O = Objective(expr= model.X) + model.C1 = Constraint(expr=model.X == 1) + model.C2 = Constraint(expr=model.X == 2) + model.O = Objective(expr=model.X) results = opt.solve(model) - self.assertEqual(results.solver.termination_condition, - TerminationCondition.infeasible) + self.assertEqual( + results.solver.termination_condition, TerminationCondition.infeasible + ) - @unittest.skipIf(not cplexpy_available, - "The 'cplex' python bindings are not available") + @unittest.skipIf( + not cplexpy_available, "The 'cplex' python bindings are not available" + ) def test_unbounded_mip(self): with SolverFactory("cplex", solver_io="python") as opt: model = AbstractModel() model.X = Var(within=Integers) - model.O = Objective(expr= model.X) + model.O = Objective(expr=model.X) instance = model.create_instance() results = opt.solve(instance) - self.assertIn(results.solver.termination_condition, - (TerminationCondition.unbounded, - TerminationCondition.infeasibleOrUnbounded)) + self.assertIn( + results.solver.termination_condition, + ( + TerminationCondition.unbounded, + TerminationCondition.infeasibleOrUnbounded, + ), + ) - @unittest.skipIf(not cplexpy_available, - "The 'cplex' python bindings are not available") + @unittest.skipIf( + not cplexpy_available, "The 'cplex' python bindings are not available" + ) def test_optimal_mip(self): with SolverFactory("cplex", solver_io="python") as opt: model = ConcreteModel() model.X = Var(within=NonNegativeIntegers) - model.O = Objective(expr= model.X) + model.O = Objective(expr=model.X) results = opt.solve(model, load_solutions=False) - self.assertEqual(results.solution.status, - SolutionStatus.optimal) + self.assertEqual(results.solution.status, SolutionStatus.optimal) @unittest.skipIf(not cplexpy_available, "The 'cplex' python bindings are not available") class TestIsFixedCallCount(unittest.TestCase): - """ Tests for PR#1402 (669e7b2b) """ + """Tests for PR#1402 (669e7b2b)""" def setup(self, skip_trivial_constraints): m = ConcreteModel() @@ -306,7 +335,7 @@ def test_constraint_data(self): @unittest.skipIf(not cplexpy_available, "The 'cplex' python bindings are not available") class TestAddVar(unittest.TestCase): def test_add_single_variable(self): - """ Test that the variable is added correctly to `solver_model`. """ + """Test that the variable is added correctly to `solver_model`.""" model = ConcreteModel() opt = SolverFactory("cplex", solver_io="python") @@ -340,7 +369,7 @@ def test_add_single_variable(self): self.assertEqual(opt._solver_model.variables.get_num_binary(), 1) def test_add_block_containing_single_variable(self): - """ Test that the variable is added correctly to `solver_model`. """ + """Test that the variable is added correctly to `solver_model`.""" model = ConcreteModel() opt = SolverFactory("cplex", solver_io="python") @@ -366,10 +395,10 @@ def test_add_block_containing_single_variable(self): self.assertEqual(opt._solver_model.variables.get_num_binary(), 1) def test_add_block_containing_multiple_variables(self): - """ Test that: - - The variable is added correctly to `solver_model` - - The CPLEX `variables` interface is called only once - - Fixed variable bounds are set correctly + """Test that: + - The variable is added correctly to `solver_model` + - The CPLEX `variables` interface is called only once + - Fixed variable bounds are set correctly """ model = ConcreteModel() diff --git a/pyomo/solvers/tests/checks/test_CPLEXPersistent.py b/pyomo/solvers/tests/checks/test_CPLEXPersistent.py index b78d3a2f825..28ba28a6288 100644 --- a/pyomo/solvers/tests/checks/test_CPLEXPersistent.py +++ b/pyomo/solvers/tests/checks/test_CPLEXPersistent.py @@ -12,8 +12,7 @@ import pyomo.common.unittest as unittest import pyomo.environ -from pyomo.core import (ConcreteModel, Var, Objective, - Constraint, NonNegativeReals) +from pyomo.core import ConcreteModel, Var, Objective, Constraint, NonNegativeReals from pyomo.opt import SolverFactory try: @@ -30,7 +29,7 @@ def test_quadratic_objective_is_set(self): model = ConcreteModel() model.X = Var(bounds=(-2, 2)) model.Y = Var(bounds=(-2, 2)) - model.O = Objective(expr=model.X ** 2 + model.Y ** 2) + model.O = Objective(expr=model.X**2 + model.Y**2) model.C1 = Constraint(expr=model.Y >= 2 * model.X - 1) model.C2 = Constraint(expr=model.Y >= -model.X + 2) opt = SolverFactory("cplex_persistent") @@ -41,7 +40,7 @@ def test_quadratic_objective_is_set(self): self.assertAlmostEqual(model.Y.value, 1, places=3) del model.O - model.O = Objective(expr=model.X ** 2) + model.O = Objective(expr=model.X**2) opt.set_objective(model.O) opt.solve() self.assertAlmostEqual(model.X.value, 0, places=3) @@ -70,7 +69,7 @@ def test_add_column_exceptions(self): m = ConcreteModel() m.x = Var() m.c = Constraint(expr=(0, m.x, 1)) - m.ci = Constraint([1,2], rule=lambda m,i:(0,m.x,i+1)) + m.ci = Constraint([1, 2], rule=lambda m, i: (0, m.x, i + 1)) m.cd = Constraint(expr=(0, -m.x, 1)) m.cd.deactivate() m.obj = Objective(expr=-m.x) @@ -84,7 +83,7 @@ def test_add_column_exceptions(self): m2 = ConcreteModel() m2.y = Var() - m2.c = Constraint(expr=(0,m.x,1)) + m2.c = Constraint(expr=(0, m.x, 1)) # different model than attached to opt self.assertRaises(RuntimeError, opt.add_column, m2, m2.y, 0, [], []) @@ -97,7 +96,7 @@ def test_add_column_exceptions(self): m.y = Var() # len(coefficents) == len(constraints) - self.assertRaises(RuntimeError, opt.add_column, m, m.y, -2, [m.c], [1,2]) + self.assertRaises(RuntimeError, opt.add_column, m, m.y, -2, [m.c], [1, 2]) self.assertRaises(RuntimeError, opt.add_column, m, m.y, -2, [m.c, z], [1]) # add indexed constraint diff --git a/pyomo/solvers/tests/checks/test_GAMS.py b/pyomo/solvers/tests/checks/test_GAMS.py index ca012b502a7..4038d9dd1ad 100644 --- a/pyomo/solvers/tests/checks/test_GAMS.py +++ b/pyomo/solvers/tests/checks/test_GAMS.py @@ -11,13 +11,17 @@ import pyomo.environ as pyo from pyomo.environ import ( - ConcreteModel, Var, Objective, Constraint, maximize, Expression, log10, + ConcreteModel, + Var, + Objective, + Constraint, + maximize, + Expression, + log10, ) from pyomo.opt import SolverFactory, TerminationCondition -from pyomo.solvers.plugins.solvers.GAMS import ( - GAMSShell, GAMSDirect, gdxcc_available -) +from pyomo.solvers.plugins.solvers.GAMS import GAMSShell, GAMSDirect, gdxcc_available import pyomo.common.unittest as unittest from pyomo.common.tempfiles import TempfileManager from pyomo.common.tee import capture_output @@ -34,54 +38,51 @@ class GAMSTests(unittest.TestCase): - - @unittest.skipIf(not gamspy_available, - "The 'gams' python bindings are not available") + @unittest.skipIf( + not gamspy_available, "The 'gams' python bindings are not available" + ) def test_check_expr_eval_py(self): with SolverFactory("gams", solver_io="python") as opt: m = ConcreteModel() m.x = Var() - m.e = Expression(expr= log10(m.x) + 5) - m.c = Constraint(expr= m.x >= 10) - m.o = Objective(expr= m.e) + m.e = Expression(expr=log10(m.x) + 5) + m.c = Constraint(expr=m.x >= 10) + m.o = Objective(expr=m.e) self.assertRaises(GamsExceptionExecution, opt.solve, m) - @unittest.skipIf(not gamsgms_available, - "The 'gams' executable is not available") + @unittest.skipIf(not gamsgms_available, "The 'gams' executable is not available") def test_check_expr_eval_gms(self): with SolverFactory("gams", solver_io="gms") as opt: m = ConcreteModel() m.x = Var() - m.e = Expression(expr= log10(m.x) + 5) - m.c = Constraint(expr= m.x >= 10) - m.o = Objective(expr= m.e) + m.e = Expression(expr=log10(m.x) + 5) + m.c = Constraint(expr=m.x >= 10) + m.o = Objective(expr=m.e) self.assertRaises(ValueError, opt.solve, m) - @unittest.skipIf(not gamspy_available, - "The 'gams' python bindings are not available") + @unittest.skipIf( + not gamspy_available, "The 'gams' python bindings are not available" + ) def test_file_removal_py(self): with SolverFactory("gams", solver_io="python") as opt: m = ConcreteModel() m.x = Var() - m.c = Constraint(expr= m.x >= 10) - m.o = Objective(expr= m.x) + m.c = Constraint(expr=m.x >= 10) + m.o = Objective(expr=m.x) tmpdir = mkdtemp() results = opt.solve(m, tmpdir=tmpdir) self.assertTrue(os.path.exists(tmpdir)) - self.assertFalse(os.path.exists(os.path.join(tmpdir, - '_gams_py_gjo0.gms'))) - self.assertFalse(os.path.exists(os.path.join(tmpdir, - '_gams_py_gjo0.lst'))) - self.assertFalse(os.path.exists(os.path.join(tmpdir, - '_gams_py_gdb0.gdx'))) + self.assertFalse(os.path.exists(os.path.join(tmpdir, '_gams_py_gjo0.gms'))) + self.assertFalse(os.path.exists(os.path.join(tmpdir, '_gams_py_gjo0.lst'))) + self.assertFalse(os.path.exists(os.path.join(tmpdir, '_gams_py_gdb0.gdx'))) os.rmdir(tmpdir) @@ -89,29 +90,24 @@ def test_file_removal_py(self): self.assertFalse(os.path.exists(tmpdir)) - @unittest.skipIf(not gamsgms_available, - "The 'gams' executable is not available") + @unittest.skipIf(not gamsgms_available, "The 'gams' executable is not available") def test_file_removal_gms(self): with SolverFactory("gams", solver_io="gms") as opt: m = ConcreteModel() m.x = Var() - m.c = Constraint(expr= m.x >= 10) - m.o = Objective(expr= m.x) + m.c = Constraint(expr=m.x >= 10) + m.o = Objective(expr=m.x) tmpdir = mkdtemp() results = opt.solve(m, tmpdir=tmpdir) self.assertTrue(os.path.exists(tmpdir)) - self.assertFalse(os.path.exists(os.path.join(tmpdir, - 'model.gms'))) - self.assertFalse(os.path.exists(os.path.join(tmpdir, - 'output.lst'))) - self.assertFalse(os.path.exists(os.path.join(tmpdir, - 'GAMS_MODEL_p.gdx'))) - self.assertFalse(os.path.exists(os.path.join(tmpdir, - 'GAMS_MODEL_s.gdx'))) + self.assertFalse(os.path.exists(os.path.join(tmpdir, 'model.gms'))) + self.assertFalse(os.path.exists(os.path.join(tmpdir, 'output.lst'))) + self.assertFalse(os.path.exists(os.path.join(tmpdir, 'GAMS_MODEL_p.gdx'))) + self.assertFalse(os.path.exists(os.path.join(tmpdir, 'GAMS_MODEL_s.gdx'))) os.rmdir(tmpdir) @@ -119,66 +115,59 @@ def test_file_removal_gms(self): self.assertFalse(os.path.exists(tmpdir)) - @unittest.skipIf(not gamspy_available, - "The 'gams' python bindings are not available") + @unittest.skipIf( + not gamspy_available, "The 'gams' python bindings are not available" + ) def test_keepfiles_py(self): with SolverFactory("gams", solver_io="python") as opt: m = ConcreteModel() m.x = Var() - m.c = Constraint(expr= m.x >= 10) - m.o = Objective(expr= m.x) + m.c = Constraint(expr=m.x >= 10) + m.o = Objective(expr=m.x) tmpdir = mkdtemp() results = opt.solve(m, tmpdir=tmpdir, keepfiles=True) self.assertTrue(os.path.exists(tmpdir)) - self.assertTrue(os.path.exists(os.path.join(tmpdir, - '_gams_py_gjo0.gms'))) - self.assertTrue(os.path.exists(os.path.join(tmpdir, - '_gams_py_gjo0.lst'))) - self.assertTrue(os.path.exists(os.path.join(tmpdir, - '_gams_py_gdb0.gdx'))) - self.assertTrue(os.path.exists(os.path.join(tmpdir, - '_gams_py_gjo0.pf'))) + self.assertTrue(os.path.exists(os.path.join(tmpdir, '_gams_py_gjo0.gms'))) + self.assertTrue(os.path.exists(os.path.join(tmpdir, '_gams_py_gjo0.lst'))) + self.assertTrue(os.path.exists(os.path.join(tmpdir, '_gams_py_gdb0.gdx'))) + self.assertTrue(os.path.exists(os.path.join(tmpdir, '_gams_py_gjo0.pf'))) shutil.rmtree(tmpdir) - @unittest.skipIf(not gamsgms_available, - "The 'gams' executable is not available") + @unittest.skipIf(not gamsgms_available, "The 'gams' executable is not available") def test_keepfiles_gms(self): with SolverFactory("gams", solver_io="gms") as opt: m = ConcreteModel() m.x = Var() - m.c = Constraint(expr= m.x >= 10) - m.o = Objective(expr= m.x) + m.c = Constraint(expr=m.x >= 10) + m.o = Objective(expr=m.x) tmpdir = mkdtemp() results = opt.solve(m, tmpdir=tmpdir, keepfiles=True) self.assertTrue(os.path.exists(tmpdir)) - self.assertTrue(os.path.exists(os.path.join(tmpdir, - 'model.gms'))) - self.assertTrue(os.path.exists(os.path.join(tmpdir, - 'output.lst'))) + self.assertTrue(os.path.exists(os.path.join(tmpdir, 'model.gms'))) + self.assertTrue(os.path.exists(os.path.join(tmpdir, 'output.lst'))) if gdxcc_available: - self.assertTrue(os.path.exists(os.path.join( - tmpdir, 'GAMS_MODEL_p.gdx'))) - self.assertTrue(os.path.exists(os.path.join( - tmpdir, 'results_s.gdx'))) + self.assertTrue( + os.path.exists(os.path.join(tmpdir, 'GAMS_MODEL_p.gdx')) + ) + self.assertTrue(os.path.exists(os.path.join(tmpdir, 'results_s.gdx'))) else: - self.assertTrue(os.path.exists(os.path.join( - tmpdir, 'results.dat'))) - self.assertTrue(os.path.exists(os.path.join( - tmpdir, 'resultsstat.dat'))) + self.assertTrue(os.path.exists(os.path.join(tmpdir, 'results.dat'))) + self.assertTrue(os.path.exists(os.path.join(tmpdir, 'resultsstat.dat'))) shutil.rmtree(tmpdir) - @unittest.skipIf(not gamspy_available, - "The 'gams' python bindings are not available") + @unittest.skipIf( + not gamspy_available, "The 'gams' python bindings are not available" + ) def test_fixed_var_sign_py(self): with SolverFactory("gams", solver_io="python") as opt: @@ -187,20 +176,20 @@ def test_fixed_var_sign_py(self): m.y = Var() m.z = Var() m.z.fix(-3) - m.c1 = Constraint(expr= m.x + m.y - m.z == 0) - m.c2 = Constraint(expr= m.z + m.y - m.z >= -10000) - m.c3 = Constraint(expr= -3 * m.z + m.y - m.z >= -10000) - m.c4 = Constraint(expr= -m.z + m.y - m.z >= -10000) - m.c5 = Constraint(expr= m.x <= 100) - m.o = Objective(expr= m.x, sense=maximize) + m.c1 = Constraint(expr=m.x + m.y - m.z == 0) + m.c2 = Constraint(expr=m.z + m.y - m.z >= -10000) + m.c3 = Constraint(expr=-3 * m.z + m.y - m.z >= -10000) + m.c4 = Constraint(expr=-m.z + m.y - m.z >= -10000) + m.c5 = Constraint(expr=m.x <= 100) + m.o = Objective(expr=m.x, sense=maximize) results = opt.solve(m) - self.assertEqual(results.solver.termination_condition, - TerminationCondition.optimal) + self.assertEqual( + results.solver.termination_condition, TerminationCondition.optimal + ) - @unittest.skipIf(not gamsgms_available, - "The 'gams' executable is not available") + @unittest.skipIf(not gamsgms_available, "The 'gams' executable is not available") def test_fixed_var_sign_gms(self): with SolverFactory("gams", solver_io="gms") as opt: @@ -209,55 +198,70 @@ def test_fixed_var_sign_gms(self): m.y = Var() m.z = Var() m.z.fix(-3) - m.c1 = Constraint(expr= m.x + m.y - m.z == 0) - m.c2 = Constraint(expr= m.z + m.y - m.z >= -10000) - m.c3 = Constraint(expr= -3 * m.z + m.y - m.z >= -10000) - m.c4 = Constraint(expr= -m.z + m.y - m.z >= -10000) - m.c5 = Constraint(expr= m.x <= 100) - m.o = Objective(expr= m.x, sense=maximize) + m.c1 = Constraint(expr=m.x + m.y - m.z == 0) + m.c2 = Constraint(expr=m.z + m.y - m.z >= -10000) + m.c3 = Constraint(expr=-3 * m.z + m.y - m.z >= -10000) + m.c4 = Constraint(expr=-m.z + m.y - m.z >= -10000) + m.c5 = Constraint(expr=m.x <= 100) + m.o = Objective(expr=m.x, sense=maximize) results = opt.solve(m) - self.assertEqual(results.solver.termination_condition, - TerminationCondition.optimal) + self.assertEqual( + results.solver.termination_condition, TerminationCondition.optimal + ) - @unittest.skipIf(not gamspy_available, - "The 'gams' python bindings are not available") + @unittest.skipIf( + not gamspy_available, "The 'gams' python bindings are not available" + ) def test_long_var_py(self): with SolverFactory("gams", solver_io="python") as opt: m = ConcreteModel() - x = m.a23456789012345678901234567890123456789012345678901234567890123 = Var() - y = m.b234567890123456789012345678901234567890123456789012345678901234 = Var() - z = m.c23456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 = Var() + x = ( + m.a23456789012345678901234567890123456789012345678901234567890123 + ) = Var() + y = ( + m.b234567890123456789012345678901234567890123456789012345678901234 + ) = Var() + z = ( + m.c23456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 + ) = Var() w = m.d01234567890 = Var() - m.c1 = Constraint(expr= x + y + z + w == 0) - m.c2 = Constraint(expr= x >= 10) - m.o = Objective(expr= x) + m.c1 = Constraint(expr=x + y + z + w == 0) + m.c2 = Constraint(expr=x >= 10) + m.o = Objective(expr=x) results = opt.solve(m) - self.assertEqual(results.solver.termination_condition, - TerminationCondition.optimal) + self.assertEqual( + results.solver.termination_condition, TerminationCondition.optimal + ) - @unittest.skipIf(not gamsgms_available, - "The 'gams' executable is not available") + @unittest.skipIf(not gamsgms_available, "The 'gams' executable is not available") def test_long_var_gms(self): with SolverFactory("gams", solver_io="gms") as opt: m = ConcreteModel() - x = m.a23456789012345678901234567890123456789012345678901234567890123 = Var() - y = m.b234567890123456789012345678901234567890123456789012345678901234 = Var() - z = m.c23456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 = Var() + x = ( + m.a23456789012345678901234567890123456789012345678901234567890123 + ) = Var() + y = ( + m.b234567890123456789012345678901234567890123456789012345678901234 + ) = Var() + z = ( + m.c23456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 + ) = Var() w = m.d01234567890 = Var() - m.c1 = Constraint(expr= x + y + z + w == 0) - m.c2 = Constraint(expr= x >= 10) - m.o = Objective(expr= x) + m.c1 = Constraint(expr=x + y + z + w == 0) + m.c2 = Constraint(expr=x >= 10) + m.o = Objective(expr=x) results = opt.solve(m) - self.assertEqual(results.solver.termination_condition, - TerminationCondition.optimal) + self.assertEqual( + results.solver.termination_condition, TerminationCondition.optimal + ) def test_subsolver_notation(self): opt1 = SolverFactory("gams:ipopt", solver_io="gms") @@ -277,68 +281,70 @@ def test_subsolver_notation(self): self.assertTrue(isinstance(opt4, GAMSDirect)) self.assertEqual(opt4.options["solver"], "cbc") - @unittest.skipIf(not gamspy_available, - "The 'gams' python bindings are not available") + @unittest.skipIf( + not gamspy_available, "The 'gams' python bindings are not available" + ) def test_options_py(self): with SolverFactory("gams", solver_io="python") as opt: m = ConcreteModel() m.x = Var() - m.c = Constraint(expr= m.x >= 10) - m.o = Objective(expr= m.x) + m.c = Constraint(expr=m.x >= 10) + m.o = Objective(expr=m.x) - opt.options["load_solutions"] = False # set option - opt.solve(m) # use option + opt.options["load_solutions"] = False # set option + opt.solve(m) # use option self.assertEqual(m.x.value, None) - opt.solve(m, load_solutions=True) # overwrite option + opt.solve(m, load_solutions=True) # overwrite option self.assertEqual(m.x.value, 10) - @unittest.skipIf(not gamsgms_available, - "The 'gams' executable is not available") + @unittest.skipIf(not gamsgms_available, "The 'gams' executable is not available") def test_options_gms(self): with SolverFactory("gams", solver_io="gms") as opt: m = ConcreteModel() m.x = Var() - m.c = Constraint(expr= m.x >= 10) - m.o = Objective(expr= m.x) + m.c = Constraint(expr=m.x >= 10) + m.o = Objective(expr=m.x) - opt.options["load_solutions"] = False # set option - opt.solve(m) # use option + opt.options["load_solutions"] = False # set option + opt.solve(m) # use option self.assertEqual(m.x.value, None) - opt.solve(m, load_solutions=True) # overwrite option + opt.solve(m, load_solutions=True) # overwrite option self.assertEqual(m.x.value, 10) - @unittest.skipIf(not gamspy_available, - "The 'gams' python bindings are not available") + @unittest.skipIf( + not gamspy_available, "The 'gams' python bindings are not available" + ) def test_version_py(self): with SolverFactory("gams", solver_io="python") as opt: self.assertIsNotNone(opt.version()) - @unittest.skipIf(not gamsgms_available, - "The 'gams' executable is not available") + @unittest.skipIf(not gamsgms_available, "The 'gams' executable is not available") def test_version_gms(self): with SolverFactory("gams", solver_io="gms") as opt: self.assertIsNotNone(opt.version()) - @unittest.skipIf(not gamsgms_available, - "The 'gams' executable is not available") + @unittest.skipIf(not gamsgms_available, "The 'gams' executable is not available") def test_dat_parser(self): # This tests issue 2571 m = pyo.ConcreteModel() m.S = pyo.Set(initialize=list(range(5))) m.a_long_var_name = pyo.Var(m.S, bounds=(0, 1), initialize=1) m.obj = pyo.Objective( - expr=2000 * pyo.summation(m.a_long_var_name), sense=pyo.maximize) + expr=2000 * pyo.summation(m.a_long_var_name), sense=pyo.maximize + ) solver = pyo.SolverFactory("gams:conopt") res = solver.solve( - m, symbolic_solver_labels=True, load_solutions=False, - io_options={'put_results_format': 'dat'}) + m, + symbolic_solver_labels=True, + load_solutions=False, + io_options={'put_results_format': 'dat'}, + ) self.assertEqual(res.solution[0].Objective['obj']['Value'], 10000) for i in range(5): self.assertEqual( - res.solution[0].Variable[f'a_long_var_name_{i}_']['Value'], - 1 + res.solution[0].Variable[f'a_long_var_name_{i}_']['Value'], 1 ) @@ -347,8 +353,8 @@ def setUp(self): """Set up model and temporary directory.""" m = ConcreteModel() m.x = Var() - m.c = Constraint(expr= m.x >= 10) - m.o = Objective(expr= m.x) + m.c = Constraint(expr=m.x >= 10) + m.o = Objective(expr=m.x) self.m = m self.tmpdir = mkdtemp() self.logfile = os.path.join(self.tmpdir, 'logfile.log') @@ -431,8 +437,7 @@ def test_logfile_relative(self): opt.solve(self.m, logfile=self.logfile) self._check_stdout(output.getvalue(), exists=False) self._check_logfile(exists=True) - self.assertTrue( - os.path.exists(os.path.join(tmpdir, self.logfile))) + self.assertTrue(os.path.exists(os.path.join(tmpdir, self.logfile))) finally: os.chdir(cwd) @@ -486,10 +491,10 @@ def test_logfile_relative(self): opt.solve(self.m, logfile=self.logfile) self._check_stdout(output.getvalue(), exists=False) self._check_logfile(exists=True) - self.assertTrue( - os.path.exists(os.path.join(tmpdir, self.logfile))) + self.assertTrue(os.path.exists(os.path.join(tmpdir, self.logfile))) finally: os.chdir(cwd) + def test_tee_and_logfile(self): with SolverFactory("gams", solver_io="python") as opt: with capture_output() as output: @@ -498,6 +503,5 @@ def test_tee_and_logfile(self): self._check_logfile(exists=True) - if __name__ == "__main__": unittest.main() diff --git a/pyomo/solvers/tests/checks/test_MOSEKDirect.py b/pyomo/solvers/tests/checks/test_MOSEKDirect.py index 6ae53f99adf..3dd50f05965 100644 --- a/pyomo/solvers/tests/checks/test_MOSEKDirect.py +++ b/pyomo/solvers/tests/checks/test_MOSEKDirect.py @@ -11,9 +11,7 @@ import pyomo.common.unittest as unittest -from pyomo.opt import ( - TerminationCondition, SolutionStatus, check_available_solvers, -) +from pyomo.opt import TerminationCondition, SolutionStatus, check_available_solvers import pyomo.environ as pyo import pyomo.kernel as pmo import sys @@ -23,10 +21,8 @@ mosek_available = check_available_solvers('mosek_direct') -@unittest.skipIf(not mosek_available, - "MOSEK's python bindings are not available") +@unittest.skipIf(not mosek_available, "MOSEK's python bindings are not available") class MOSEKDirectTests(unittest.TestCase): - def setUp(self): self.stderr = sys.stderr sys.stderr = None @@ -55,9 +51,13 @@ def test_infeasible_lp(self): opt = pyo.SolverFactory("mosek_direct") results = opt.solve(model) - self.assertIn(results.solver.termination_condition, - (TerminationCondition.infeasible, - TerminationCondition.infeasibleOrUnbounded)) + self.assertIn( + results.solver.termination_condition, + ( + TerminationCondition.infeasible, + TerminationCondition.infeasibleOrUnbounded, + ), + ) def test_unbounded_lp(self): @@ -68,9 +68,13 @@ def test_unbounded_lp(self): opt = pyo.SolverFactory("mosek_direct") results = opt.solve(model) - self.assertIn(results.solver.termination_condition, - (TerminationCondition.unbounded, - TerminationCondition.infeasibleOrUnbounded)) + self.assertIn( + results.solver.termination_condition, + ( + TerminationCondition.unbounded, + TerminationCondition.infeasibleOrUnbounded, + ), + ) def test_optimal_lp(self): @@ -81,8 +85,7 @@ def test_optimal_lp(self): opt = pyo.SolverFactory("mosek_direct") results = opt.solve(model, load_solutions=False) - self.assertEqual(results.solution.status, - SolutionStatus.optimal) + self.assertEqual(results.solution.status, SolutionStatus.optimal) def test_get_duals_lp(self): @@ -90,8 +93,8 @@ def test_get_duals_lp(self): model.X = pyo.Var(within=pyo.NonNegativeReals) model.Y = pyo.Var(within=pyo.NonNegativeReals) - model.C1 = pyo.Constraint(expr=2*model.X + model.Y >= 8) - model.C2 = pyo.Constraint(expr=model.X + 3*model.Y >= 6) + model.C1 = pyo.Constraint(expr=2 * model.X + model.Y >= 8) + model.C2 = pyo.Constraint(expr=model.X + 3 * model.Y >= 6) model.O = pyo.Objective(expr=model.X + model.Y) @@ -115,9 +118,13 @@ def test_infeasible_mip(self): opt = pyo.SolverFactory("mosek_direct") results = opt.solve(model) - self.assertIn(results.solver.termination_condition, - (TerminationCondition.infeasibleOrUnbounded, - TerminationCondition.infeasible)) + self.assertIn( + results.solver.termination_condition, + ( + TerminationCondition.infeasibleOrUnbounded, + TerminationCondition.infeasible, + ), + ) def test_unbounded_mip(self): @@ -129,9 +136,13 @@ def test_unbounded_mip(self): opt = pyo.SolverFactory("mosek_direct") results = opt.solve(instance) - self.assertIn(results.solver.termination_condition, - (TerminationCondition.unbounded, - TerminationCondition.infeasibleOrUnbounded)) + self.assertIn( + results.solver.termination_condition, + ( + TerminationCondition.unbounded, + TerminationCondition.infeasibleOrUnbounded, + ), + ) def test_optimal_mip(self): @@ -142,8 +153,7 @@ def test_optimal_mip(self): opt = pyo.SolverFactory("mosek_direct") results = opt.solve(model, load_solutions=False) - self.assertEqual(results.solution.status, - SolutionStatus.optimal) + self.assertEqual(results.solution.status, SolutionStatus.optimal) def test_qcqo(self): model = pmo.block() @@ -151,11 +161,25 @@ def test_qcqo(self): for i in range(3): model.x.append(pmo.variable(lb=0.0)) - model.cons = pmo.constraint(expr=model.x[0] + model.x[1] + model.x[2] - model.x[0] - ** 2 - model.x[1]**2 - 0.1*model.x[2]**2 + 0.2*model.x[0]*model.x[2] >= 1.0) - - model.o = pmo.objective(expr=model.x[0]**2 + 0.1*model.x[1]**2 + - model.x[2]**2 - model.x[0]*model.x[2] - model.x[1], sense=pmo.minimize) + model.cons = pmo.constraint( + expr=model.x[0] + + model.x[1] + + model.x[2] + - model.x[0] ** 2 + - model.x[1] ** 2 + - 0.1 * model.x[2] ** 2 + + 0.2 * model.x[0] * model.x[2] + >= 1.0 + ) + + model.o = pmo.objective( + expr=model.x[0] ** 2 + + 0.1 * model.x[1] ** 2 + + model.x[2] ** 2 + - model.x[0] * model.x[2] + - model.x[1], + sense=pmo.minimize, + ) opt = pmo.SolverFactory("mosek_direct") results = opt.solve(model) @@ -169,53 +193,42 @@ def test_conic(self): model = pmo.block() model.o = pmo.objective(0.0) - model.c = pmo.constraint(body=0.0, - rhs=1) + model.c = pmo.constraint(body=0.0, rhs=1) b = model.quadratic = pmo.block() - b.x = pmo.variable_tuple((pmo.variable(), - pmo.variable())) + b.x = pmo.variable_tuple((pmo.variable(), pmo.variable())) b.r = pmo.variable(lb=0) - b.c = pmo.conic.quadratic(x=b.x, - r=b.r) + b.c = pmo.conic.quadratic(x=b.x, r=b.r) model.o.expr += b.r model.c.body += b.r del b b = model.rotated_quadratic = pmo.block() - b.x = pmo.variable_tuple((pmo.variable(), - pmo.variable())) + b.x = pmo.variable_tuple((pmo.variable(), pmo.variable())) b.r1 = pmo.variable(lb=0) b.r2 = pmo.variable(lb=0) - b.c = pmo.conic.rotated_quadratic(x=b.x, - r1=b.r1, - r2=b.r2) + b.c = pmo.conic.rotated_quadratic(x=b.x, r1=b.r1, r2=b.r2) model.o.expr += b.r1 + b.r2 model.c.body += b.r1 + b.r2 del b import mosek + if mosek.Env().getversion() >= (9, 0, 0): b = model.primal_exponential = pmo.block() b.x1 = pmo.variable(lb=0) b.x2 = pmo.variable() b.r = pmo.variable(lb=0) - b.c = pmo.conic.primal_exponential(x1=b.x1, - x2=b.x2, - r=b.r) + b.c = pmo.conic.primal_exponential(x1=b.x1, x2=b.x2, r=b.r) model.o.expr += b.r model.c.body += b.r del b b = model.primal_power = pmo.block() - b.x = pmo.variable_tuple((pmo.variable(), - pmo.variable())) + b.x = pmo.variable_tuple((pmo.variable(), pmo.variable())) b.r1 = pmo.variable(lb=0) b.r2 = pmo.variable(lb=0) - b.c = pmo.conic.primal_power(x=b.x, - r1=b.r1, - r2=b.r2, - alpha=0.6) + b.c = pmo.conic.primal_power(x=b.x, r1=b.r1, r2=b.r2, alpha=0.6) model.o.expr += b.r1 + b.r2 model.c.body += b.r1 + b.r2 del b @@ -224,22 +237,16 @@ def test_conic(self): b.x1 = pmo.variable() b.x2 = pmo.variable(ub=0) b.r = pmo.variable(lb=0) - b.c = pmo.conic.dual_exponential(x1=b.x1, - x2=b.x2, - r=b.r) + b.c = pmo.conic.dual_exponential(x1=b.x1, x2=b.x2, r=b.r) model.o.expr += b.r model.c.body += b.r del b b = model.dual_power = pmo.block() - b.x = pmo.variable_tuple((pmo.variable(), - pmo.variable())) + b.x = pmo.variable_tuple((pmo.variable(), pmo.variable())) b.r1 = pmo.variable(lb=0) b.r2 = pmo.variable(lb=0) - b.c = pmo.conic.dual_power(x=b.x, - r1=b.r1, - r2=b.r2, - alpha=0.4) + b.c = pmo.conic.dual_power(x=b.x, r1=b.r1, r2=b.r2, alpha=0.4) model.o.expr += b.r1 + b.r2 model.c.body += b.r1 + b.r2 @@ -261,27 +268,30 @@ def test_conic(self): del b b = model.svec_psdcone = pmo.block() - b.x = pmo.variable_tuple(( - pmo.variable(), pmo.variable(), pmo.variable())) + b.x = pmo.variable_tuple((pmo.variable(), pmo.variable(), pmo.variable())) b.c = pmo.conic.svec_psdcone(x=b.x) - model.o.expr += b.x[0] + 2*b.x[1] + b.x[2] - model.c.body += b.x[0] + 2*b.x[1] + b.x[2] + model.o.expr += b.x[0] + 2 * b.x[1] + b.x[2] + model.c.body += b.x[0] + 2 * b.x[1] + b.x[2] del b opt = pmo.SolverFactory("mosek_direct") results = opt.solve(model) - self.assertEqual(results.solution.status, - SolutionStatus.optimal) + self.assertEqual(results.solution.status, SolutionStatus.optimal) def _test_model(self): model = pmo.block() model.x0, model.x1, model.x2 = [pmo.variable() for i in range(3)] - model.obj = pmo.objective(2*model.x0 + 3*model.x1 - model.x2, sense=-1) + model.obj = pmo.objective(2 * model.x0 + 3 * model.x1 - model.x2, sense=-1) model.con1 = pmo.constraint(model.x0 + model.x1 + model.x2 == 1) - model.quad = pmo.conic.quadratic.as_domain(r=0.03, x=[pmo.expression( - 1.5*model.x0 + 0.1*model.x1), pmo.expression(0.3*model.x0 + 2.1*model.x2 + 0.1)]) + model.quad = pmo.conic.quadratic.as_domain( + r=0.03, + x=[ + pmo.expression(1.5 * model.x0 + 0.1 * model.x1), + pmo.expression(0.3 * model.x0 + 2.1 * model.x2 + 0.1), + ], + ) return model def test_conic_duals(self): @@ -293,8 +303,7 @@ def test_conic_duals(self): model.dual = pmo.suffix(direction=pmo.suffix.IMPORT) solver.load_duals() for i in range(3): - self.assertAlmostEqual( - model.dual[model.quad.q][i], check[i], 5) + self.assertAlmostEqual(model.dual[model.quad.q][i], check[i], 5) # load_duals (with args) with pmo.SolverFactory('mosek_direct') as solver: model = self._test_model() @@ -302,8 +311,7 @@ def test_conic_duals(self): model.dual = pmo.suffix(direction=pmo.suffix.IMPORT) solver.load_duals([model.quad.q]) for i in range(3): - self.assertAlmostEqual( - model.dual[model.quad.q][i], check[i], 5) + self.assertAlmostEqual(model.dual[model.quad.q][i], check[i], 5) # save_results=True (deprecated) with pmo.SolverFactory('mosek_direct') as solver: model = self._test_model() @@ -311,7 +319,8 @@ def test_conic_duals(self): results = solver.solve(model, save_results=True) for i in range(3): self.assertAlmostEqual( - results.Solution.constraint['x11']['Dual'][i], check[i], 5) + results.Solution.constraint['x11']['Dual'][i], check[i], 5 + ) if __name__ == "__main__": diff --git a/pyomo/solvers/tests/checks/test_MOSEKPersistent.py b/pyomo/solvers/tests/checks/test_MOSEKPersistent.py index c1c17b3b1d8..c96e84ad874 100644 --- a/pyomo/solvers/tests/checks/test_MOSEKPersistent.py +++ b/pyomo/solvers/tests/checks/test_MOSEKPersistent.py @@ -1,7 +1,9 @@ import pyomo.common.unittest as unittest from pyomo.opt import ( - TerminationCondition, SolutionStatus, SolverStatus, + TerminationCondition, + SolutionStatus, + SolverStatus, check_available_solvers, ) import pyomo.environ as pyo @@ -18,7 +20,6 @@ @unittest.skipIf(not mosek_available, "MOSEK's python bindings are missing.") class MOSEKPersistentTests(unittest.TestCase): - def setUp(self): self.stderr = sys.stderr sys.stderr = None @@ -59,7 +60,7 @@ def test_constraint_removal_1(self): m.x = pyo.Var() m.y = pyo.Var() m.z = pyo.Var() - m.c1 = pyo.Constraint(expr=2*m.x >= m.y**2) + m.c1 = pyo.Constraint(expr=2 * m.x >= m.y**2) m.c2 = pyo.Constraint(expr=m.x**2 >= m.y**2 + m.z**2) m.c3 = pyo.Constraint(expr=m.z >= 0) m.c4 = pyo.Constraint(expr=m.x + m.y >= 0) @@ -78,7 +79,9 @@ def test_constraint_removal_1(self): self.assertEqual(opt._solver_model.getnumcon(), 2) self.assertRaises(ValueError, opt.remove_constraint, m.c2) - @unittest.skipIf(msk_version[0] > 9, "MOSEK 10 does not (yet) have a removeacc method.") + @unittest.skipIf( + msk_version[0] > 9, "MOSEK 10 does not (yet) have a removeacc method." + ) def test_constraint_removal_2(self): m = pmo.block() m.x = pmo.variable() @@ -117,10 +120,10 @@ def test_column_addition(self): m.x = pyo.Var(bounds=(0, None)) m.y = pyo.Var(bounds=(0, 10)) m.z = pyo.Var(bounds=(0, None)) - m.c1 = pyo.Constraint(expr=3*m.x + m.y + 2*m.z == 30) - m.c2 = pyo.Constraint(expr=2*m.x + m.y + 3*m.z >= 15) - m.c3 = pyo.Constraint(expr=2*m.y <= 25) - m.o = pyo.Objective(expr=3*m.x + m.y + 5*m.z, sense=pyo.maximize) + m.c1 = pyo.Constraint(expr=3 * m.x + m.y + 2 * m.z == 30) + m.c2 = pyo.Constraint(expr=2 * m.x + m.y + 3 * m.z >= 15) + m.c3 = pyo.Constraint(expr=2 * m.y <= 25) + m.o = pyo.Objective(expr=3 * m.x + m.y + 5 * m.z, sense=pyo.maximize) opt = pyo.SolverFactory('mosek_persistent') opt.set_instance(m) @@ -144,9 +147,9 @@ def test_variable_update(self): m = pyo.ConcreteModel() m.x = pyo.Var() m.y = pyo.Var() - m.c1 = pyo.Constraint(expr=50*m.x + 31*m.y <= 250) - m.c2 = pyo.Constraint(expr=3*m.x - 2*m.y >= -4) - m.o = pyo.Objective(expr=m.x + 0.64*m.y, sense=pyo.maximize) + m.c1 = pyo.Constraint(expr=50 * m.x + 31 * m.y <= 250) + m.c2 = pyo.Constraint(expr=3 * m.x - 2 * m.y >= -4) + m.o = pyo.Objective(expr=m.x + 0.64 * m.y, sense=pyo.maximize) opt = pyo.SolverFactory('mosek_persistent') opt.set_instance(m) opt.solve(m) diff --git a/pyomo/solvers/tests/checks/test_cbc.py b/pyomo/solvers/tests/checks/test_cbc.py index a774aa30910..ead0804f812 100644 --- a/pyomo/solvers/tests/checks/test_cbc.py +++ b/pyomo/solvers/tests/checks/test_cbc.py @@ -11,8 +11,15 @@ import os from pyomo.environ import ( - SolverFactory, ConcreteModel, Var, Constraint, Objective, - Integers, Boolean, Suffix, maximize, + SolverFactory, + ConcreteModel, + Var, + Constraint, + Objective, + Integers, + Boolean, + Suffix, + maximize, ) from pyomo.common.tee import capture_output from pyomo.common.tempfiles import TempfileManager @@ -23,9 +30,7 @@ class CBCTests(unittest.TestCase): - - @unittest.skipIf(not cbc_available, - "The CBC solver is not available") + @unittest.skipIf(not cbc_available, "The CBC solver is not available") def test_warm_start(self): m = ConcreteModel() @@ -39,9 +44,8 @@ def test_warm_start(self): tempdir = os.path.dirname(TempfileManager.create_tempfile()) TempfileManager.pop() - sameDrive = os.path.splitdrive(tempdir)[0] == \ - os.path.splitdrive(os.getcwd())[0] - + sameDrive = os.path.splitdrive(tempdir)[0] == os.path.splitdrive(os.getcwd())[0] + # At the moment, CBC does not cleanly handle windows-style drive # names in the MIPSTART file name (though at least 2.10.5). # @@ -58,8 +62,9 @@ def test_warm_start(self): m.w.set_value(1) with SolverFactory("cbc") as opt, capture_output() as output: - opt.solve(m, tee=True, warmstart=True, options={ - 'sloglevel': 2, 'loglevel': 2}) + opt.solve( + m, tee=True, warmstart=True, options={'sloglevel': 2, 'loglevel': 2} + ) log = output.getvalue() # Check if CBC loaded the warmstart file. @@ -73,7 +78,6 @@ def test_warm_start(self): else: self.assertNotIn('MIPStart values read', log) - # Set some initial values for warm start. m.x.set_value(10) m.z.set_value(5) @@ -83,8 +87,9 @@ def test_warm_start(self): _origDir = os.getcwd() os.chdir(tempdir) with SolverFactory("cbc") as opt, capture_output() as output: - opt.solve(m, tee=True, warmstart=True, options={ - 'sloglevel': 2, 'loglevel': 2}) + opt.solve( + m, tee=True, warmstart=True, options={'sloglevel': 2, 'loglevel': 2} + ) finally: os.chdir(_origDir) @@ -96,9 +101,7 @@ def test_warm_start(self): # m.x is ignored because it is continuous, so cost should be 5+1 self.assertIn('MIPStart provided solution with cost 6', log) - - @unittest.skipIf(not cbc_available, - "The CBC solver is not available") + @unittest.skipIf(not cbc_available, "The CBC solver is not available") def test_duals_signs(self): m = ConcreteModel() m.x = Var() @@ -116,8 +119,7 @@ def test_duals_signs(self): self.assertAlmostEqual(res.problem.upper_bound, 1) self.assertAlmostEqual(m.dual[m.c], 1) - @unittest.skipIf(not cbc_available, - "The CBC solver is not available") + @unittest.skipIf(not cbc_available, "The CBC solver is not available") def test_rc_signs(self): m = ConcreteModel() m.x = Var(bounds=(-1, 1)) diff --git a/pyomo/solvers/tests/checks/test_cplex.py b/pyomo/solvers/tests/checks/test_cplex.py index 9e2269d5aad..65f4bda3524 100644 --- a/pyomo/solvers/tests/checks/test_cplex.py +++ b/pyomo/solvers/tests/checks/test_cplex.py @@ -15,18 +15,35 @@ import pyomo.common.unittest as unittest import pyomo.kernel as pmo -from pyomo.core import Binary, ConcreteModel, Constraint, Objective, Var, Integers, RangeSet, minimize, quicksum, Suffix +from pyomo.core import ( + Binary, + ConcreteModel, + Constraint, + Objective, + Var, + Integers, + RangeSet, + minimize, + quicksum, + Suffix, +) from pyomo.opt import ProblemFormat, convert_problem, SolverFactory, BranchDirection -from pyomo.solvers.plugins.solvers.CPLEX import CPLEXSHELL, MockCPLEX, _validate_file_name +from pyomo.solvers.plugins.solvers.CPLEX import ( + CPLEXSHELL, + MockCPLEX, + _validate_file_name, +) class _mock_cplex_128(object): def version(self): - return (12,8,0) + return (12, 8, 0) + class _mock_cplex_126(object): def version(self): - return (12,6,0) + return (12, 6, 0) + class CPLEX_utils(unittest.TestCase): def test_validate_file_name(self): @@ -40,11 +57,9 @@ def test_validate_file_name(self): # Check spaces in the file fname = 'foo bar.lp' - with self.assertRaisesRegex( - ValueError, "Space detected in CPLEX xxx file"): + with self.assertRaisesRegex(ValueError, "Space detected in CPLEX xxx file"): _validate_file_name(_126, fname, 'xxx') - self.assertEqual('"%s"' % (fname,), - _validate_file_name(_128, fname, 'xxx')) + self.assertEqual('"%s"' % (fname,), _validate_file_name(_128, fname, 'xxx')) # check OK path separators fname = 'foo%sbar.lp' % (os.path.sep,) @@ -52,10 +67,11 @@ def test_validate_file_name(self): self.assertEqual(fname, _validate_file_name(_128, fname, 'xxx')) # check BAD path separators - bad_char = '/\\'.replace(os.path.sep,'') + bad_char = '/\\'.replace(os.path.sep, '') fname = 'foo%sbar.lp' % (bad_char,) msg = r'Unallowed character \(%s\) found in CPLEX xxx file' % ( - repr(bad_char)[1:-1],) + repr(bad_char)[1:-1], + ) with self.assertRaisesRegex(ValueError, msg): _validate_file_name(_126, fname, 'xxx') with self.assertRaisesRegex(ValueError, msg): @@ -88,9 +104,9 @@ def test_validate_file_name(self): _validate_file_name(_128, fname, 'xxx') - class CPLEXShellWritePrioritiesFile(unittest.TestCase): - """ Unit test on writing of priorities via `CPLEXSHELL._write_priorities_file()` """ + """Unit test on writing of priorities via `CPLEXSHELL._write_priorities_file()`""" + suffix_cls = Suffix def setUp(self): @@ -113,7 +129,11 @@ def get_mock_model(self): def get_mock_cplex_shell(self, mock_model): solver = MockCPLEX() - solver._problem_files, solver._problem_format, solver._smap_id = convert_problem( + ( + solver._problem_files, + solver._problem_format, + solver._smap_id, + ) = convert_problem( (mock_model,), ProblemFormat.cpxlp, [ProblemFormat.cpxlp], @@ -135,9 +155,13 @@ def test_write_without_priority_suffix(self): CPLEXSHELL._write_priorities_file(self.mock_cplex_shell, self.mock_model) def test_write_priority_to_priorities_file(self): - self.mock_model.priority = self.suffix_cls(direction=Suffix.EXPORT, datatype=Suffix.INT) + self.mock_model.priority = self.suffix_cls( + direction=Suffix.EXPORT, datatype=Suffix.INT + ) priority_val = 10 - self._set_suffix_value(self.mock_model.priority, self.mock_model.x, priority_val) + self._set_suffix_value( + self.mock_model.priority, self.mock_model.x, priority_val + ) CPLEXSHELL._write_priorities_file(self.mock_cplex_shell, self.mock_model) priorities_file = self.get_priorities_file_as_string(self.mock_cplex_shell) @@ -147,17 +171,25 @@ def test_write_priority_to_priorities_file(self): "* ENCODING=ISO-8859-1\n" "NAME Priority Order\n" " x1 10\n" - "ENDATA\n" + "ENDATA\n", ) def test_write_priority_and_direction_to_priorities_file(self): - self.mock_model.priority = self.suffix_cls(direction=Suffix.EXPORT, datatype=Suffix.INT) + self.mock_model.priority = self.suffix_cls( + direction=Suffix.EXPORT, datatype=Suffix.INT + ) priority_val = 10 - self._set_suffix_value(self.mock_model.priority, self.mock_model.x, priority_val) + self._set_suffix_value( + self.mock_model.priority, self.mock_model.x, priority_val + ) - self.mock_model.direction = self.suffix_cls(direction=Suffix.EXPORT, datatype=Suffix.INT) + self.mock_model.direction = self.suffix_cls( + direction=Suffix.EXPORT, datatype=Suffix.INT + ) direction_val = BranchDirection.down - self._set_suffix_value(self.mock_model.direction, self.mock_model.x, direction_val) + self._set_suffix_value( + self.mock_model.direction, self.mock_model.x, direction_val + ) CPLEXSHELL._write_priorities_file(self.mock_cplex_shell, self.mock_model) priorities_file = self.get_priorities_file_as_string(self.mock_cplex_shell) @@ -167,11 +199,13 @@ def test_write_priority_and_direction_to_priorities_file(self): "* ENCODING=ISO-8859-1\n" "NAME Priority Order\n" " DN x1 10\n" - "ENDATA\n" + "ENDATA\n", ) def test_raise_due_to_invalid_priority(self): - self.mock_model.priority = self.suffix_cls(direction=Suffix.EXPORT, datatype=Suffix.INT) + self.mock_model.priority = self.suffix_cls( + direction=Suffix.EXPORT, datatype=Suffix.INT + ) self._set_suffix_value(self.mock_model.priority, self.mock_model.x, -1) with self.assertRaises(ValueError): CPLEXSHELL._write_priorities_file(self.mock_cplex_shell, self.mock_model) @@ -181,11 +215,17 @@ def test_raise_due_to_invalid_priority(self): CPLEXSHELL._write_priorities_file(self.mock_cplex_shell, self.mock_model) def test_use_default_due_to_invalid_direction(self): - self.mock_model.priority = self.suffix_cls(direction=Suffix.EXPORT, datatype=Suffix.INT) + self.mock_model.priority = self.suffix_cls( + direction=Suffix.EXPORT, datatype=Suffix.INT + ) priority_val = 10 - self._set_suffix_value(self.mock_model.priority, self.mock_model.x, priority_val) + self._set_suffix_value( + self.mock_model.priority, self.mock_model.x, priority_val + ) - self.mock_model.direction = self.suffix_cls(direction=Suffix.EXPORT, datatype=Suffix.INT) + self.mock_model.direction = self.suffix_cls( + direction=Suffix.EXPORT, datatype=Suffix.INT + ) self._set_suffix_value( self.mock_model.direction, self.mock_model.x, "invalid_branching_direction" ) @@ -198,7 +238,7 @@ def test_use_default_due_to_invalid_direction(self): "* ENCODING=ISO-8859-1\n" "NAME Priority Order\n" " x1 10\n" - "ENDATA\n" + "ENDATA\n", ) @@ -218,7 +258,8 @@ def get_mock_model(self): class CPLEXShellSolvePrioritiesFile(unittest.TestCase): - """ Integration test on the end-to-end application of priorities via the `Suffix` through a `solve()` """ + """Integration test on the end-to-end application of priorities via the `Suffix` through a `solve()`""" + def get_mock_model_with_priorities(self): m = ConcreteModel() m.x = Var(domain=Integers) @@ -278,7 +319,7 @@ def test_ignore_variable_priorities(self): self.assertNotIn(".ord", opt._command.script) def test_can_use_manual_priorities_file_with_lp_solve(self): - """ Test that we can pass an LP file (not a pyomo model) along with a priorities file to `.solve()` """ + """Test that we can pass an LP file (not a pyomo model) along with a priorities file to `.solve()`""" model = self.get_mock_model_with_priorities() with SolverFactory("_mock_cplex") as pre_opt: diff --git a/pyomo/solvers/tests/checks/test_gurobi.py b/pyomo/solvers/tests/checks/test_gurobi.py index 950eac39856..f33a00ce8a2 100644 --- a/pyomo/solvers/tests/checks/test_gurobi.py +++ b/pyomo/solvers/tests/checks/test_gurobi.py @@ -4,25 +4,25 @@ try: from pyomo.solvers.plugins.solvers.GUROBI_RUN import gurobi_run from gurobipy import GRB + gurobipy_available = True has_worklimit = hasattr(GRB, "WORK_LIMIT") except: gurobipy_available = False has_worklimit = False -@unittest.skipIf(not gurobipy_available, - "gurobipy is not available") -class GurobiTest(unittest.TestCase): - @unittest.skipIf(not has_worklimit, - "gurobi < 9.5") +@unittest.skipIf(not gurobipy_available, "gurobipy is not available") +class GurobiTest(unittest.TestCase): + @unittest.skipIf(not has_worklimit, "gurobi < 9.5") @patch("builtins.open") @patch("pyomo.solvers.plugins.solvers.GUROBI_RUN.read") - def test_work_limit(self, read:MagicMock, open: MagicMock): + def test_work_limit(self, read: MagicMock, open: MagicMock): file = MagicMock() open.return_value = file model = MagicMock() read.return_value = model + def getAttr(attr): if attr == GRB.Attr.Status: return GRB.WORK_LIMIT @@ -36,6 +36,7 @@ def getAttr(attr): return 0 return None + model.getAttr = getAttr gurobi_run(None, None, None, None, {}, []) self.assertTrue("WorkLimit" in file.write.call_args[0][0]) diff --git a/pyomo/solvers/tests/checks/test_gurobi_persistent.py b/pyomo/solvers/tests/checks/test_gurobi_persistent.py index f0485be4ced..3535c331bfc 100644 --- a/pyomo/solvers/tests/checks/test_gurobi_persistent.py +++ b/pyomo/solvers/tests/checks/test_gurobi_persistent.py @@ -12,8 +12,10 @@ import pyomo.common.unittest as unittest import pyomo.environ as pyo from pyomo.core.expr.taylor_series import taylor_series_expansion + try: import gurobipy + m = gurobipy.Model() gurobipy_available = True except: @@ -27,7 +29,7 @@ def test_basics(self): m.x = pyo.Var(bounds=(-10, 10)) m.y = pyo.Var() m.obj = pyo.Objective(expr=m.x**2 + m.y**2) - m.c1 = pyo.Constraint(expr=m.y >= 2*m.x + 1) + m.c1 = pyo.Constraint(expr=m.y >= 2 * m.x + 1) opt = pyo.SolverFactory('gurobi_persistent') opt.set_instance(m) @@ -192,7 +194,7 @@ def test_update4(self): @unittest.skipIf(not gurobipy_available, "gurobipy is not available") def test_update5(self): m = pyo.ConcreteModel() - m.a = pyo.Set(initialize=[1,2,3], ordered=True) + m.a = pyo.Set(initialize=[1, 2, 3], ordered=True) m.x = pyo.Var(m.a, within=pyo.Binary) m.y = pyo.Var(within=pyo.Binary) m.obj = pyo.Objective(expr=m.y) @@ -214,7 +216,7 @@ def test_update5(self): @unittest.skipIf(not gurobipy_available, "gurobipy is not available") def test_update6(self): m = pyo.ConcreteModel() - m.a = pyo.Set(initialize=[1,2,3], ordered=True) + m.a = pyo.Set(initialize=[1, 2, 3], ordered=True) m.x = pyo.Var(m.a, within=pyo.Binary) m.y = pyo.Var(within=pyo.Binary) m.obj = pyo.Objective(expr=m.y) @@ -295,12 +297,12 @@ def test_callback(self): m = pyo.ConcreteModel() m.x = pyo.Var(bounds=(0, 4)) m.y = pyo.Var(within=pyo.Integers, bounds=(0, None)) - m.obj = pyo.Objective(expr=2*m.x + m.y) + m.obj = pyo.Objective(expr=2 * m.x + m.y) m.cons = pyo.ConstraintList() def _add_cut(xval): m.x.value = xval - return m.cons.add(m.y >= taylor_series_expansion((m.x - 2)**2)) + return m.cons.add(m.y >= taylor_series_expansion((m.x - 2) ** 2)) _add_cut(0) _add_cut(4) @@ -313,7 +315,7 @@ def _add_cut(xval): def _my_callback(cb_m, cb_opt, cb_where): if cb_where == gurobipy.GRB.Callback.MIPSOL: cb_opt.cbGetSolution(vars=[m.x, m.y]) - if m.y.value < (m.x.value - 2)**2 - 1e-6: + if m.y.value < (m.x.value - 2) ** 2 - 1e-6: cb_opt.cbLazy(_add_cut(m.x.value)) opt.set_callback(_my_callback) @@ -346,7 +348,7 @@ def test_add_column_exceptions(self): m = pyo.ConcreteModel() m.x = pyo.Var() m.c = pyo.Constraint(expr=(0, m.x, 1)) - m.ci = pyo.Constraint([1,2], rule=lambda m,i:(0,m.x,i+1)) + m.ci = pyo.Constraint([1, 2], rule=lambda m, i: (0, m.x, i + 1)) m.cd = pyo.Constraint(expr=(0, -m.x, 1)) m.cd.deactivate() m.obj = pyo.Objective(expr=-m.x) @@ -360,7 +362,7 @@ def test_add_column_exceptions(self): m2 = pyo.ConcreteModel() m2.y = pyo.Var() - m2.c = pyo.Constraint(expr=(0,m.x,1)) + m2.c = pyo.Constraint(expr=(0, m.x, 1)) # different model than attached to opt self.assertRaises(RuntimeError, opt.add_column, m2, m2.y, 0, [], []) @@ -373,7 +375,7 @@ def test_add_column_exceptions(self): m.y = pyo.Var() # len(coefficents) == len(constraints) - self.assertRaises(RuntimeError, opt.add_column, m, m.y, -2, [m.c], [1,2]) + self.assertRaises(RuntimeError, opt.add_column, m, m.y, -2, [m.c], [1, 2]) self.assertRaises(RuntimeError, opt.add_column, m, m.y, -2, [m.c, z], [1]) # add indexed constraint diff --git a/pyomo/solvers/tests/checks/test_no_solution_behavior.py b/pyomo/solvers/tests/checks/test_no_solution_behavior.py index f96c8efb340..a7999159285 100644 --- a/pyomo/solvers/tests/checks/test_no_solution_behavior.py +++ b/pyomo/solvers/tests/checks/test_no_solution_behavior.py @@ -11,11 +11,13 @@ import os import types + try: import new - new_available=True + + new_available = True except: - new_available=False + new_available = False import pyomo.common.unittest as unittest @@ -26,7 +28,7 @@ from io import StringIO # The test directory -thisDir = os.path.dirname(os.path.abspath( __file__ )) +thisDir = os.path.dirname(os.path.abspath(__file__)) # Cleanup Expected Failure Results Files _cleanup_expected_failures = True @@ -36,10 +38,7 @@ # A function that returns a function that gets # added to a test class. # -def create_method(model, - solver, - io, - test_case): +def create_method(model, solver, io, test_case): is_expected_failure = test_case.status == 'expected failure' @@ -66,11 +65,11 @@ def failed_solve_test(self): test_case.testcase.io_options, test_case.testcase.options, symbolic_labels, - load_solutions) + load_solutions, + ) model_class.post_solve_test_validation(self, results) if len(results.solution) == 0: - self.assertIn("No solution is available", - out.getvalue()) + self.assertIn("No solution is available", out.getvalue()) else: # Note ASL solvers might still return a solution # file with garbage values in it for a failed solve @@ -78,19 +77,25 @@ def failed_solve_test(self): # Skip this test if the status is 'skip' if test_case.status == 'skip': + def return_test(self): return self.skipTest(test_case.msg) + elif is_expected_failure: + @unittest.expectedFailure def return_test(self): return failed_solve_test(self) + else: # Return a normal test def return_test(self): return failed_solve_test(self) + unittest.pytest.mark.solver(solver)(return_test) return return_test + cls = None # @@ -123,7 +128,7 @@ def return_test(self): # a change in load_solutions behavior is # propagated into that framework. if "_kernel" in cls.__name__: - test_name = "test_"+solver+"_"+io + test_name = "test_" + solver + "_" + io test_method = create_method(model, solver, io, value) if test_method is not None: setattr(cls, test_name, test_method) diff --git a/pyomo/solvers/tests/checks/test_pickle.py b/pyomo/solvers/tests/checks/test_pickle.py index b5f50ba68a3..2b872625a53 100644 --- a/pyomo/solvers/tests/checks/test_pickle.py +++ b/pyomo/solvers/tests/checks/test_pickle.py @@ -11,11 +11,13 @@ import pickle import types + try: import new - new_available=True + + new_available = True except: - new_available=False + new_available = False import pyomo.common.unittest as unittest from pyomo.solvers.tests.models.base import all_models @@ -25,9 +27,7 @@ # A function that returns a function that gets # added to a test class. # -def create_method(model, solver, io, - test_case, - symbolic_labels): +def create_method(model, solver, io, test_case, symbolic_labels): # Ignore expected failures? is_expected_failure = False @@ -41,16 +41,19 @@ def pickle_test(self): model_class.generate_model(test_case.testcase.import_suffixes) model_class.warmstart_model() - load_solutions = (not model_class.solve_should_fail) and \ - (test_case.status != 'expected failure') + load_solutions = (not model_class.solve_should_fail) and ( + test_case.status != 'expected failure' + ) try: - opt, status = model_class.solve(solver, - io, - test_case.testcase.io_options, - test_case.testcase.options, - symbolic_labels, - load_solutions) + opt, status = model_class.solve( + solver, + io, + test_case.testcase.io_options, + test_case.testcase.options, + symbolic_labels, + load_solutions, + ) except: if test_case.status == 'expected failure': return @@ -62,69 +65,86 @@ def pickle_test(self): # instance1 = m.clone() model_class.model = instance1 - opt, status1 = model_class.solve(solver, - io, - test_case.testcase.io_options, - test_case.testcase.options, - symbolic_labels, - load_solutions) - inst, res = pickle.loads(pickle.dumps([instance1,status1])) + opt, status1 = model_class.solve( + solver, + io, + test_case.testcase.io_options, + test_case.testcase.options, + symbolic_labels, + load_solutions, + ) + inst, res = pickle.loads(pickle.dumps([instance1, status1])) # # operate on an unpickled model # # try to pickle then unpickle instance instance2 = pickle.loads(pickle.dumps(instance1)) - self.assertNotEqual(id(instance1),id(instance2)) + self.assertNotEqual(id(instance1), id(instance2)) model_class.model = instance2 - opt, status2 = model_class.solve(solver, - io, - test_case.testcase.io_options, - test_case.testcase.options, - symbolic_labels, - load_solutions) + opt, status2 = model_class.solve( + solver, + io, + test_case.testcase.io_options, + test_case.testcase.options, + symbolic_labels, + load_solutions, + ) # try to pickle the instance and status, # then unpickle and load status - inst, res = pickle.loads(pickle.dumps([instance2,status2])) + inst, res = pickle.loads(pickle.dumps([instance2, status2])) # # operate on a clone of an unpickled model # instance3 = instance2.clone() - self.assertNotEqual(id(instance2),id(instance3)) + self.assertNotEqual(id(instance2), id(instance3)) model_class.model = instance3 - opt, status3 = model_class.solve(solver, - io, - test_case.testcase.io_options, - test_case.testcase.options, - symbolic_labels, - load_solutions) + opt, status3 = model_class.solve( + solver, + io, + test_case.testcase.io_options, + test_case.testcase.options, + symbolic_labels, + load_solutions, + ) # try to pickle the instance and status, # then unpickle and load status - inst, res = pickle.loads(pickle.dumps([instance3,status3])) + inst, res = pickle.loads(pickle.dumps([instance3, status3])) # Skip this test if the status is 'skip' if test_case.status == 'skip': + def return_test(self): return self.skipTest(test_case.msg) + elif is_expected_failure: + @unittest.expectedFailure def return_test(self): return pickle_test(self) + else: # If this solver is in demo mode size = getattr(test_case.model, 'size', (None, None, None)) for prb, sol in zip(size, test_case.demo_limits): if prb and sol and prb > sol: + def return_test(self): - return self.skipTest("Problem is too large for unlicensed %s solver" % solver) + return self.skipTest( + "Problem is too large for unlicensed %s solver" % solver + ) + break else: + def return_test(self): return pickle_test(self) + unittest.pytest.mark.solver(solver)(return_test) return return_test + cls = None # @@ -152,14 +172,14 @@ def return_test(self): cls = driver[model] # Symbolic labels - test_name = "test_"+solver+"_"+io +"_symbolic_labels" + test_name = "test_" + solver + "_" + io + "_symbolic_labels" test_method = create_method(model, solver, io, value, True) if test_method is not None: setattr(cls, test_name, test_method) test_method = None # Non-symbolic labels - test_name = "test_"+solver+"_"+io +"_nonsymbolic_labels" + test_name = "test_" + solver + "_" + io + "_nonsymbolic_labels" test_method = create_method(model, solver, io, value, False) if test_method is not None: setattr(cls, test_name, test_method) diff --git a/pyomo/solvers/tests/checks/test_writers.py b/pyomo/solvers/tests/checks/test_writers.py index 249986da1ac..2624e489841 100644 --- a/pyomo/solvers/tests/checks/test_writers.py +++ b/pyomo/solvers/tests/checks/test_writers.py @@ -12,11 +12,13 @@ import os from os.path import join, dirname, abspath import types + try: import new - new_available=True + + new_available = True except: - new_available=False + new_available = False import pyomo.common.unittest as unittest from pyomo.opt import TerminationCondition @@ -25,7 +27,7 @@ from pyomo.core.kernel.block import IBlock # The test directory -thisDir = dirname(abspath( __file__ )) +thisDir = dirname(abspath(__file__)) # Cleanup Expected Failure Results Files _cleanup_expected_failures = True @@ -34,11 +36,7 @@ # A function that returns a function that gets # added to a test class. # -def create_method(test_name, model, - solver, - io, - test_case, - symbolic_labels): +def create_method(test_name, model, solver, io, test_case, symbolic_labels): is_expected_failure = test_case.status == 'expected failure' @@ -67,23 +65,32 @@ def writer_test(self): test_case.testcase.io_options, test_case.testcase.options, symbolic_labels, - load_solutions) + load_solutions, + ) termination_condition = results['Solver'][0]['termination condition'] model_class.post_solve_test_validation(self, results) - if termination_condition == TerminationCondition.unbounded or \ - termination_condition == TerminationCondition.infeasible or \ - termination_condition == TerminationCondition.infeasibleOrUnbounded: + if ( + termination_condition == TerminationCondition.unbounded + or termination_condition == TerminationCondition.infeasible + or termination_condition == TerminationCondition.infeasibleOrUnbounded + ): return # validate the solution returned by the solver if isinstance(model_class.model, IBlock): model_class.model.load_solution(results.Solution) else: - model_class.model.solutions.load_from(results, default_variable_value=opt.default_variable_value()) - model_class.save_current_solution(save_filename, suffixes=model_class.test_suffixes) - rc = model_class.validate_current_solution(suffixes=model_class.test_suffixes, - exclude_suffixes=test_case.exclude_suffixes) + model_class.model.solutions.load_from( + results, default_variable_value=opt.default_variable_value() + ) + model_class.save_current_solution( + save_filename, suffixes=model_class.test_suffixes + ) + rc = model_class.validate_current_solution( + suffixes=model_class.test_suffixes, + exclude_suffixes=test_case.exclude_suffixes, + ) if is_expected_failure: if rc[0]: @@ -106,11 +113,18 @@ def writer_test(self): model_class.model.solutions.store_to(results) except ValueError: pass - self.fail("Solution mismatch for plugin "+test_name - +', '+io+ - " interface and problem type " - +model_class.description+"\n"+rc[1]+"\n" - +(str(results.Solution(0)) if len(results.solution) else "No Solution")) + self.fail( + "Solution mismatch for plugin " + + test_name + + ', ' + + io + + " interface and problem type " + + model_class.description + + "\n" + + rc[1] + + "\n" + + (str(results.Solution(0)) if len(results.solution) else "No Solution") + ) # cleanup if the test passed try: @@ -120,26 +134,37 @@ def writer_test(self): # Skip this test if the status is 'skip' if test_case.status == 'skip': + def return_test(self): return self.skipTest(test_case.msg) + elif is_expected_failure: + @unittest.expectedFailure def return_test(self): return writer_test(self) + else: # Skip if solver is in demo mode size = getattr(test_case.model, 'size', (None, None, None)) for prb, sol in zip(size, test_case.demo_limits): if (prb and sol) and prb > sol: + def return_test(self): - return self.skipTest("Problem is too large for unlicensed %s solver" % solver) + return self.skipTest( + "Problem is too large for unlicensed %s solver" % solver + ) + break else: + def return_test(self): return writer_test(self) + unittest.pytest.mark.solver(solver)(return_test) return return_test + cls = None # @@ -168,14 +193,14 @@ def return_test(self): cls = driver[model] # Symbolic labels - test_name = "test_"+solver+"_"+io +"_symbolic_labels" + test_name = "test_" + solver + "_" + io + "_symbolic_labels" test_method = create_method(test_name, model, solver, io, value, True) if test_method is not None: setattr(cls, test_name, test_method) test_method = None # Non-symbolic labels - test_name = "test_"+solver+"_"+io +"_nonsymbolic_labels" + test_name = "test_" + solver + "_" + io + "_nonsymbolic_labels" test_method = create_method(test_name, model, solver, io, value, False) if test_method is not None: setattr(cls, test_name, test_method) diff --git a/pyomo/solvers/tests/checks/test_xpress_persistent.py b/pyomo/solvers/tests/checks/test_xpress_persistent.py index 0acb9c09777..9c3d4d48783 100644 --- a/pyomo/solvers/tests/checks/test_xpress_persistent.py +++ b/pyomo/solvers/tests/checks/test_xpress_persistent.py @@ -4,6 +4,7 @@ from pyomo.solvers.plugins.solvers.xpress_direct import xpress_available from pyomo.opt.results.solver import TerminationCondition, SolverStatus + class TestXpressPersistent(unittest.TestCase): @unittest.skipIf(not xpress_available, "xpress is not available") def test_basics(self): @@ -11,7 +12,7 @@ def test_basics(self): m.x = pe.Var(bounds=(-10, 10)) m.y = pe.Var() m.obj = pe.Objective(expr=m.x**2 + m.y**2) - m.c1 = pe.Constraint(expr=m.y >= 2*m.x + 1) + m.c1 = pe.Constraint(expr=m.y >= 2 * m.x + 1) opt = pe.SolverFactory('xpress_persistent') opt.set_instance(m) @@ -138,7 +139,7 @@ def test_add_remove_lconstraint(self): @unittest.skipIf(not xpress_available, "xpress is not available") def test_add_remove_sosconstraint(self): m = pe.ConcreteModel() - m.a = pe.Set(initialize=[1,2,3], ordered=True) + m.a = pe.Set(initialize=[1, 2, 3], ordered=True) m.x = pe.Var(m.a, within=pe.Binary) m.y = pe.Var(within=pe.Binary) m.obj = pe.Objective(expr=m.y) @@ -157,7 +158,7 @@ def test_add_remove_sosconstraint(self): @unittest.skipIf(not xpress_available, "xpress is not available") def test_add_remove_sosconstraint2(self): m = pe.ConcreteModel() - m.a = pe.Set(initialize=[1,2,3], ordered=True) + m.a = pe.Set(initialize=[1, 2, 3], ordered=True) m.x = pe.Var(m.a, within=pe.Binary) m.y = pe.Var(within=pe.Binary) m.obj = pe.Objective(expr=m.y) @@ -218,7 +219,7 @@ def test_add_column_exceptions(self): m = pe.ConcreteModel() m.x = pe.Var() m.c = pe.Constraint(expr=(0, m.x, 1)) - m.ci = pe.Constraint([1,2], rule=lambda m,i:(0,m.x,i+1)) + m.ci = pe.Constraint([1, 2], rule=lambda m, i: (0, m.x, i + 1)) m.cd = pe.Constraint(expr=(0, -m.x, 1)) m.cd.deactivate() m.obj = pe.Objective(expr=-m.x) @@ -232,7 +233,7 @@ def test_add_column_exceptions(self): m2 = pe.ConcreteModel() m2.y = pe.Var() - m2.c = pe.Constraint(expr=(0,m.x,1)) + m2.c = pe.Constraint(expr=(0, m.x, 1)) # different model than attached to opt self.assertRaises(RuntimeError, opt.add_column, m2, m2.y, 0, [], []) @@ -245,7 +246,7 @@ def test_add_column_exceptions(self): m.y = pe.Var() # len(coefficents) == len(constraints) - self.assertRaises(RuntimeError, opt.add_column, m, m.y, -2, [m.c], [1,2]) + self.assertRaises(RuntimeError, opt.add_column, m, m.y, -2, [m.c], [1, 2]) self.assertRaises(RuntimeError, opt.add_column, m, m.y, -2, [m.c, z], [1]) # add indexed constraint @@ -272,8 +273,7 @@ def test_nonconvexqp_locally_optimal(self): m.x2 = pe.Var() m.x3 = pe.Var() - m.obj = pe.Objective(rule=lambda m: 2 * m.x1 + m.x2 + m.x3, - sense=pe.minimize) + m.obj = pe.Objective(rule=lambda m: 2 * m.x1 + m.x2 + m.x3, sense=pe.minimize) m.equ1 = pe.Constraint(rule=lambda m: m.x1 + m.x2 + m.x3 == 1) m.cone = pe.Constraint(rule=lambda m: m.x2 * m.x2 + m.x3 * m.x3 <= m.x1 * m.x1) m.equ2 = pe.Constraint(rule=lambda m: m.x1 >= 0) @@ -283,7 +283,9 @@ def test_nonconvexqp_locally_optimal(self): results = opt.solve(m) self.assertEqual(results.solver.status, SolverStatus.ok) - self.assertEqual(results.solver.termination_condition, TerminationCondition.locallyOptimal) + self.assertEqual( + results.solver.termination_condition, TerminationCondition.locallyOptimal + ) # Cannot test exact values since the may be different depending on # random effects. So just test all are non-zero. @@ -299,8 +301,7 @@ def test_nonconvexqp_infeasible(self): m.x2 = pe.Var() m.x3 = pe.Var() - m.obj = pe.Objective(rule=lambda m: 2 * m.x1 + m.x2 + m.x3, - sense=pe.minimize) + m.obj = pe.Objective(rule=lambda m: 2 * m.x1 + m.x2 + m.x3, sense=pe.minimize) m.equ1a = pe.Constraint(rule=lambda m: m.x1 + m.x2 + m.x3 == 1) m.equ1b = pe.Constraint(rule=lambda m: m.x1 + m.x2 + m.x3 == -1) m.cone = pe.Constraint(rule=lambda m: m.x2 * m.x2 + m.x3 * m.x3 <= m.x1 * m.x1) @@ -311,4 +312,6 @@ def test_nonconvexqp_infeasible(self): results = opt.solve(m) self.assertEqual(results.solver.status, SolverStatus.ok) - self.assertEqual(results.solver.termination_condition, TerminationCondition.infeasible) + self.assertEqual( + results.solver.termination_condition, TerminationCondition.infeasible + ) diff --git a/pyomo/solvers/tests/mip/model.py b/pyomo/solvers/tests/mip/model.py index 91e1ce9a427..389151160b8 100644 --- a/pyomo/solvers/tests/mip/model.py +++ b/pyomo/solvers/tests/mip/model.py @@ -13,10 +13,13 @@ model = AbstractModel() -model.A = RangeSet(1,4) +model.A = RangeSet(1, 4) model.x = Var(model.A) + def obj_rule(model): return sum_product(model.x) + + model.obj = Objective(rule=obj_rule) diff --git a/pyomo/solvers/tests/mip/test_asl.py b/pyomo/solvers/tests/mip/test_asl.py index f08b0fe68e5..1e6a9e53030 100644 --- a/pyomo/solvers/tests/mip/test_asl.py +++ b/pyomo/solvers/tests/mip/test_asl.py @@ -27,28 +27,34 @@ deleteFiles = True old_ignore_time = None + + def setUpModule(): global old_ignore_time old_ignore_time = SolverResults.default_print_options.ignore_time SolverResults.default_print_options.ignore_time = True + def tearDownModule(): SolverResults.default_print_options.ignore_time = old_ignore_time + cplexamp_available = False -class mock_all(unittest.TestCase): + +class mock_all(unittest.TestCase): @classmethod def setUpClass(cls): global cplexamp_available import pyomo.environ from pyomo.solvers.tests.solvers import test_solver_cases + cplexamp_available = test_solver_cases('cplex', 'nl').available def setUp(self): self.do_setup(False) - def do_setup(self,flag): + def do_setup(self, flag): TempfileManager.push() if flag: if not cplexamp_available: @@ -62,76 +68,74 @@ def tearDown(self): self.asl = None def test_path(self): - """ Verify that the ASL path is what is expected """ + """Verify that the ASL path is what is expected""" if type(self.asl) == 'ASL': - self.assertEqual(self.asl.executable.split(os.sep)[-1], - "ASL"+pyomo.common.executable_extension) + self.assertEqual( + self.asl.executable.split(os.sep)[-1], + "ASL" + pyomo.common.executable_extension, + ) def test_solve4(self): - """ Test ASL - test4.nl """ + """Test ASL - test4.nl""" _log = TempfileManager.create_tempfile(".test_solve4.log") _out = TempfileManager.create_tempfile(".test_solve4.txt") - results = self.asl.solve(join(currdir, "test4.nl"), - logfile=_log, - suffixes=['.*']) + results = self.asl.solve( + join(currdir, "test4.nl"), logfile=_log, suffixes=['.*'] + ) results.write(filename=_out, times=False, format='json') _baseline = join(currdir, "test4_asl.txt") with open(_out, 'r') as out, open(_baseline, 'r') as txt: - self.assertStructuredAlmostEqual(json.load(txt), json.load(out), - abstol=1e-4, - allow_second_superset=True) + self.assertStructuredAlmostEqual( + json.load(txt), json.load(out), abstol=1e-4, allow_second_superset=True + ) # # This test is disabled, but it's useful for interactively exercising # the option specifications of a solver # def Xtest_options(self): - """ Test ASL options behavior """ - results = self.asl.solve(currdir+"bell3a.mps", - logfile=currdir+"test_options.log", - options="sec=0.1 foo=1 bar='a=b c=d' xx_zz=yy", - suffixes=['.*']) - results.write(filename=currdir+"test_options.txt", - times=False) + """Test ASL options behavior""" + results = self.asl.solve( + currdir + "bell3a.mps", + logfile=currdir + "test_options.log", + options="sec=0.1 foo=1 bar='a=b c=d' xx_zz=yy", + suffixes=['.*'], + ) + results.write(filename=currdir + "test_options.txt", times=False) _out, _log = join(currdir, "test_options.txt"), join(currdir, "test4_asl.txt") - self.assertTrue(cmp(_out, _log), - msg="Files %s and %s differ" % (_out, _log)) - #os.remove(currdir+"test4.sol") - #os.remove(currdir+"test_solve4.log") + self.assertTrue(cmp(_out, _log), msg="Files %s and %s differ" % (_out, _log)) + # os.remove(currdir+"test4.sol") + # os.remove(currdir+"test_solve4.log") def test_error1(self): - """ Bad results format """ + """Bad results format""" try: model = ConcreteModel() - results = self.asl.solve(model, - format=ResultsFormat.sol, - suffixes=['.*']) + results = self.asl.solve(model, format=ResultsFormat.sol, suffixes=['.*']) self.fail("test_error1") except ValueError: pass def test_error2(self): - """ Bad solve option """ + """Bad solve option""" try: model = ConcreteModel() - results = self.asl.solve(model, - foo="bar") + results = self.asl.solve(model, foo="bar") self.fail("test_error2") except ValueError: pass def test_error3(self): - """ Bad solve option """ + """Bad solve option""" try: - results = self.asl.solve(currdir+"model.py", - foo="bar") + results = self.asl.solve(currdir + "model.py", foo="bar") self.fail("test_error3") except ValueError: pass -class mip_all(mock_all): +class mip_all(mock_all): def setUp(self): self.do_setup(True) diff --git a/pyomo/solvers/tests/mip/test_convert.py b/pyomo/solvers/tests/mip/test_convert.py index 4fa48399bf9..cd916da29f2 100644 --- a/pyomo/solvers/tests/mip/test_convert.py +++ b/pyomo/solvers/tests/mip/test_convert.py @@ -28,59 +28,60 @@ from pyomo.opt import ProblemFormat, ConverterError, convert_problem from pyomo.common import Executable + def filter(line): return 'Problem' in line or line.startswith('NAME') + currdir = this_file_dir() deleteFiles = True -class MockArg(object): +class MockArg(object): def __init__(self): pass def valid_problem_types(self): return [ProblemFormat.pyomo] - def write(self,filename="", format=None, solver_capability=None, io_options={}): - return (filename,None) + def write(self, filename="", format=None, solver_capability=None, io_options={}): + return (filename, None) -class MockArg2(MockArg): +class MockArg2(MockArg): def valid_problem_types(self): return [ProblemFormat.nl] - def write(self,filename="", format=None, solver_capability=None, io_options={}): - OUTPUT=open(filename,"w") - INPUT=open(join(currdir, "test4.nl")) + def write(self, filename="", format=None, solver_capability=None, io_options={}): + OUTPUT = open(filename, "w") + INPUT = open(join(currdir, "test4.nl")) for line in INPUT: OUTPUT.write(line) OUTPUT.close() INPUT.close() - return (filename,None) + return (filename, None) -class MockArg3(MockArg): +class MockArg3(MockArg): def valid_problem_types(self): return [ProblemFormat.mod] - def write(self,filename="", format=None, solver_capability=None, io_options={}): - return (filename,None) + def write(self, filename="", format=None, solver_capability=None, io_options={}): + return (filename, None) -class MockArg4(MockArg): - def write(self,filename="", format=None, solver_capability=None, io_options={}): - OUTPUT=open(filename,"w") - INPUT=open(join(currdir, "test4.nl")) +class MockArg4(MockArg): + def write(self, filename="", format=None, solver_capability=None, io_options={}): + OUTPUT = open(filename, "w") + INPUT = open(join(currdir, "test4.nl")) for line in INPUT: OUTPUT.write(line) OUTPUT.close() INPUT.close() - return (filename,None) + return (filename, None) class Test(unittest.TestCase): - @classmethod def setUpClass(cls): import pyomo.environ @@ -92,33 +93,35 @@ def tearDown(self): TempfileManager.pop(remove=deleteFiles or self.currentTestPassed()) def test_nl_nl1(self): - #""" Convert from NL to NL """ - ans = convert_problem( ("test4.nl",), None, [ProblemFormat.nl]) - self.assertEqual(ans[0],("test4.nl",)) + # """ Convert from NL to NL """ + ans = convert_problem(("test4.nl",), None, [ProblemFormat.nl]) + self.assertEqual(ans[0], ("test4.nl",)) def test_nl_nl2(self): - #""" Convert from NL to NL """ - ans = convert_problem( ("test4.nl","tmp.nl"), None, [ProblemFormat.nl]) - self.assertEqual(ans[0],("test4.nl","tmp.nl")) + # """ Convert from NL to NL """ + ans = convert_problem(("test4.nl", "tmp.nl"), None, [ProblemFormat.nl]) + self.assertEqual(ans[0], ("test4.nl", "tmp.nl")) @unittest.skipUnless( - Executable("pico_convert").available(), 'pico_convert required') + Executable("pico_convert").available(), 'pico_convert required' + ) def test_nl_lp1(self): - #""" Convert from NL to LP """ - ans = convert_problem( - (join(currdir, "test4.nl"),), None, [ProblemFormat.cpxlp]) - self.assertEqual(ans[0][0][-15:],"pico_convert.lp") + # """ Convert from NL to LP """ + ans = convert_problem((join(currdir, "test4.nl"),), None, [ProblemFormat.cpxlp]) + self.assertEqual(ans[0][0][-15:], "pico_convert.lp") _out, _log = ans[0][0], join(currdir, "test1_convert.lp") - self.assertTrue(cmp(_out, _log), - msg="Files %s and %s differ" % (_out, _log)) + self.assertTrue(cmp(_out, _log), msg="Files %s and %s differ" % (_out, _log)) @unittest.skipUnless(Executable("glpsol").available(), 'glpsol required') def test_mod_lp1(self): - #""" Convert from MOD to LP """ + # """ Convert from MOD to LP """ ans = convert_problem( - (join(currdir, "test3.mod"),), None, [ProblemFormat.cpxlp]) + (join(currdir, "test3.mod"),), None, [ProblemFormat.cpxlp] + ) self.assertTrue(ans[0][0].endswith("glpsol.lp")) - with open(ans[0][0], 'r') as f1, open(join(currdir, "test2_convert.lp"), 'r') as f2: + with open(ans[0][0], 'r') as f1, open( + join(currdir, "test2_convert.lp"), 'r' + ) as f2: for line1, line2 in zip_longest(f1, f2): if 'Problem' in line1: continue @@ -126,12 +129,16 @@ def test_mod_lp1(self): @unittest.skipUnless(Executable("glpsol").available(), 'glpsol required') def test_mod_lp2(self): - #""" Convert from MOD+DAT to LP """ + # """ Convert from MOD+DAT to LP """ ans = convert_problem( (join(currdir, "test5.mod"), join(currdir, "test5.dat")), - None, [ProblemFormat.cpxlp]) + None, + [ProblemFormat.cpxlp], + ) self.assertTrue(ans[0][0].endswith("glpsol.lp")) - with open(ans[0][0], 'r') as f1, open(join(currdir, "test3_convert.lp"), 'r') as f2: + with open(ans[0][0], 'r') as f1, open( + join(currdir, "test3_convert.lp"), 'r' + ) as f2: for line1, line2 in zip_longest(f1, f2): if 'Problem' in line1: continue @@ -139,187 +146,229 @@ def test_mod_lp2(self): @unittest.skipUnless(Executable("ampl").available(), 'ampl required') def test_mod_nl1(self): - #""" Convert from MOD to NL """ - ans = convert_problem( - (join(currdir, "test3.mod"),), None, [ProblemFormat.nl]) + # """ Convert from MOD to NL """ + ans = convert_problem((join(currdir, "test3.mod"),), None, [ProblemFormat.nl]) self.assertTrue(ans[0][0].endswith('.nl')) - #self.assertFileEqualsBinaryFile(ans[0][0], join(currdir, "test_mod_nl1.nl") + # self.assertFileEqualsBinaryFile(ans[0][0], join(currdir, "test_mod_nl1.nl") @unittest.skipUnless(Executable("ampl").available(), 'ampl required') def test_mod_nl2(self): - #""" Convert from MOD+DAT to NL """ + # """ Convert from MOD+DAT to NL """ ans = convert_problem( (join(currdir, "test5.mod"), join(currdir, "test5.dat")), - None, [ProblemFormat.nl]) + None, + [ProblemFormat.nl], + ) self.assertTrue(ans[0][0].endswith('.nl')) - #self.assertTrue(cmp(ans[0][0], join(currdir, "test_mod_nl2.nl") + # self.assertTrue(cmp(ans[0][0], join(currdir, "test_mod_nl2.nl") def test_mock_lp1(self): - #""" Convert from Pyomo to LP """ - arg=MockArg() - ans = convert_problem( (arg, ProblemFormat.cpxlp,arg), None, [ProblemFormat.cpxlp]) - self.assertNotEqual(re.match(".*tmp.*pyomo.lp$",ans[0][0]), None) + # """ Convert from Pyomo to LP """ + arg = MockArg() + ans = convert_problem( + (arg, ProblemFormat.cpxlp, arg), None, [ProblemFormat.cpxlp] + ) + self.assertNotEqual(re.match(".*tmp.*pyomo.lp$", ans[0][0]), None) def test_pyomo_lp1(self): - #""" Convert from Pyomo to LP with file""" - ans = convert_problem( (join(currdir, 'model.py'), ProblemFormat.cpxlp,), None, [ProblemFormat.cpxlp]) - self.assertNotEqual(re.match(".*tmp.*pyomo.lp$",ans[0][0]), None) + # """ Convert from Pyomo to LP with file""" + ans = convert_problem( + (join(currdir, 'model.py'), ProblemFormat.cpxlp), + None, + [ProblemFormat.cpxlp], + ) + self.assertNotEqual(re.match(".*tmp.*pyomo.lp$", ans[0][0]), None) def test_mock_lp2(self): - #""" Convert from NL to LP """ - arg=MockArg2() + # """ Convert from NL to LP """ + arg = MockArg2() try: - ans = convert_problem( (arg,), None, [ProblemFormat.cpxlp]) + ans = convert_problem((arg,), None, [ProblemFormat.cpxlp]) except ConverterError: err = sys.exc_info()[1] if not Executable("pico_convert"): return else: - self.fail("Expected ApplicationError because pico_convert " - "is not available: '%s'" % str(err)) - self.assertEqual(ans[0][0][-15:],"pico_convert.lp") + self.fail( + "Expected ApplicationError because pico_convert " + "is not available: '%s'" % str(err) + ) + self.assertEqual(ans[0][0][-15:], "pico_convert.lp") os.remove(ans[0][0]) # Note sure what to do with this test now that we # have a native MPS converter def Xtest_mock_mps1(self): - #""" Convert from Pyomo to MPS """ - arg=MockArg4() + # """ Convert from Pyomo to MPS """ + arg = MockArg4() try: - ans = convert_problem((arg, ProblemFormat.mps,arg), None, [ProblemFormat.mps]) + ans = convert_problem( + (arg, ProblemFormat.mps, arg), None, [ProblemFormat.mps] + ) except ConverterError: err = sys.exc_info()[1] if not Executable("pico_convert"): return else: - self.fail("Expected ApplicationError because pico_convert " - "is not available: '%s'" % str(err)) - self.assertEqual(ans[0][0][-16:],"pico_convert.mps") + self.fail( + "Expected ApplicationError because pico_convert " + "is not available: '%s'" % str(err) + ) + self.assertEqual(ans[0][0][-16:], "pico_convert.mps") os.remove(ans[0][0]) def test_pyomo_mps1(self): - #""" Convert from Pyomo to MPS with file""" + # """ Convert from Pyomo to MPS with file""" try: - ans = convert_problem( (join(currdir, 'model.py'), ProblemFormat.mps,), None, [ProblemFormat.mps]) + ans = convert_problem( + (join(currdir, 'model.py'), ProblemFormat.mps), + None, + [ProblemFormat.mps], + ) except ConverterError: err = sys.exc_info()[1] if not Executable("pico_convert"): return else: - self.fail("Expected ApplicationError because pico_convert " - "is not available: '%s'" % str(err)) - self.assertEqual(ans[0][0][-16:],"pico_convert.mps") + self.fail( + "Expected ApplicationError because pico_convert " + "is not available: '%s'" % str(err) + ) + self.assertEqual(ans[0][0][-16:], "pico_convert.mps") os.remove(ans[0][0]) def test_mock_nl1(self): - #""" Convert from Pyomo to NL """ + # """ Convert from Pyomo to NL """ arg = MockArg4() - ans = convert_problem( (arg, ProblemFormat.nl,arg), None, [ProblemFormat.nl]) - self.assertNotEqual(re.match(".*tmp.*pyomo.nl$",ans[0][0]), None) + ans = convert_problem((arg, ProblemFormat.nl, arg), None, [ProblemFormat.nl]) + self.assertNotEqual(re.match(".*tmp.*pyomo.nl$", ans[0][0]), None) os.remove(ans[0][0]) def test_pyomo_nl1(self): - #""" Convert from Pyomo to NL with file""" - ans = convert_problem( (join(currdir, 'model.py'), ProblemFormat.nl,), None, [ProblemFormat.nl]) - self.assertNotEqual(re.match(".*tmp.*pyomo.nl$",ans[0][0]), None) + # """ Convert from Pyomo to NL with file""" + ans = convert_problem( + (join(currdir, 'model.py'), ProblemFormat.nl), None, [ProblemFormat.nl] + ) + self.assertNotEqual(re.match(".*tmp.*pyomo.nl$", ans[0][0]), None) os.remove(ans[0][0]) def test_error1(self): - #""" No valid problem types """ + # """ No valid problem types """ try: - convert_problem( ("test4.nl","tmp.nl"), ProblemFormat.nl, []) + convert_problem(("test4.nl", "tmp.nl"), ProblemFormat.nl, []) self.fail("Expected ConverterError exception") except ConverterError: err = sys.exc_info()[1] pass def test_error2(self): - #""" Target problem type is not valid """ + # """ Target problem type is not valid """ try: - convert_problem( ("test4.nl","tmp.nl"), ProblemFormat.nl, [ProblemFormat.mps]) + convert_problem( + ("test4.nl", "tmp.nl"), ProblemFormat.nl, [ProblemFormat.mps] + ) self.fail("Expected ConverterError exception") except ConverterError: pass def test_error3(self): - #""" Empty argument list """ + # """ Empty argument list """ try: - convert_problem( (), None, [ProblemFormat.mps]) + convert_problem((), None, [ProblemFormat.mps]) self.fail("Expected ConverterError exception") - except ConverterError: + except ConverterError: pass def test_error4(self): - #""" Unknown source type """ + # """ Unknown source type """ try: - convert_problem( ("prob.foo",), None, [ProblemFormat.mps]) + convert_problem(("prob.foo",), None, [ProblemFormat.mps]) self.fail("Expected ConverterError exception") - except ConverterError: + except ConverterError: pass def test_error5(self): - #""" Unknown source type """ + # """ Unknown source type """ try: - convert_problem( ("prob.lp",), ProblemFormat.nl, [ProblemFormat.nl]) + convert_problem(("prob.lp",), ProblemFormat.nl, [ProblemFormat.nl]) self.fail("Expected ConverterError exception") - except ConverterError: + except ConverterError: pass def test_error6(self): - #""" Cannot use pico_convert with more than one file """ + # """ Cannot use pico_convert with more than one file """ try: - ans = convert_problem( (join(currdir, "test4.nl"), "foo"), None, [ProblemFormat.cpxlp]) + ans = convert_problem( + (join(currdir, "test4.nl"), "foo"), None, [ProblemFormat.cpxlp] + ) self.fail("Expected ConverterError exception") except ConverterError: pass def test_error8(self): - #""" Error when source file cannot be found """ + # """ Error when source file cannot be found """ try: - ans = convert_problem( (join(currdir, "unknown.nl"),), None, [ProblemFormat.cpxlp]) + ans = convert_problem( + (join(currdir, "unknown.nl"),), None, [ProblemFormat.cpxlp] + ) self.fail("Expected ConverterError exception") except ApplicationError: err = sys.exc_info()[1] if not Executable("pico_convert"): - self.fail("Expected ApplicationError because pico_convert " - "is not available: '%s'" % str(err)) + self.fail( + "Expected ApplicationError because pico_convert " + "is not available: '%s'" % str(err) + ) return except ConverterError: pass def test_error9(self): - #""" The Opt configuration has not been initialized """ + # """ The Opt configuration has not been initialized """ cmd = Executable("pico_convert").disable() try: - ans = convert_problem( (join(currdir, "test4.nl"),), None, [ProblemFormat.cpxlp]) + ans = convert_problem( + (join(currdir, "test4.nl"),), None, [ProblemFormat.cpxlp] + ) self.fail("This test didn't fail, but pico_convert should not be defined.") except ConverterError: pass cmd = Executable("pico_convert").rehash() def test_error10(self): - #""" GLPSOL can only convert file data """ + # """ GLPSOL can only convert file data """ try: arg = MockArg3() - ans = convert_problem( (arg, ProblemFormat.cpxlp,arg), None, [ProblemFormat.cpxlp]) + ans = convert_problem( + (arg, ProblemFormat.cpxlp, arg), None, [ProblemFormat.cpxlp] + ) self.fail("This test didn't fail, but glpsol cannot handle objects.") except ConverterError: pass def test_error11(self): - #""" Cannot convert MOD that contains data """ + # """ Cannot convert MOD that contains data """ try: - ans = convert_problem( (join(currdir, "test3.mod"),join(currdir, "test5.dat")), None, [ProblemFormat.cpxlp]) - self.fail("Expected ConverterError exception because we provided a MOD file with a 'data;' declaration") + ans = convert_problem( + (join(currdir, "test3.mod"), join(currdir, "test5.dat")), + None, + [ProblemFormat.cpxlp], + ) + self.fail( + "Expected ConverterError exception because we provided a MOD file with a 'data;' declaration" + ) except ApplicationError: err = sys.exc_info()[1] if Executable("glpsol"): - self.fail("Expected ApplicationError because glpsol " - "is not available: '%s'" % str(err)) + self.fail( + "Expected ApplicationError because glpsol " + "is not available: '%s'" % str(err) + ) return except ConverterError: pass + if __name__ == "__main__": deleteFiles = False unittest.main() diff --git a/pyomo/solvers/tests/mip/test_factory.py b/pyomo/solvers/tests/mip/test_factory.py index 80e6f48fc91..6960a0f8ced 100644 --- a/pyomo/solvers/tests/mip/test_factory.py +++ b/pyomo/solvers/tests/mip/test_factory.py @@ -16,35 +16,37 @@ import pyomo.common.unittest as unittest -from pyomo.opt import (AbstractProblemWriter, AbstractResultsReader, - OptSolver, ReaderFactory, - SolverFactory, WriterFactory) +from pyomo.opt import ( + AbstractProblemWriter, + AbstractResultsReader, + OptSolver, + ReaderFactory, + SolverFactory, + WriterFactory, +) from pyomo.opt.base.solvers import UnknownSolver from pyomo.opt.plugins.sol import ResultsReader_sol from pyomo.solvers.plugins.solvers.CBCplugin import MockCBC -class MockWriter(AbstractProblemWriter): +class MockWriter(AbstractProblemWriter): def __init__(self, name=None): - AbstractProblemWriter.__init__(self,name) + AbstractProblemWriter.__init__(self, name) class MockReader(AbstractResultsReader): - def __init__(self, name=None): - AbstractResultsReader.__init__(self,name) + AbstractResultsReader.__init__(self, name) class MockSolver(OptSolver): - def __init__(self, **kwds): kwds['type'] = 'stest_type' kwds['doc'] = 'MockSolver Documentation' - OptSolver.__init__(self,**kwds) + OptSolver.__init__(self, **kwds) class OptFactoryDebug(unittest.TestCase): - @classmethod def setUpClass(cls): import pyomo.environ @@ -60,9 +62,22 @@ def test_solver_factory(self): """ SolverFactory.register('stest3')(MockSolver) ans = sorted(SolverFactory) - tmp = ['_mock_asl', '_mock_cbc', '_mock_cplex', '_mock_glpk', 'cbc', 'cplex', 'glpk', 'scip', 'stest3', 'asl'] + tmp = [ + '_mock_asl', + '_mock_cbc', + '_mock_cplex', + '_mock_glpk', + 'cbc', + 'cplex', + 'glpk', + 'scip', + 'stest3', + 'asl', + ] tmp.sort() - self.assertTrue(set(tmp) <= set(ans), msg="Set %s is not a subset of set %s" %(tmp,ans)) + self.assertTrue( + set(tmp) <= set(ans), msg="Set %s is not a subset of set %s" % (tmp, ans) + ) def test_solver_instance(self): """ @@ -74,7 +89,7 @@ def test_solver_instance(self): self.assertEqual(type(ans), MockCBC) ans = SolverFactory("_mock_cbc", name="mymock") self.assertEqual(type(ans), MockCBC) - self.assertEqual(ans.name, "mymock") + self.assertEqual(ans.name, "mymock") def test_solver_registration(self): """ @@ -115,15 +130,14 @@ def test_writer_registration(self): WriterFactory.register('wtest3')(MockWriter) self.assertTrue('wtest3' in WriterFactory) - def test_reader_factory(self): """ Testing the pyomo.opt reader factory """ ReaderFactory.register('rtest3')(MockReader) ans = ReaderFactory - #self.assertEqual(len(ans),4) - self.assertTrue(set(ans) >= set(["rtest3", "sol","yaml", "json"])) + # self.assertEqual(len(ans),4) + self.assertTrue(set(ans) >= set(["rtest3", "sol", "yaml", "json"])) def test_reader_instance(self): """ @@ -133,9 +147,9 @@ def test_reader_instance(self): self.assertEqual(ans, None) ans = ReaderFactory("sol") self.assertEqual(type(ans), ResultsReader_sol) - #ans = pyomo.opt.ReaderFactory("osrl", "myreader") - #self.assertEqual(type(ans), pyomo.opt.reader.OS.ResultsReader_osrl) - #self.assertEqual(ans.name, "myreader") + # ans = pyomo.opt.ReaderFactory("osrl", "myreader") + # self.assertEqual(type(ans), pyomo.opt.reader.OS.ResultsReader_osrl) + # self.assertEqual(ans.name, "myreader") def test_reader_registration(self): """ @@ -146,5 +160,6 @@ def test_reader_registration(self): ReaderFactory.register('rtest3')(MockReader) self.assertTrue('rtest3' in ReaderFactory) + if __name__ == "__main__": unittest.main() diff --git a/pyomo/solvers/tests/mip/test_ipopt.py b/pyomo/solvers/tests/mip/test_ipopt.py index 5fb0ea071d1..d74b75244f4 100644 --- a/pyomo/solvers/tests/mip/test_ipopt.py +++ b/pyomo/solvers/tests/mip/test_ipopt.py @@ -21,22 +21,30 @@ import pyomo.opt from pyomo.core import ( - ConcreteModel, RangeSet, Var, Param, Objective, ConstraintList, - value, minimize, + ConcreteModel, + RangeSet, + Var, + Param, + Objective, + ConstraintList, + value, + minimize, ) currdir = this_file_dir() deleteFiles = True ipopt_available = False -class Test(unittest.TestCase): + +class Test(unittest.TestCase): @classmethod def setUpClass(cls): global ipopt_available import pyomo.environ from pyomo.solvers.tests.solvers import test_solver_cases - ipopt_available = test_solver_cases('ipopt','nl').available + + ipopt_available = test_solver_cases('ipopt', 'nl').available def setUp(self): if not ipopt_available: @@ -65,30 +73,33 @@ def setUp(self): sisser_instance = ConcreteModel() - sisser_instance.N = RangeSet(1,2) - sisser_instance.xinit = Param( - sisser_instance.N, initialize={ 1 : 1.0, 2 : 0.1}) + sisser_instance.N = RangeSet(1, 2) + sisser_instance.xinit = Param(sisser_instance.N, initialize={1: 1.0, 2: 0.1}) def fa(model, i): return value(model.xinit[i]) - sisser_instance.x = Var(sisser_instance.N,initialize=fa) + + sisser_instance.x = Var(sisser_instance.N, initialize=fa) def f(model): - return 3*model.x[1]**4 - 2*(model.x[1]*model.x[2])**2 + 3*model.x[2]**4 - sisser_instance.f = Objective(rule=f,sense=minimize) + return ( + 3 * model.x[1] ** 4 + - 2 * (model.x[1] * model.x[2]) ** 2 + + 3 * model.x[2] ** 4 + ) - self.sisser_instance = sisser_instance + sisser_instance.f = Objective(rule=f, sense=minimize) + self.sisser_instance = sisser_instance def tearDown(self): TempfileManager.pop(remove=deleteFiles or self.currentTestPassed()) def compare_json(self, file1, file2): - with open(file1, 'r') as out, \ - open(file2, 'r') as txt: - self.assertStructuredAlmostEqual(json.load(txt), json.load(out), - abstol=1e-7, - allow_second_superset=True) + with open(file1, 'r') as out, open(file2, 'r') as txt: + self.assertStructuredAlmostEqual( + json.load(txt), json.load(out), abstol=1e-7, allow_second_superset=True + ) def test_version_asl(self): self.assertTrue(self.asl.version() is not None) @@ -103,76 +114,63 @@ def test_version_ipopt(self): def test_asl_solve_from_nl(self): # Test ipopt solve from nl file _log = TempfileManager.create_tempfile(".test_ipopt.log") - results = self.asl.solve(join(currdir, "sisser.pyomo.nl"), - logfile=_log, - suffixes=['.*']) + results = self.asl.solve( + join(currdir, "sisser.pyomo.nl"), logfile=_log, suffixes=['.*'] + ) # We don't want the test to care about which Ipopt version we are using results.Solution(0).Message = "Ipopt" results.Solver.Message = "Ipopt" _out = TempfileManager.create_tempfile(".test_ipopt.txt") - results.write(filename=_out, - times=False, - format='json') - self.compare_json( - _out, join(currdir, "test_solve_from_nl.baseline")) + results.write(filename=_out, times=False, format='json') + self.compare_json(_out, join(currdir, "test_solve_from_nl.baseline")) def test_ipopt_solve_from_nl(self): # Test ipopt solve from nl file _log = TempfileManager.create_tempfile(".test_ipopt.log") - results = self.ipopt.solve(join(currdir, "sisser.pyomo.nl"), - logfile=_log, - suffixes=['.*']) + results = self.ipopt.solve( + join(currdir, "sisser.pyomo.nl"), logfile=_log, suffixes=['.*'] + ) # We don't want the test to care about which Ipopt version we are using results.Solution(0).Message = "Ipopt" results.Solver.Message = "Ipopt" _out = TempfileManager.create_tempfile(".test_ipopt.txt") - results.write(filename=_out, - times=False, - format='json') - self.compare_json( - _out, join(currdir, "test_solve_from_nl.baseline")) + results.write(filename=_out, times=False, format='json') + self.compare_json(_out, join(currdir, "test_solve_from_nl.baseline")) def test_asl_solve_from_instance(self): # Test ipopt solve from a pyomo instance and load the solution - results = self.asl.solve(self.sisser_instance, - suffixes=['.*']) + results = self.asl.solve(self.sisser_instance, suffixes=['.*']) # We don't want the test to care about which Ipopt version we are using self.sisser_instance.solutions.store_to(results) results.Solution(0).Message = "Ipopt" results.Solver.Message = "Ipopt" _out = TempfileManager.create_tempfile(".test_ipopt.txt") - results.write(filename=_out, - times=False, - format='json') - self.compare_json( - _out, join(currdir, "test_solve_from_instance.baseline")) - #self.sisser_instance.load_solutions(results) + results.write(filename=_out, times=False, format='json') + self.compare_json(_out, join(currdir, "test_solve_from_instance.baseline")) + # self.sisser_instance.load_solutions(results) def test_ipopt_solve_from_instance(self): # Test ipopt solve from a pyomo instance and load the solution - results = self.ipopt.solve(self.sisser_instance, - suffixes=['.*']) + results = self.ipopt.solve(self.sisser_instance, suffixes=['.*']) # We don't want the test to care about which Ipopt version we are using self.sisser_instance.solutions.store_to(results) results.Solution(0).Message = "Ipopt" results.Solver.Message = "Ipopt" _out = TempfileManager.create_tempfile(".test_ipopt.txt") - results.write(filename=_out, - times=False, - format='json') - self.compare_json( - _out, join(currdir, "test_solve_from_instance.baseline")) - #self.sisser_instance.load_solutions(results) + results.write(filename=_out, times=False, format='json') + self.compare_json(_out, join(currdir, "test_solve_from_instance.baseline")) + # self.sisser_instance.load_solutions(results) def test_ipopt_solve_from_instance_OF_options(self): with self.assertRaises(ValueError): # using OF_ options AND option_file_name # is not allowed - self.ipopt.solve(self.sisser_instance, - suffixes=['.*'], - options={"OF_mu_init": 0.1, - "option_file_name": "junk.opt"}) + self.ipopt.solve( + self.sisser_instance, + suffixes=['.*'], + options={"OF_mu_init": 0.1, "option_file_name": "junk.opt"}, + ) # Creating a dummy ipopt.opt file in the cwd # will cover the code that prints a warning _cwd = os.getcwd() @@ -183,15 +181,16 @@ def test_ipopt_solve_from_instance_OF_options(self): open(join(tmpdir, 'ipopt.opt'), "w").close() # Test ipopt solve from a pyomo instance and load the solution with LoggingIntercept() as LOG: - results = self.ipopt.solve(self.sisser_instance, - suffixes=['.*'], - options={"OF_mu_init": 0.1}) + results = self.ipopt.solve( + self.sisser_instance, suffixes=['.*'], options={"OF_mu_init": 0.1} + ) self.assertRegex( LOG.getvalue().replace("\n", " "), r"A file named (.*) exists in the current working " r"directory, but Ipopt options file options \(i.e., " r"options that start with 'OF_'\) were provided. The " - r"options file \1 will be ignored.") + r"options file \1 will be ignored.", + ) finally: os.chdir(_cwd) @@ -200,12 +199,9 @@ def test_ipopt_solve_from_instance_OF_options(self): results.Solution(0).Message = "Ipopt" results.Solver.Message = "Ipopt" _out = TempfileManager.create_tempfile(".test_ipopt.txt") - results.write(filename=_out, - times=False, - format='json') - self.compare_json( - _out, join(currdir, "test_solve_from_instance.baseline")) - #self.sisser_instance.load_solutions(results) + results.write(filename=_out, times=False, format='json') + self.compare_json(_out, join(currdir, "test_solve_from_instance.baseline")) + # self.sisser_instance.load_solutions(results) def test_bad_dof(self): m = ConcreteModel() @@ -214,12 +210,13 @@ def test_bad_dof(self): m.c = ConstraintList() m.c.add(m.x + m.y == 1) m.c.add(m.x - m.y == 0) - m.c.add(2*m.x - 3*m.y == 1) + m.c.add(2 * m.x - 3 * m.y == 1) res = self.ipopt.solve(m) self.assertEqual(str(res.solver.status), "warning") self.assertEqual(str(res.solver.termination_condition), "other") self.assertTrue("Too few degrees of freedom" in res.solver.message) + if __name__ == "__main__": deleteFiles = False unittest.main() diff --git a/pyomo/solvers/tests/mip/test_mip.py b/pyomo/solvers/tests/mip/test_mip.py index 4e4a04ea4bd..0257e65de20 100644 --- a/pyomo/solvers/tests/mip/test_mip.py +++ b/pyomo/solvers/tests/mip/test_mip.py @@ -14,7 +14,8 @@ import os from os.path import abspath, dirname -currdir = dirname(abspath(__file__))+os.sep + +currdir = dirname(abspath(__file__)) + os.sep import pyomo.common.unittest as unittest diff --git a/pyomo/solvers/tests/mip/test_scip.py b/pyomo/solvers/tests/mip/test_scip.py index 186e2d74d9d..9e49bd802f4 100644 --- a/pyomo/solvers/tests/mip/test_scip.py +++ b/pyomo/solvers/tests/mip/test_scip.py @@ -26,14 +26,16 @@ deleteFiles = True scip_available = False -class Test(unittest.TestCase): + +class Test(unittest.TestCase): @classmethod def setUpClass(cls): global scip_available import pyomo.environ from pyomo.solvers.tests.solvers import test_solver_cases - scip_available = test_solver_cases('scip','nl').available + + scip_available = test_solver_cases('scip', 'nl').available def setUp(self): if not scip_available: @@ -51,11 +53,10 @@ def tearDown(self): TempfileManager.pop(remove=deleteFiles or self.currentTestPassed()) def compare_json(self, file1, file2): - with open(file1, 'r') as out, \ - open(file2, 'r') as txt: - self.assertStructuredAlmostEqual(json.load(txt), json.load(out), - abstol=1e-7, - allow_second_superset=True) + with open(file1, 'r') as out, open(file2, 'r') as txt: + self.assertStructuredAlmostEqual( + json.load(txt), json.load(out), abstol=1e-7, allow_second_superset=True + ) def test_version_scip(self): self.assertTrue(self.scip.version() is not None) @@ -64,8 +65,7 @@ def test_version_scip(self): def test_scip_solve_from_instance(self): # Test scip solve from a pyomo instance and load the solution - results = self.scip.solve(self.model, - suffixes=['.*']) + results = self.scip.solve(self.model, suffixes=['.*']) # We don't want the test to care about which Scip version we are using self.model.solutions.store_to(results) results.Solution(0).Message = "Scip" @@ -73,8 +73,7 @@ def test_scip_solve_from_instance(self): results.Solver.Time = 0 _out = TempfileManager.create_tempfile(".txt") results.write(filename=_out, times=False, format='json') - self.compare_json( - _out, join(currdir, "test_scip_solve_from_instance.baseline")) + self.compare_json(_out, join(currdir, "test_scip_solve_from_instance.baseline")) def test_scip_solve_from_instance_options(self): @@ -87,15 +86,16 @@ def test_scip_solve_from_instance_options(self): open(join(tmpdir, 'scip.set'), "w").close() # Test scip solve from a pyomo instance and load the solution with LoggingIntercept() as LOG: - results = self.scip.solve(self.model, - suffixes=['.*'], - options={"limits/softtime": 100}) + results = self.scip.solve( + self.model, suffixes=['.*'], options={"limits/softtime": 100} + ) self.assertRegex( LOG.getvalue().replace("\n", " "), r"A file named (.*) exists in the current working " r"directory, but SCIP options are being " r"set using a separate options file. The " - r"options file \1 will be ignored.") + r"options file \1 will be ignored.", + ) finally: os.chdir(_cwd) # We don't want the test to care about which Scip version we are using @@ -105,8 +105,8 @@ def test_scip_solve_from_instance_options(self): results.Solver.Time = 0 _out = TempfileManager.create_tempfile(".txt") results.write(filename=_out, times=False, format='json') - self.compare_json( - _out, join(currdir, "test_scip_solve_from_instance.baseline")) + self.compare_json(_out, join(currdir, "test_scip_solve_from_instance.baseline")) + if __name__ == "__main__": deleteFiles = False diff --git a/pyomo/solvers/tests/mip/test_scip_log_data.py b/pyomo/solvers/tests/mip/test_scip_log_data.py index 518e86f972f..2ad2c5bd26b 100644 --- a/pyomo/solvers/tests/mip/test_scip_log_data.py +++ b/pyomo/solvers/tests/mip/test_scip_log_data.py @@ -13,169 +13,177 @@ import random -#****************************************************************************** -#****************************************************************************** +# ****************************************************************************** +# ****************************************************************************** # carry out optimisations -def optimise(problem: pyo.ConcreteModel, - solver_timelimit, - solver_rel_mip_gap, - solver_abs_mip_gap, - print_solver_output: bool = False): - + +def optimise( + problem: pyo.ConcreteModel, + solver_timelimit, + solver_rel_mip_gap, + solver_abs_mip_gap, + print_solver_output: bool = False, +): + # config - + options_dict_format = { - 'limits/time':solver_timelimit, - 'limits/gap':solver_rel_mip_gap, - 'limits/absgap':solver_abs_mip_gap - } - + 'limits/time': solver_timelimit, + 'limits/gap': solver_rel_mip_gap, + 'limits/absgap': solver_abs_mip_gap, + } + opt = pyo.SolverFactory('scip') - + for key, value in options_dict_format.items(): - + opt.options[key] = value - + # solve - - results = opt.solve( - problem, - tee=print_solver_output - ) - + + results = opt.solve(problem, tee=print_solver_output) + # return - + return results, opt -#****************************************************************************** -#****************************************************************************** + +# ****************************************************************************** +# ****************************************************************************** + def problem_lp_optimal(): - + model = pyo.ConcreteModel('lp_optimal') - - model.x = pyo.Var([1,2], domain=pyo.NonNegativeReals) - - model.OBJ = pyo.Objective(expr = 2*model.x[1] + 3*model.x[2]) - - model.Constraint1 = pyo.Constraint(expr = 3*model.x[1] + 4*model.x[2] >= 1) - + + model.x = pyo.Var([1, 2], domain=pyo.NonNegativeReals) + + model.OBJ = pyo.Objective(expr=2 * model.x[1] + 3 * model.x[2]) + + model.Constraint1 = pyo.Constraint(expr=3 * model.x[1] + 4 * model.x[2] >= 1) + return model + def problem_lp_infeasible(): - + model = pyo.ConcreteModel('lp_infeasible') - - model.x = pyo.Var([1,2], domain=pyo.NonNegativeReals) - - model.OBJ = pyo.Objective(expr = 2*model.x[1] + 3*model.x[2]) - - model.Constraint1 = pyo.Constraint(expr = 3*model.x[1] + 4*model.x[2] <= -1) - + + model.x = pyo.Var([1, 2], domain=pyo.NonNegativeReals) + + model.OBJ = pyo.Objective(expr=2 * model.x[1] + 3 * model.x[2]) + + model.Constraint1 = pyo.Constraint(expr=3 * model.x[1] + 4 * model.x[2] <= -1) + return model + def problem_lp_unbounded(): - + model = pyo.ConcreteModel('lp_unbounded') - - model.x = pyo.Var([1,2], domain=pyo.NonNegativeReals) - - model.OBJ = pyo.Objective(expr = 2*model.x[1] + 3*model.x[2], - sense=pyo.maximize) - - model.Constraint1 = pyo.Constraint(expr = 3*model.x[1] + 4*model.x[2] >= 1) - + + model.x = pyo.Var([1, 2], domain=pyo.NonNegativeReals) + + model.OBJ = pyo.Objective(expr=2 * model.x[1] + 3 * model.x[2], sense=pyo.maximize) + + model.Constraint1 = pyo.Constraint(expr=3 * model.x[1] + 4 * model.x[2] >= 1) + return model + def problem_milp_optimal(): - + model = pyo.ConcreteModel('milp_optimal') - - model.x = pyo.Var([1,2], domain=pyo.Binary) - - model.OBJ = pyo.Objective(expr = 2.15*model.x[1] + 3.8*model.x[2]) - - model.Constraint1 = pyo.Constraint(expr = 3*model.x[1] + 4*model.x[2] >= 1) - + + model.x = pyo.Var([1, 2], domain=pyo.Binary) + + model.OBJ = pyo.Objective(expr=2.15 * model.x[1] + 3.8 * model.x[2]) + + model.Constraint1 = pyo.Constraint(expr=3 * model.x[1] + 4 * model.x[2] >= 1) + return model + def problem_milp_infeasible(): - + model = pyo.ConcreteModel('milp_infeasible') - - model.x = pyo.Var([1,2], domain=pyo.Binary) - - model.OBJ = pyo.Objective(expr = 2*model.x[1] + 3*model.x[2]) - - model.Constraint1 = pyo.Constraint(expr = 3*model.x[1] + 4*model.x[2] <= -1) - + + model.x = pyo.Var([1, 2], domain=pyo.Binary) + + model.OBJ = pyo.Objective(expr=2 * model.x[1] + 3 * model.x[2]) + + model.Constraint1 = pyo.Constraint(expr=3 * model.x[1] + 4 * model.x[2] <= -1) + return model + def problem_milp_unbounded(): - + model = pyo.ConcreteModel('milp_unbounded') - - model.x = pyo.Var([1,2], domain=pyo.NonNegativeReals) - + + model.x = pyo.Var([1, 2], domain=pyo.NonNegativeReals) + model.y = pyo.Var(domain=pyo.Binary) - - model.OBJ = pyo.Objective(expr = 2*model.x[1] + 3*model.x[2] + model.y, - sense=pyo.maximize) - - model.Constraint1 = pyo.Constraint(expr = 3*model.x[1] + 4*model.x[2] >= 1) - + + model.OBJ = pyo.Objective( + expr=2 * model.x[1] + 3 * model.x[2] + model.y, sense=pyo.maximize + ) + + model.Constraint1 = pyo.Constraint(expr=3 * model.x[1] + 4 * model.x[2] >= 1) + return model + def problem_milp_feasible(): - + model = pyo.ConcreteModel('milp_feasible') - + random.seed(6254) - + # a knapsack-type problem - - number_binary_variables = 40 # may need to be tweaked depending on specs - + + number_binary_variables = 40 # may need to be tweaked depending on specs + model.Y = pyo.RangeSet(number_binary_variables) - - model.y = pyo.Var(model.Y, - domain=pyo.Binary) - + + model.y = pyo.Var(model.Y, domain=pyo.Binary) + model.OBJ = pyo.Objective( - expr = sum(model.y[j]*random.random() - for j in model.Y), - sense=pyo.maximize - ) - + expr=sum(model.y[j] * random.random() for j in model.Y), sense=pyo.maximize + ) + model.Constraint1 = pyo.Constraint( - expr = sum(model.y[j]*random.random() - for j in model.Y) <= round(number_binary_variables/5) - ) - + expr=sum(model.y[j] * random.random() for j in model.Y) + <= round(number_binary_variables / 5) + ) + def rule_c1(m, i): return ( - sum(model.y[j]*(random.random()-0.5) + sum( + model.y[j] * (random.random() - 0.5) for j in model.Y if j != i - if random.randint(0,1) - ) <= round(number_binary_variables/5)*model.y[i] + if random.randint(0, 1) ) - model.constr_c1 = pyo.Constraint( - model.Y, - rule=rule_c1) - + <= round(number_binary_variables / 5) * model.y[i] + ) + + model.constr_c1 = pyo.Constraint(model.Y, rule=rule_c1) + return model -#****************************************************************************** -#****************************************************************************** + +# ****************************************************************************** +# ****************************************************************************** + @unittest.skipIf(not scip_available, "SCIP solver is not available.") def test_scip_some_more(): - + # list of problems - + list_concrete_models = [ problem_lp_unbounded(), problem_lp_infeasible(), @@ -183,81 +191,75 @@ def test_scip_some_more(): problem_milp_unbounded(), problem_milp_infeasible(), problem_milp_optimal(), - problem_milp_feasible() # may reach optimality depending on the budget - ] - + problem_milp_feasible(), # may reach optimality depending on the budget + ] + list_extra_data_expected = [ - (), # problem_lp_unbounded(), - (), # problem_lp_infeasible(), - ('Time', - 'Gap', - 'Primal bound', - 'Dual bound'), # problem_lp_optimal(), - (), # problem_milp_unbounded(), - (), # problem_milp_infeasible(), - ('Time', - 'Gap', - 'Primal bound', - 'Dual bound'), # problem_milp_optimal(), - ('Time', - 'Gap', - 'Primal bound', - 'Dual bound') # problem_milp_feasible() - ] - - #************************************************************************** - #************************************************************************** - + (), # problem_lp_unbounded(), + (), # problem_lp_infeasible(), + ('Time', 'Gap', 'Primal bound', 'Dual bound'), # problem_lp_optimal(), + (), # problem_milp_unbounded(), + (), # problem_milp_infeasible(), + ('Time', 'Gap', 'Primal bound', 'Dual bound'), # problem_milp_optimal(), + ('Time', 'Gap', 'Primal bound', 'Dual bound'), # problem_milp_feasible() + ] + + # ************************************************************************** + # ************************************************************************** + # solver settings - + solver_timelimit = 1 - + solver_abs_mip_gap = 0 - + solver_rel_mip_gap = 1e-6 - - #************************************************************************** - #************************************************************************** - + + # ************************************************************************** + # ************************************************************************** + for problem_index, problem in enumerate(list_concrete_models): - + print('******************************') print('******************************') - + print(problem.name) - + print('******************************') print('******************************') - - results, opt = optimise(problem, - solver_timelimit, - solver_rel_mip_gap, - solver_abs_mip_gap, - print_solver_output=True) - + + results, opt = optimise( + problem, + solver_timelimit, + solver_rel_mip_gap, + solver_abs_mip_gap, + print_solver_output=True, + ) + print(results) - + # check the version - + executable = opt._command.cmd[0] - + version = opt._known_versions[executable] - - if version < (8,0,0,0): - + + if version < (8, 0, 0, 0): + # if older and untested, skip tests - + continue - + # for each new attribute expected - + for log_file_attr in list_extra_data_expected[problem_index]: - + # check that it is part of the results object - + assert log_file_attr in results['Solver'][0] -#****************************************************************************** -#****************************************************************************** + +# ****************************************************************************** +# ****************************************************************************** # test_scip_some_more() # uncomment to run individually diff --git a/pyomo/solvers/tests/mip/test_scip_version.py b/pyomo/solvers/tests/mip/test_scip_version.py index 04f92df33ec..81450282365 100644 --- a/pyomo/solvers/tests/mip/test_scip_version.py +++ b/pyomo/solvers/tests/mip/test_scip_version.py @@ -27,25 +27,35 @@ currdir = this_file_dir() deleteFiles = True -@unittest.skipIf('pypy_version_info' in dir(sys), - "Skip SCIPAMPL tests on Pypy due to performance") -class Test(unittest.TestCase): +@unittest.skipIf( + 'pypy_version_info' in dir(sys), "Skip SCIPAMPL tests on Pypy due to performance" +) +class Test(unittest.TestCase): def setUp(self): scip = SolverFactory('scip', solver_io='nl') type(scip)._known_versions = {} TempfileManager.push() - self.patch_run = unittest.mock.patch('pyomo.solvers.plugins.solvers.SCIPAMPL.subprocess.run') + self.patch_run = unittest.mock.patch( + 'pyomo.solvers.plugins.solvers.SCIPAMPL.subprocess.run' + ) # Executable cannot be partially mocked since it creates a PathData object. - self.patch_path = unittest.mock.patch.object(pyomo.common.fileutils.PathData, 'path', autospec=True) - self.patch_available = unittest.mock.patch.object(pyomo.common.fileutils.PathData, 'available', autospec=True) + self.patch_path = unittest.mock.patch.object( + pyomo.common.fileutils.PathData, 'path', autospec=True + ) + self.patch_available = unittest.mock.patch.object( + pyomo.common.fileutils.PathData, 'available', autospec=True + ) self.run = self.patch_run.start() self.path = self.patch_path.start() self.available = self.patch_available.start() - self.executable_paths = {"scip": join(currdir, "scip"), "scipampl": join(currdir, "scipampl")} + self.executable_paths = { + "scip": join(currdir, "scip"), + "scipampl": join(currdir, "scipampl"), + } def tearDown(self): self.patch_run.stop() @@ -57,19 +67,23 @@ def tearDown(self): def generate_stdout(self, solver, version): if solver == "scip": # Template from SCIP 8.0.0 - stdout = "SCIP version {} [precision: 8 byte] [memory: block] [mode: optimized] [LP solver: SoPlex 6.0.0] [GitHash: d9b84b0709]\n"\ - "Copyright (C) 2002-2021 Konrad-Zuse-Zentrum fuer Informationstechnik Berlin (ZIB)\n"\ - "\n"\ - "External libraries:\n" \ - " SoPlex 6.0.0 Linear Programming Solver developed at Zuse Institute Berlin (soplex.zib.de) [GitHash: f5cfa86b]" + stdout = ( + "SCIP version {} [precision: 8 byte] [memory: block] [mode: optimized] [LP solver: SoPlex 6.0.0] [GitHash: d9b84b0709]\n" + "Copyright (C) 2002-2021 Konrad-Zuse-Zentrum fuer Informationstechnik Berlin (ZIB)\n" + "\n" + "External libraries:\n" + " SoPlex 6.0.0 Linear Programming Solver developed at Zuse Institute Berlin (soplex.zib.de) [GitHash: f5cfa86b]" + ) # Template from SCIPAMPL 7.0.3 elif solver == "scipampl": - stdout = "SCIP version {} [precision: 8 byte] [memory: block] [mode: optimized] [LP solver: SoPlex 5.0.2] [GitHash: 74c11e60cd]\n"\ - "Copyright (C) 2002-2021 Konrad-Zuse-Zentrum fuer Informationstechnik Berlin (ZIB)\n"\ - "\n"\ - "External libraries:\n"\ - " Readline 8.0 GNU library for command line editing (gnu.org/s/readline)" + stdout = ( + "SCIP version {} [precision: 8 byte] [memory: block] [mode: optimized] [LP solver: SoPlex 5.0.2] [GitHash: 74c11e60cd]\n" + "Copyright (C) 2002-2021 Konrad-Zuse-Zentrum fuer Informationstechnik Berlin (ZIB)\n" + "\n" + "External libraries:\n" + " Readline 8.0 GNU library for command line editing (gnu.org/s/readline)" + ) else: raise ValueError("Unsupported solver for stdout generation.") @@ -108,7 +122,9 @@ def run(args, **kwargs): if solver_version is None: raise FileNotFoundError() else: - return subprocess.CompletedProcess(args, 0, self.generate_stdout(solver_name, solver_version), None) + return subprocess.CompletedProcess( + args, 0, self.generate_stdout(solver_name, solver_version), None + ) if fail: self.fail("Solver creation looked up a non scip executable.") diff --git a/pyomo/solvers/tests/mip/test_solver.py b/pyomo/solvers/tests/mip/test_solver.py index 24d03b1f8a9..90a7076cbca 100644 --- a/pyomo/solvers/tests/mip/test_solver.py +++ b/pyomo/solvers/tests/mip/test_solver.py @@ -20,18 +20,17 @@ import pyomo.solvers.plugins.solvers from pyomo.solvers.plugins.solvers.CBCplugin import MockCBC -class MockSolver2(pyomo.opt.OptSolver): +class MockSolver2(pyomo.opt.OptSolver): def __init__(self, **kwds): kwds['type'] = 'stest_type' - pyomo.opt.OptSolver.__init__(self,**kwds) + pyomo.opt.OptSolver.__init__(self, **kwds) def enabled(self): return False class OptSolverDebug(unittest.TestCase): - def setUp(self): pyomo.opt.SolverFactory.register('stest2')(MockSolver2) diff --git a/pyomo/solvers/tests/models/LP_block.py b/pyomo/solvers/tests/models/LP_block.py index 9c817de168a..64c866faa9e 100644 --- a/pyomo/solvers/tests/models/LP_block.py +++ b/pyomo/solvers/tests/models/LP_block.py @@ -10,9 +10,18 @@ # ___________________________________________________________________________ import pyomo.kernel as pmo -from pyomo.core import ConcreteModel, Param, Var, Objective, Constraint, Block, NonNegativeReals +from pyomo.core import ( + ConcreteModel, + Param, + Var, + Objective, + Constraint, + Block, + NonNegativeReals, +) from pyomo.solvers.tests.models.base import _BaseTestModel, register_model + @register_model class LP_block(_BaseTestModel): """ @@ -24,7 +33,7 @@ class LP_block(_BaseTestModel): def __init__(self): _BaseTestModel.__init__(self) - self.add_results(self.description+".json") + self.add_results(self.description + ".json") def _generate_model(self): self.model = ConcreteModel() @@ -32,16 +41,16 @@ def _generate_model(self): model._name = self.description model.b = Block() - model.B = Block([1,2,3]) + model.B = Block([1, 2, 3]) model.a = Param(initialize=1.0, mutable=True) model.b.x = Var(within=NonNegativeReals) model.B[1].x = Var(within=NonNegativeReals) - model.obj = Objective(expr=model.b.x + 3.0*model.B[1].x) + model.obj = Objective(expr=model.b.x + 3.0 * model.B[1].x) model.obj.deactivate() model.B[2].c = Constraint(expr=-model.B[1].x <= -model.a) - model.B[2].obj = Objective(expr=model.b.x + 3.0*model.B[1].x + 2) - model.B[3].c = Constraint(expr=(2.0, model.b.x/model.a - model.B[1].x, 10)) + model.B[2].obj = Objective(expr=model.b.x + 3.0 * model.B[1].x + 2) + model.B[3].c = Constraint(expr=(2.0, model.b.x / model.a - model.B[1].x, 10)) def warmstart_model(self): assert self.model is not None @@ -49,24 +58,24 @@ def warmstart_model(self): model.b.x.value = 1.0 model.B[1].x.value = 1.0 + @register_model class LP_block_kernel(LP_block): - def _generate_model(self): self.model = pmo.block() model = self.model model._name = self.description model.b = pmo.block() - model.B = pmo.block_dict((i, pmo.block()) - for i in range(1,4)) + model.B = pmo.block_dict((i, pmo.block()) for i in range(1, 4)) model.a = pmo.parameter(value=1.0) model.b.x = pmo.variable(lb=0) model.B[1].x = pmo.variable(lb=0) - model.obj = pmo.objective(expr=model.b.x + 3.0*model.B[1].x) + model.obj = pmo.objective(expr=model.b.x + 3.0 * model.B[1].x) model.obj.deactivate() model.B[2].c = pmo.constraint(expr=-model.B[1].x <= -model.a) - model.B[2].obj = pmo.objective(expr=model.b.x + 3.0*model.B[1].x + 2) - model.B[3].c = pmo.constraint(expr=(2.0, model.b.x/model.a - model.B[1].x, 10)) - + model.B[2].obj = pmo.objective(expr=model.b.x + 3.0 * model.B[1].x + 2) + model.B[3].c = pmo.constraint( + expr=(2.0, model.b.x / model.a - model.B[1].x, 10) + ) diff --git a/pyomo/solvers/tests/models/LP_compiled.py b/pyomo/solvers/tests/models/LP_compiled.py index 36011fdad1f..a3d6dabcd1d 100644 --- a/pyomo/solvers/tests/models/LP_compiled.py +++ b/pyomo/solvers/tests/models/LP_compiled.py @@ -10,13 +10,21 @@ # ___________________________________________________________________________ import pyomo.kernel as pmo -from pyomo.core import ConcreteModel, Var, Objective, Constraint, RangeSet, ConstraintList +from pyomo.core import ( + ConcreteModel, + Var, + Objective, + Constraint, + RangeSet, + ConstraintList, +) from pyomo.solvers.tests.models.base import _BaseTestModel, register_model from pyomo.repn.beta.matrix import compile_block_linear_constraints has_numpy = False try: import numpy + has_numpy = True except: pass @@ -25,10 +33,12 @@ try: import scipy import scipy.sparse + has_scipy = True except: pass + @register_model class LP_compiled(_BaseTestModel): """ @@ -43,7 +53,7 @@ class LP_compiled(_BaseTestModel): def __init__(self): _BaseTestModel.__init__(self) - self.add_results(self.description+".json") + self.add_results(self.description + ".json") self.disable_suffix_tests = True def _generate_model(self): @@ -51,28 +61,29 @@ def _generate_model(self): model = self.model model._name = self.description - model.s = RangeSet(1,12) + model.s = RangeSet(1, 12) model.x = Var(model.s) model.x[1].setlb(-1) model.x[1].setub(1) model.x[2].setlb(-1) model.x[2].setub(1) - model.obj = Objective(expr=sum(model.x[i]*((-1)**(i+1)) - for i in model.x.index_set())) + model.obj = Objective( + expr=sum(model.x[i] * ((-1) ** (i + 1)) for i in model.x.index_set()) + ) model.c = ConstraintList() # to make the variable used in the constraint match the name model.c.add(Constraint.Skip) model.c.add(Constraint.Skip) - model.c.add(model.x[3]>=-1.) - model.c.add(model.x[4]<=1.) - model.c.add(model.x[5]==-1.) - model.c.add(model.x[6]==-1.) - model.c.add(model.x[7]==1.) - model.c.add(model.x[8]==1.) - model.c.add((-1.,model.x[9],-1.)) - model.c.add((-1.,model.x[10],-1.)) - model.c.add((1.,model.x[11],1.)) - model.c.add((1.,model.x[12],1.)) + model.c.add(model.x[3] >= -1.0) + model.c.add(model.x[4] <= 1.0) + model.c.add(model.x[5] == -1.0) + model.c.add(model.x[6] == -1.0) + model.c.add(model.x[7] == 1.0) + model.c.add(model.x[8] == 1.0) + model.c.add((-1.0, model.x[9], -1.0)) + model.c.add((-1.0, model.x[10], -1.0)) + model.c.add((1.0, model.x[11], 1.0)) + model.c.add((1.0, model.x[12], 1.0)) cdata = model.c.add((0, 1, 3)) assert cdata.lower == 0 assert cdata.upper == 3 @@ -93,7 +104,7 @@ def _generate_model(self): assert cdata.upper == 1 assert cdata.body() == 0 assert not cdata.equality - cdata = model.c.add((1,1)) + cdata = model.c.add((1, 1)) assert cdata.lower == 1 assert cdata.upper == 1 assert cdata.body() == 1 @@ -101,7 +112,7 @@ def _generate_model(self): model.fixed_var = Var() model.fixed_var.fix(1.0) - cdata = model.c.add((0, 1+model.fixed_var, 3)) + cdata = model.c.add((0, 1 + model.fixed_var, 3)) cdata = model.c.add((0, 2 + model.fixed_var, 3)) cdata = model.c.add((0, model.fixed_var, None)) cdata = model.c.add((None, model.fixed_var, 1)) @@ -111,8 +122,8 @@ def _generate_model(self): # to make the variable used in the constraint match the name model.c_inactive.add(Constraint.Skip) model.c_inactive.add(Constraint.Skip) - model.c_inactive.add(model.x[3]>=-2.) - model.c_inactive.add(model.x[4]<=2.) + model.c_inactive.add(model.x[3] >= -2.0) + model.c_inactive.add(model.x[4] <= 2.0) compile_block_linear_constraints(model, 'Amatrix') @@ -122,54 +133,95 @@ def warmstart_model(self): for i in model.s: model.x[i].value = None + if has_numpy and has_scipy: # TODO: we need to somehow label this as a skip rather # than not defining the test class @register_model class LP_compiled_dense_kernel(LP_compiled): - def _get_dense_data(self): assert has_numpy and has_scipy A = numpy.array( - [[1., 0., 0., 0., 0., 0., 0., 0., 0., 0.], - [0., 1., 0., 0., 0., 0., 0., 0., 0., 0.], - [0., 0., 1., 0., 0., 0., 0., 0., 0., 0.], - [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.], - [0., 0., 0., 0., 1., 0., 0., 0., 0., 0.], - [0., 0., 0., 0., 0., 1., 0., 0., 0., 0.], - [0., 0., 0., 0., 0., 0., 1., 0., 0., 0.], - [0., 0., 0., 0., 0., 0., 0., 1., 0., 0.], - [0., 0., 0., 0., 0., 0., 0., 0., 1., 0.], - [0., 0., 0., 0., 0., 0., 0., 0., 0., 1.], - [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], - [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], - [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], - [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], - [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], - [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], - [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], - [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], - [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], - [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], - [1., 0., 0., 0., 0., 0., 0., 0., 0., 0.], - [0., 1., 0., 0., 0., 0., 0., 0., 0., 0.]], - dtype=float) + [ + [1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + ], + dtype=float, + ) lb = numpy.array( - [-1.0, -numpy.inf, -1.0, -1.0, - 1.0, 1.0, -1.0, -1.0, - 1.0, 1.0, -1.0, -2.0, - -1.0, -numpy.inf, 0.0, -2.0, - -3.0, -1.0, -numpy.inf, 0.0, - -2.0, -numpy.inf]) - ub = numpy.array([ - numpy.inf, 1.0, -1.0, -1.0, - 1.0, 1.0, -1.0, -1.0, - 1.0, 1.0, 2.0, 1.0, - numpy.inf, 1.0, 0.0, 1.0, - 0.0, numpy.inf, 0.0, 0.0, - numpy.inf, 2.0]) - eq_index = [2,3,4,5,14,19] + [ + -1.0, + -numpy.inf, + -1.0, + -1.0, + 1.0, + 1.0, + -1.0, + -1.0, + 1.0, + 1.0, + -1.0, + -2.0, + -1.0, + -numpy.inf, + 0.0, + -2.0, + -3.0, + -1.0, + -numpy.inf, + 0.0, + -2.0, + -numpy.inf, + ] + ) + ub = numpy.array( + [ + numpy.inf, + 1.0, + -1.0, + -1.0, + 1.0, + 1.0, + -1.0, + -1.0, + 1.0, + 1.0, + 2.0, + 1.0, + numpy.inf, + 1.0, + 0.0, + 1.0, + 0.0, + numpy.inf, + 0.0, + 0.0, + numpy.inf, + 2.0, + ] + ) + eq_index = [2, 3, 4, 5, 14, 19] return A, lb, ub, eq_index @@ -179,15 +231,15 @@ def _generate_base_model(self): model = self.model model._name = self.description - model.s = list(range(1,13)) - model.x = pmo.variable_dict( - ((i, pmo.variable()) for i in model.s)) + model.s = list(range(1, 13)) + model.x = pmo.variable_dict(((i, pmo.variable()) for i in model.s)) model.x[1].lb = -1 model.x[1].ub = 1 model.x[2].lb = -1 model.x[2].ub = 1 - model.obj = pmo.objective(expr=sum(model.x[i]*((-1)**(i+1)) - for i in model.s)) + model.obj = pmo.objective( + expr=sum(model.x[i] * ((-1) ** (i + 1)) for i in model.s) + ) variable_order = [ model.x[3], model.x[4], @@ -198,7 +250,8 @@ def _generate_base_model(self): model.x[9], model.x[10], model.x[11], - model.x[12]] + model.x[12], + ] return variable_order @@ -206,26 +259,18 @@ def _generate_model(self): x = self._generate_base_model() model = self.model A, lb, ub, eq_index = self._get_dense_data() - model.Amatrix = pmo.matrix_constraint( - A, lb=lb, ub=ub, x=x, sparse=False) + model.Amatrix = pmo.matrix_constraint(A, lb=lb, ub=ub, x=x, sparse=False) for i in eq_index: - assert model.Amatrix[i].lb == \ - model.Amatrix[i].ub - model.Amatrix[i].rhs = \ - model.Amatrix[i].lb + assert model.Amatrix[i].lb == model.Amatrix[i].ub + model.Amatrix[i].rhs = model.Amatrix[i].lb @register_model - class LP_compiled_sparse_kernel( - LP_compiled_dense_kernel): - + class LP_compiled_sparse_kernel(LP_compiled_dense_kernel): def _generate_model(self): x = self._generate_base_model() model = self.model A, lb, ub, eq_index = self._get_dense_data() - model.Amatrix = pmo.matrix_constraint( - A, lb=lb, ub=ub, x=x, sparse=True) + model.Amatrix = pmo.matrix_constraint(A, lb=lb, ub=ub, x=x, sparse=True) for i in eq_index: - assert model.Amatrix[i].lb == \ - model.Amatrix[i].ub - model.Amatrix[i].rhs = \ - model.Amatrix[i].lb + assert model.Amatrix[i].lb == model.Amatrix[i].ub + model.Amatrix[i].rhs = model.Amatrix[i].lb diff --git a/pyomo/solvers/tests/models/LP_constant_objective1.py b/pyomo/solvers/tests/models/LP_constant_objective1.py index 2c46ac9bc6a..306a7a867a2 100644 --- a/pyomo/solvers/tests/models/LP_constant_objective1.py +++ b/pyomo/solvers/tests/models/LP_constant_objective1.py @@ -13,6 +13,7 @@ from pyomo.core import ConcreteModel, Var, Objective, Constraint, NonNegativeReals from pyomo.solvers.tests.models.base import _BaseTestModel, register_model + @register_model class LP_constant_objective1(_BaseTestModel): """ @@ -24,7 +25,7 @@ class LP_constant_objective1(_BaseTestModel): def __init__(self): _BaseTestModel.__init__(self) - self.add_results(self.description+".json") + self.add_results(self.description + ".json") def _generate_model(self): self.model = ConcreteModel() @@ -40,9 +41,9 @@ def warmstart_model(self): model = self.model model.x.value = None + @register_model class LP_constant_objective1_kernel(LP_constant_objective1): - def _generate_model(self): self.model = pmo.block() model = self.model @@ -50,5 +51,4 @@ def _generate_model(self): model.x = pmo.variable(domain=NonNegativeReals) model.obj = pmo.objective(0.0) - model.con = pmo.linear_constraint(terms=[(model.x,1.0)], - rhs=1.0) + model.con = pmo.linear_constraint(terms=[(model.x, 1.0)], rhs=1.0) diff --git a/pyomo/solvers/tests/models/LP_constant_objective2.py b/pyomo/solvers/tests/models/LP_constant_objective2.py index 4d5512547ec..17da01bf209 100644 --- a/pyomo/solvers/tests/models/LP_constant_objective2.py +++ b/pyomo/solvers/tests/models/LP_constant_objective2.py @@ -13,6 +13,7 @@ from pyomo.core import ConcreteModel, Var, Objective, Constraint, NonNegativeReals from pyomo.solvers.tests.models.base import _BaseTestModel, register_model + @register_model class LP_constant_objective2(_BaseTestModel): """ @@ -25,7 +26,7 @@ class LP_constant_objective2(_BaseTestModel): def __init__(self): _BaseTestModel.__init__(self) - self.add_results(self.description+".json") + self.add_results(self.description + ".json") def _generate_model(self): self.model = ConcreteModel() @@ -33,7 +34,7 @@ def _generate_model(self): model._name = self.description model.x = Var(within=NonNegativeReals) - model.obj = Objective(expr=model.x-model.x) + model.obj = Objective(expr=model.x - model.x) model.con = Constraint(expr=model.x == 1.0) def warmstart_model(self): @@ -41,14 +42,14 @@ def warmstart_model(self): model = self.model model.x.value = 1.0 + @register_model class LP_constant_objective2_kernel(LP_constant_objective2): - def _generate_model(self): self.model = pmo.block() model = self.model model._name = self.description model.x = pmo.variable(domain=NonNegativeReals) - model.obj = pmo.objective(model.x-model.x) + model.obj = pmo.objective(model.x - model.x) model.con = pmo.constraint(model.x == 1.0) diff --git a/pyomo/solvers/tests/models/LP_duals_maximize.py b/pyomo/solvers/tests/models/LP_duals_maximize.py index abc13aabb1e..61d827daa62 100644 --- a/pyomo/solvers/tests/models/LP_duals_maximize.py +++ b/pyomo/solvers/tests/models/LP_duals_maximize.py @@ -10,9 +10,19 @@ # ___________________________________________________________________________ import pyomo.kernel as pmo -from pyomo.core import ConcreteModel, Param, Var, Objective, Constraint, RangeSet, maximize, ConstraintList +from pyomo.core import ( + ConcreteModel, + Param, + Var, + Objective, + Constraint, + RangeSet, + maximize, + ConstraintList, +) from pyomo.solvers.tests.models.base import _BaseTestModel, register_model + @register_model class LP_duals_maximize(_BaseTestModel): """ @@ -28,7 +38,7 @@ class LP_duals_maximize(_BaseTestModel): def __init__(self): _BaseTestModel.__init__(self) - self.add_results(self.description+".json") + self.add_results(self.description + ".json") def _generate_model(self): self.model = ConcreteModel() @@ -38,36 +48,37 @@ def _generate_model(self): model.neg1 = Param(initialize=-1.0, mutable=True) model.pos1 = Param(initialize=1.0, mutable=True) - model.s = RangeSet(1,12) + model.s = RangeSet(1, 12) model.x = Var(model.s) model.x[1].setlb(model.neg1) model.x[1].setub(model.pos1) model.x[2].setlb(model.neg1) model.x[2].setub(model.pos1) - model.obj = Objective(expr=sum(model.x[i]*((-1)**(i)) - for i in model.x.index_set()), - sense=maximize) + model.obj = Objective( + expr=sum(model.x[i] * ((-1) ** (i)) for i in model.x.index_set()), + sense=maximize, + ) model.c = ConstraintList() # to make the variable used in the constraint match the name model.c.add(Constraint.Skip) model.c.add(Constraint.Skip) - model.c.add(model.x[3]>=-1.) - model.c.add(model.x[4]<=1.) - model.c.add(model.x[5]==-1.) - model.c.add(model.x[6]==-1.) - model.c.add(model.x[7]==1.) - model.c.add(model.x[8]==1.) - model.c.add((model.neg1,model.x[9],model.neg1)) - model.c.add((-1.,model.x[10],-1.)) - model.c.add((1.,model.x[11],1.)) - model.c.add((1.,model.x[12],1.)) + model.c.add(model.x[3] >= -1.0) + model.c.add(model.x[4] <= 1.0) + model.c.add(model.x[5] == -1.0) + model.c.add(model.x[6] == -1.0) + model.c.add(model.x[7] == 1.0) + model.c.add(model.x[8] == 1.0) + model.c.add((model.neg1, model.x[9], model.neg1)) + model.c.add((-1.0, model.x[10], -1.0)) + model.c.add((1.0, model.x[11], 1.0)) + model.c.add((1.0, model.x[12], 1.0)) model.c_inactive = ConstraintList() # to make the variable used in the constraint match the name model.c_inactive.add(Constraint.Skip) model.c_inactive.add(Constraint.Skip) - model.c_inactive.add(model.x[3]>=-2.) - model.c_inactive.add(model.x[4]<=2.) + model.c_inactive.add(model.x[3] >= -2.0) + model.c_inactive.add(model.x[4] <= 2.0) def warmstart_model(self): assert self.model is not None @@ -75,9 +86,9 @@ def warmstart_model(self): for i in model.s: model.x[i].value = None + @register_model class LP_duals_maximize_kernel(LP_duals_maximize): - def _generate_model(self): self.model = pmo.block() model = self.model @@ -86,29 +97,28 @@ def _generate_model(self): model.neg1 = pmo.parameter(value=-1.0) model.pos1 = pmo.parameter(value=1.0) - model.s = list(range(1,13)) - model.x = pmo.variable_dict( - (i, pmo.variable()) for i in model.s) + model.s = list(range(1, 13)) + model.x = pmo.variable_dict((i, pmo.variable()) for i in model.s) model.x[1].lb = model.neg1 model.x[1].ub = model.pos1 model.x[2].lb = model.neg1 model.x[2].ub = model.pos1 - model.obj = pmo.objective(sum(model.x[i]*((-1)**(i)) - for i in model.s), - sense=pmo.maximize) + model.obj = pmo.objective( + sum(model.x[i] * ((-1) ** (i)) for i in model.s), sense=pmo.maximize + ) model.c = pmo.constraint_dict() - model.c[3] = pmo.constraint(model.x[3]>=-1.) - model.c[4] = pmo.constraint(model.x[4]<=1.) - model.c[5] = pmo.constraint(model.x[5]==-1.) - model.c[6] = pmo.constraint(model.x[6]==-1.) - model.c[7] = pmo.constraint(model.x[7]==1.) - model.c[8] = pmo.constraint(model.x[8]==1.) - model.c[9] = pmo.constraint((model.neg1,model.x[9],model.neg1)) - model.c[10] = pmo.constraint((-1.,model.x[10],-1.)) - model.c[11] = pmo.constraint((1.,model.x[11],1.)) - model.c[12] = pmo.constraint((1.,model.x[12],1.)) + model.c[3] = pmo.constraint(model.x[3] >= -1.0) + model.c[4] = pmo.constraint(model.x[4] <= 1.0) + model.c[5] = pmo.constraint(model.x[5] == -1.0) + model.c[6] = pmo.constraint(model.x[6] == -1.0) + model.c[7] = pmo.constraint(model.x[7] == 1.0) + model.c[8] = pmo.constraint(model.x[8] == 1.0) + model.c[9] = pmo.constraint((model.neg1, model.x[9], model.neg1)) + model.c[10] = pmo.constraint((-1.0, model.x[10], -1.0)) + model.c[11] = pmo.constraint((1.0, model.x[11], 1.0)) + model.c[12] = pmo.constraint((1.0, model.x[12], 1.0)) model.c_inactive = pmo.constraint_dict() # to make the variable used in the constraint match the name - model.c_inactive[3] = pmo.constraint(model.x[3]>=-2.) - model.c_inactive[4] = pmo.constraint(model.x[4]<=2.) + model.c_inactive[3] = pmo.constraint(model.x[3] >= -2.0) + model.c_inactive[4] = pmo.constraint(model.x[4] <= 2.0) diff --git a/pyomo/solvers/tests/models/LP_duals_minimize.py b/pyomo/solvers/tests/models/LP_duals_minimize.py index b150b68bbef..77471d0182c 100644 --- a/pyomo/solvers/tests/models/LP_duals_minimize.py +++ b/pyomo/solvers/tests/models/LP_duals_minimize.py @@ -10,9 +10,17 @@ # ___________________________________________________________________________ import pyomo.kernel as pmo -from pyomo.core import ConcreteModel, Var, Objective, Constraint, RangeSet, ConstraintList +from pyomo.core import ( + ConcreteModel, + Var, + Objective, + Constraint, + RangeSet, + ConstraintList, +) from pyomo.solvers.tests.models.base import _BaseTestModel, register_model + @register_model class LP_duals_minimize(_BaseTestModel): """ @@ -28,7 +36,7 @@ class LP_duals_minimize(_BaseTestModel): def __init__(self): _BaseTestModel.__init__(self) - self.add_results(self.description+".json") + self.add_results(self.description + ".json") def _generate_model(self): self.model = None @@ -36,35 +44,36 @@ def _generate_model(self): model = self.model model._name = self.description - model.s = RangeSet(1,12) + model.s = RangeSet(1, 12) model.x = Var(model.s) model.x[1].setlb(-1) model.x[1].setub(1) model.x[2].setlb(-1) model.x[2].setub(1) - model.obj = Objective(expr=sum(model.x[i]*((-1)**(i+1)) - for i in model.x.index_set())) + model.obj = Objective( + expr=sum(model.x[i] * ((-1) ** (i + 1)) for i in model.x.index_set()) + ) model.c = ConstraintList() # to make the variable used in the constraint match the name model.c.add(Constraint.Skip) model.c.add(Constraint.Skip) - model.c.add(model.x[3]>=-1.) - model.c.add(model.x[4]<=1.) - model.c.add(model.x[5]==-1.) - model.c.add(model.x[6]==-1.) - model.c.add(model.x[7]==1.) - model.c.add(model.x[8]==1.) - model.c.add((-1.,model.x[9],-1.)) - model.c.add((-1.,model.x[10],-1.)) - model.c.add((1.,model.x[11],1.)) - model.c.add((1.,model.x[12],1.)) + model.c.add(model.x[3] >= -1.0) + model.c.add(model.x[4] <= 1.0) + model.c.add(model.x[5] == -1.0) + model.c.add(model.x[6] == -1.0) + model.c.add(model.x[7] == 1.0) + model.c.add(model.x[8] == 1.0) + model.c.add((-1.0, model.x[9], -1.0)) + model.c.add((-1.0, model.x[10], -1.0)) + model.c.add((1.0, model.x[11], 1.0)) + model.c.add((1.0, model.x[12], 1.0)) model.c_inactive = ConstraintList() # to make the variable used in the constraint match the name model.c_inactive.add(Constraint.Skip) model.c_inactive.add(Constraint.Skip) - model.c_inactive.add(model.x[3]>=-2.) - model.c_inactive.add(model.x[4]<=2.) + model.c_inactive.add(model.x[3] >= -2.0) + model.c_inactive.add(model.x[4] <= 2.0) def warmstart_model(self): assert self.model is not None @@ -72,38 +81,36 @@ def warmstart_model(self): for i in model.s: model.x[i].value = None + @register_model class LP_duals_minimize_kernel(LP_duals_minimize): - def _generate_model(self): self.model = None self.model = pmo.block() model = self.model model._name = self.description - model.s = list(range(1,13)) - model.x = pmo.variable_dict( - (i, pmo.variable()) for i in model.s) + model.s = list(range(1, 13)) + model.x = pmo.variable_dict((i, pmo.variable()) for i in model.s) model.x[1].lb = -1 model.x[1].ub = 1 model.x[2].lb = -1 model.x[2].ub = 1 - model.obj = pmo.objective(sum(model.x[i]*((-1)**(i+1)) - for i in model.s)) + model.obj = pmo.objective(sum(model.x[i] * ((-1) ** (i + 1)) for i in model.s)) model.c = pmo.constraint_dict() # to make the variable used in the constraint match the name - model.c[3] = pmo.constraint(model.x[3]>=-1.) - model.c[4] = pmo.constraint(model.x[4]<=1.) - model.c[5] = pmo.constraint(model.x[5]==-1.) - model.c[6] = pmo.constraint(model.x[6]==-1.) - model.c[7] = pmo.constraint(model.x[7]==1.) - model.c[8] = pmo.constraint(model.x[8]==1.) - model.c[9] = pmo.constraint((-1.,model.x[9],-1.)) - model.c[10] = pmo.constraint((-1.,model.x[10],-1.)) - model.c[11] = pmo.constraint((1.,model.x[11],1.)) - model.c[12] = pmo.constraint((1.,model.x[12],1.)) + model.c[3] = pmo.constraint(model.x[3] >= -1.0) + model.c[4] = pmo.constraint(model.x[4] <= 1.0) + model.c[5] = pmo.constraint(model.x[5] == -1.0) + model.c[6] = pmo.constraint(model.x[6] == -1.0) + model.c[7] = pmo.constraint(model.x[7] == 1.0) + model.c[8] = pmo.constraint(model.x[8] == 1.0) + model.c[9] = pmo.constraint((-1.0, model.x[9], -1.0)) + model.c[10] = pmo.constraint((-1.0, model.x[10], -1.0)) + model.c[11] = pmo.constraint((1.0, model.x[11], 1.0)) + model.c[12] = pmo.constraint((1.0, model.x[12], 1.0)) model.c_inactive = pmo.constraint_dict() # to make the variable used in the constraint match the name - model.c_inactive[3] = pmo.constraint(model.x[3]>=-2.) - model.c_inactive[4] = pmo.constraint(model.x[4]<=2.) + model.c_inactive[3] = pmo.constraint(model.x[3] >= -2.0) + model.c_inactive[4] = pmo.constraint(model.x[4] <= 2.0) diff --git a/pyomo/solvers/tests/models/LP_inactive_index.py b/pyomo/solvers/tests/models/LP_inactive_index.py index b269c4e31ff..d3fdd5b32ca 100644 --- a/pyomo/solvers/tests/models/LP_inactive_index.py +++ b/pyomo/solvers/tests/models/LP_inactive_index.py @@ -10,21 +10,32 @@ # ___________________________________________________________________________ import pyomo.kernel as pmo -from pyomo.core import ConcreteModel, Var, Objective, Constraint, Set, ConstraintList, Block +from pyomo.core import ( + ConcreteModel, + Var, + Objective, + Constraint, + Set, + ConstraintList, + Block, +) from pyomo.solvers.tests.models.base import _BaseTestModel, register_model -def inactive_index_LP_obj_rule(model,i): + +def inactive_index_LP_obj_rule(model, i): if i == 1: - return model.x-model.y + return model.x - model.y else: - return -model.x+model.y+model.z + return -model.x + model.y + model.z + -def inactive_index_LP_c2_rule(model,i): +def inactive_index_LP_c2_rule(model, i): if i == 1: return model.y >= -2 else: return model.x <= 2 + @register_model class LP_inactive_index(_BaseTestModel): """ @@ -36,32 +47,30 @@ class LP_inactive_index(_BaseTestModel): def __init__(self): _BaseTestModel.__init__(self) - self.add_results(self.description+".json") + self.add_results(self.description + ".json") def _generate_model(self): self.model = ConcreteModel() model = self.model model._name = self.description - model.s = Set(initialize=[1,2]) + model.s = Set(initialize=[1, 2]) model.x = Var() model.y = Var() - model.z = Var(bounds=(0,None)) + model.z = Var(bounds=(0, None)) - model.obj = Objective(model.s, - rule=inactive_index_LP_obj_rule) - model.OBJ = Objective(expr=model.x+model.y) + model.obj = Objective(model.s, rule=inactive_index_LP_obj_rule) + model.OBJ = Objective(expr=model.x + model.y) model.obj[1].deactivate() model.OBJ.deactivate() model.c1 = ConstraintList() - model.c1.add(model.x<=1) # index=1 - model.c1.add(model.x>=-1) # index=2 - model.c1.add(model.y<=1) # index=3 - model.c1.add(model.y>=-1) # index=4 + model.c1.add(model.x <= 1) # index=1 + model.c1.add(model.x >= -1) # index=2 + model.c1.add(model.y <= 1) # index=3 + model.c1.add(model.y >= -1) # index=4 model.c1[1].deactivate() model.c1[4].deactivate() - model.c2 = Constraint(model.s, - rule=inactive_index_LP_c2_rule) + model.c2 = Constraint(model.s, rule=inactive_index_LP_c2_rule) model.b = Block() model.b.c = Constraint(expr=model.z >= 2) @@ -80,38 +89,36 @@ def warmstart_model(self): model.y.value = None model.z.value = 2.0 + @register_model class LP_inactive_index_kernel(LP_inactive_index): - def _generate_model(self): self.model = pmo.block() model = self.model model._name = self.description - model.s = [1,2] + model.s = [1, 2] model.x = pmo.variable() model.y = pmo.variable() model.z = pmo.variable(lb=0) model.obj = pmo.objective_dict() for i in model.s: - model.obj[i] = pmo.objective( - inactive_index_LP_obj_rule(model,i)) + model.obj[i] = pmo.objective(inactive_index_LP_obj_rule(model, i)) - model.OBJ = pmo.objective(model.x+model.y) + model.OBJ = pmo.objective(model.x + model.y) model.obj[1].deactivate() model.OBJ.deactivate() model.c1 = pmo.constraint_dict() - model.c1[1] = pmo.constraint(model.x<=1) - model.c1[2] = pmo.constraint(model.x>=-1) - model.c1[3] = pmo.constraint(model.y<=1) - model.c1[4] = pmo.constraint(model.y>=-1) + model.c1[1] = pmo.constraint(model.x <= 1) + model.c1[2] = pmo.constraint(model.x >= -1) + model.c1[3] = pmo.constraint(model.y <= 1) + model.c1[4] = pmo.constraint(model.y >= -1) model.c1[1].deactivate() model.c1[4].deactivate() model.c2 = pmo.constraint_dict() for i in model.s: - model.c2[i] = pmo.constraint( - inactive_index_LP_c2_rule(model, i)) + model.c2[i] = pmo.constraint(inactive_index_LP_c2_rule(model, i)) model.b = pmo.block() model.b.c = pmo.constraint(model.z >= 2) diff --git a/pyomo/solvers/tests/models/LP_infeasible1.py b/pyomo/solvers/tests/models/LP_infeasible1.py index d0801bebbc3..28243574a37 100644 --- a/pyomo/solvers/tests/models/LP_infeasible1.py +++ b/pyomo/solvers/tests/models/LP_infeasible1.py @@ -14,6 +14,7 @@ from pyomo.opt import TerminationCondition from pyomo.solvers.tests.models.base import _BaseTestModel, register_model + @register_model class LP_infeasible1(_BaseTestModel): """ @@ -27,17 +28,17 @@ class LP_infeasible1(_BaseTestModel): def __init__(self): _BaseTestModel.__init__(self) self.solve_should_fail = True - self.add_results(self.description+".json") + self.add_results(self.description + ".json") def _generate_model(self): self.model = ConcreteModel() model = self.model model._name = self.description - model.x = Var(bounds=(1,None)) - model.y = Var(bounds=(1,None)) - model.o = Objective(expr=model.x+model.y) - model.c = Constraint(expr=model.x+model.y <= 0) + model.x = Var(bounds=(1, None)) + model.y = Var(bounds=(1, None)) + model.o = Objective(expr=model.x + model.y) + model.c = Constraint(expr=model.x + model.y <= 0) def warmstart_model(self): assert self.model is not None @@ -47,15 +48,19 @@ def warmstart_model(self): def post_solve_test_validation(self, tester, results): if tester is None: - assert results['Solver'][0]['termination condition'] == \ - TerminationCondition.infeasible + assert ( + results['Solver'][0]['termination condition'] + == TerminationCondition.infeasible + ) else: - tester.assertEqual(results['Solver'][0]['termination condition'], - TerminationCondition.infeasible) + tester.assertEqual( + results['Solver'][0]['termination condition'], + TerminationCondition.infeasible, + ) + @register_model class LP_infeasible1_kernel(LP_infeasible1): - def _generate_model(self): self.model = pmo.block() model = self.model @@ -63,5 +68,5 @@ def _generate_model(self): model.x = pmo.variable(lb=1) model.y = pmo.variable(lb=1) - model.o = pmo.objective(model.x+model.y) - model.c = pmo.constraint(model.x+model.y <= 0) + model.o = pmo.objective(model.x + model.y) + model.c = pmo.constraint(model.x + model.y <= 0) diff --git a/pyomo/solvers/tests/models/LP_infeasible2.py b/pyomo/solvers/tests/models/LP_infeasible2.py index 9550026f1f3..383267c0e3c 100644 --- a/pyomo/solvers/tests/models/LP_infeasible2.py +++ b/pyomo/solvers/tests/models/LP_infeasible2.py @@ -14,6 +14,7 @@ from pyomo.opt import TerminationCondition from pyomo.solvers.tests.models.base import _BaseTestModel, register_model + @register_model class LP_infeasible2(_BaseTestModel): """ @@ -27,17 +28,17 @@ class LP_infeasible2(_BaseTestModel): def __init__(self): _BaseTestModel.__init__(self) self.solve_should_fail = True - self.add_results(self.description+".json") + self.add_results(self.description + ".json") def _generate_model(self): self.model = ConcreteModel() model = self.model model._name = self.description - model.x = Var(bounds=(1,None)) - model.y = Var(bounds=(1,None)) - model.o = Objective(expr=-model.x-model.y, sense=maximize) - model.c = Constraint(expr=model.x+model.y <= 0) + model.x = Var(bounds=(1, None)) + model.y = Var(bounds=(1, None)) + model.o = Objective(expr=-model.x - model.y, sense=maximize) + model.c = Constraint(expr=model.x + model.y <= 0) def warmstart_model(self): assert self.model is not None @@ -47,15 +48,19 @@ def warmstart_model(self): def post_solve_test_validation(self, tester, results): if tester is None: - assert results['Solver'][0]['termination condition'] == \ - TerminationCondition.infeasible + assert ( + results['Solver'][0]['termination condition'] + == TerminationCondition.infeasible + ) else: - tester.assertEqual(results['Solver'][0]['termination condition'], - TerminationCondition.infeasible) + tester.assertEqual( + results['Solver'][0]['termination condition'], + TerminationCondition.infeasible, + ) + @register_model class LP_infeasible2_kernel(LP_infeasible2): - def _generate_model(self): self.model = pmo.block() model = self.model @@ -63,5 +68,5 @@ def _generate_model(self): model.x = pmo.variable(lb=1) model.y = pmo.variable(lb=1) - model.o = pmo.objective(-model.x-model.y, sense=pmo.maximize) - model.c = pmo.constraint(model.x+model.y <= 0) + model.o = pmo.objective(-model.x - model.y, sense=pmo.maximize) + model.c = pmo.constraint(model.x + model.y <= 0) diff --git a/pyomo/solvers/tests/models/LP_piecewise.py b/pyomo/solvers/tests/models/LP_piecewise.py index ce85158b906..9512b9aacd4 100644 --- a/pyomo/solvers/tests/models/LP_piecewise.py +++ b/pyomo/solvers/tests/models/LP_piecewise.py @@ -12,6 +12,7 @@ from pyomo.core import ConcreteModel, Var, Objective, Piecewise from pyomo.solvers.tests.models.base import _BaseTestModel, register_model + @register_model class LP_piecewise(_BaseTestModel): """ @@ -23,7 +24,7 @@ class LP_piecewise(_BaseTestModel): def __init__(self): _BaseTestModel.__init__(self) - self.add_results(self.description+".json") + self.add_results(self.description + ".json") def _generate_model(self): self.model = ConcreteModel() @@ -34,12 +35,15 @@ def _generate_model(self): model.y = Var() model.obj = Objective(expr=model.y) - model.p = Piecewise(model.y, model.x, - pw_pts=[-1,0,1], - f_rule=[1,0.5,1], - pw_repn='SOS2', - pw_constr_type='LB', - unbounded_domain_var=True) + model.p = Piecewise( + model.y, + model.x, + pw_pts=[-1, 0, 1], + f_rule=[1, 0.5, 1], + pw_repn='SOS2', + pw_constr_type='LB', + unbounded_domain_var=True, + ) def warmstart_model(self): assert self.model is not None @@ -47,6 +51,7 @@ def warmstart_model(self): model.x.value = None model.y.value = 1.0 + @register_model class LP_piecewise_nosuffixes(LP_piecewise): diff --git a/pyomo/solvers/tests/models/LP_simple.py b/pyomo/solvers/tests/models/LP_simple.py index bbcb4e60340..3449a657f79 100644 --- a/pyomo/solvers/tests/models/LP_simple.py +++ b/pyomo/solvers/tests/models/LP_simple.py @@ -10,9 +10,18 @@ # ___________________________________________________________________________ import pyomo.kernel as pmo -from pyomo.core import ConcreteModel, Param, Var, Expression, Objective, Constraint, NonNegativeReals +from pyomo.core import ( + ConcreteModel, + Param, + Var, + Expression, + Objective, + Constraint, + NonNegativeReals, +) from pyomo.solvers.tests.models.base import _BaseTestModel, register_model + @register_model class LP_simple(_BaseTestModel): """ @@ -24,7 +33,7 @@ class LP_simple(_BaseTestModel): def __init__(self): _BaseTestModel.__init__(self) - self.add_results(self.description+".json") + self.add_results(self.description + ".json") def _generate_model(self): self.model = ConcreteModel() @@ -39,17 +48,18 @@ def _generate_model(self): model.y = Var(within=NonNegativeReals) model.z1 = Var(bounds=(float('-inf'), float('inf'))) model.z2 = Var() - model.dummy_expr1 = Expression(initialize=model.a1*model.a2[1]) - model.dummy_expr2 = Expression(initialize=model.y/model.a3*model.a4[1]) + model.dummy_expr1 = Expression(initialize=model.a1 * model.a2[1]) + model.dummy_expr2 = Expression(initialize=model.y / model.a3 * model.a4[1]) model.inactive_obj = Objective( - expr=model.x + 3.0*model.y + 1.0 + model.z1 - model.z2) + expr=model.x + 3.0 * model.y + 1.0 + model.z1 - model.z2 + ) model.inactive_obj.deactivate() model.p = Param(mutable=True, initialize=0.0) model.obj = Objective(expr=model.p + model.inactive_obj) model.c1 = Constraint(expr=model.dummy_expr1 <= model.dummy_expr2) - model.c2 = Constraint(expr=(2.0, model.x/model.a3 - model.y, 10)) + model.c2 = Constraint(expr=(2.0, model.x / model.a3 - model.y, 10)) model.c3 = Constraint(expr=(0, model.z1 + 1, 10)) model.c4 = Constraint(expr=(-10, model.z2 + 1, 0)) @@ -59,34 +69,33 @@ def warmstart_model(self): model.x.value = None model.y.value = 1.0 + @register_model class LP_simple_kernel(LP_simple): - def _generate_model(self): self.model = pmo.block() model = self.model model._name = self.description model.a1 = pmo.parameter(value=1.0) - model.a2 = pmo.parameter_dict( - {1: pmo.parameter(value=1.0)}) + model.a2 = pmo.parameter_dict({1: pmo.parameter(value=1.0)}) model.a3 = pmo.parameter(value=1.0) - model.a4 = pmo.parameter_dict( - {1: pmo.parameter(value=1.0)}) + model.a4 = pmo.parameter_dict({1: pmo.parameter(value=1.0)}) model.x = pmo.variable(domain=NonNegativeReals) model.y = pmo.variable(domain=NonNegativeReals) model.z1 = pmo.variable() model.z2 = pmo.variable() - model.dummy_expr1 = pmo.expression(model.a1*model.a2[1]) - model.dummy_expr2 = pmo.expression(model.y/model.a3*model.a4[1]) + model.dummy_expr1 = pmo.expression(model.a1 * model.a2[1]) + model.dummy_expr2 = pmo.expression(model.y / model.a3 * model.a4[1]) model.inactive_obj = pmo.objective( - model.x + 3.0*model.y + 1.0 + model.z1 - model.z2) + model.x + 3.0 * model.y + 1.0 + model.z1 - model.z2 + ) model.inactive_obj.deactivate() model.p = pmo.parameter(value=0.0) model.obj = pmo.objective(model.p + model.inactive_obj) model.c1 = pmo.constraint(model.dummy_expr1 <= pmo.noclone(model.dummy_expr2)) - model.c2 = pmo.constraint((2.0, model.x/model.a3 - model.y, 10)) + model.c2 = pmo.constraint((2.0, model.x / model.a3 - model.y, 10)) model.c3 = pmo.constraint((0, model.z1 + 1, 10)) model.c4 = pmo.constraint((-10, model.z2 + 1, 0)) diff --git a/pyomo/solvers/tests/models/LP_trivial_constraints.py b/pyomo/solvers/tests/models/LP_trivial_constraints.py index 19c29dc962a..096c9e71712 100644 --- a/pyomo/solvers/tests/models/LP_trivial_constraints.py +++ b/pyomo/solvers/tests/models/LP_trivial_constraints.py @@ -11,10 +11,16 @@ import pyomo.kernel as pmo from pyomo.core import ( - ConcreteModel, Var, Objective, Constraint, RangeSet, ConstraintList + ConcreteModel, + Var, + Objective, + Constraint, + RangeSet, + ConstraintList, ) from pyomo.solvers.tests.models.base import _BaseTestModel, register_model + @register_model class LP_trivial_constraints(_BaseTestModel): """ @@ -27,7 +33,7 @@ class LP_trivial_constraints(_BaseTestModel): def __init__(self): _BaseTestModel.__init__(self) - self.add_results(self.description+".json") + self.add_results(self.description + ".json") def _generate_model(self): self.model = None @@ -61,13 +67,12 @@ def _generate_model(self): assert cdata.upper == 1 assert cdata.body() == 0 assert not cdata.equality - cdata = model.c.add((1,1)) + cdata = model.c.add((1, 1)) assert cdata.lower == 1 assert cdata.upper == 1 assert cdata.body() == 1 assert cdata.equality - model.d = Constraint( - rule=lambda m: (float('-inf'), m.x, float('inf'))) + model.d = Constraint(rule=lambda m: (float('-inf'), m.x, float('inf'))) assert not model.d.equality def warmstart_model(self): @@ -86,9 +91,9 @@ def post_solve_test_validation(self, tester, results): tester.assertIn(id(self.model.c[i]), symbol_map.byObject) tester.assertNotIn(id(self.model.d), symbol_map.byObject) + @register_model class LP_trivial_constraints_kernel(LP_trivial_constraints): - def _generate_model(self): self.model = None self.model = pmo.block() @@ -121,7 +126,7 @@ def _generate_model(self): assert cdata.ub == 1 assert cdata.body() == 0 assert not cdata.equality - cdata = model.c[7] = pmo.constraint((1,1)) + cdata = model.c[7] = pmo.constraint((1, 1)) assert cdata.lb == 1 assert cdata.ub == 1 assert cdata.body() == 1 diff --git a/pyomo/solvers/tests/models/LP_unbounded.py b/pyomo/solvers/tests/models/LP_unbounded.py index 5ac0e4e55ec..e3173e2ff07 100644 --- a/pyomo/solvers/tests/models/LP_unbounded.py +++ b/pyomo/solvers/tests/models/LP_unbounded.py @@ -14,6 +14,7 @@ from pyomo.opt import TerminationCondition from pyomo.solvers.tests.models.base import _BaseTestModel, register_model + @register_model class LP_unbounded(_BaseTestModel): """ @@ -26,7 +27,7 @@ class LP_unbounded(_BaseTestModel): def __init__(self): _BaseTestModel.__init__(self) self.solve_should_fail = True - self.add_results(self.description+".json") + self.add_results(self.description + ".json") def _generate_model(self): self.model = ConcreteModel() @@ -36,7 +37,7 @@ def _generate_model(self): model.x = Var() model.y = Var() - model.o = Objective(expr=model.x+model.y) + model.o = Objective(expr=model.x + model.y) def warmstart_model(self): assert self.model is not None @@ -46,17 +47,22 @@ def warmstart_model(self): def post_solve_test_validation(self, tester, results): if tester is None: - assert results['Solver'][0]['termination condition'] in \ - (TerminationCondition.unbounded, - TerminationCondition.infeasibleOrUnbounded) + assert results['Solver'][0]['termination condition'] in ( + TerminationCondition.unbounded, + TerminationCondition.infeasibleOrUnbounded, + ) else: - tester.assertIn(results['Solver'][0]['termination condition'], - (TerminationCondition.unbounded, - TerminationCondition.infeasibleOrUnbounded)) + tester.assertIn( + results['Solver'][0]['termination condition'], + ( + TerminationCondition.unbounded, + TerminationCondition.infeasibleOrUnbounded, + ), + ) + @register_model class LP_unbounded_kernel(LP_unbounded): - def _generate_model(self): self.model = pmo.block() model = self.model @@ -65,4 +71,4 @@ def _generate_model(self): model.x = pmo.variable() model.y = pmo.variable() - model.o = pmo.objective(model.x+model.y) + model.o = pmo.objective(model.x + model.y) diff --git a/pyomo/solvers/tests/models/LP_unique_duals.py b/pyomo/solvers/tests/models/LP_unique_duals.py index 4e0c4f842e1..624181eb27d 100644 --- a/pyomo/solvers/tests/models/LP_unique_duals.py +++ b/pyomo/solvers/tests/models/LP_unique_duals.py @@ -9,28 +9,42 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.core import ConcreteModel, Param, Var, Objective, Constraint, RangeSet, NonNegativeReals, Suffix, sum_product +from pyomo.core import ( + ConcreteModel, + Param, + Var, + Objective, + Constraint, + RangeSet, + NonNegativeReals, + Suffix, + sum_product, +) from pyomo.solvers.tests.models.base import _BaseTestModel, register_model + def c_rule(model, j): - return 5 if j<5 else 9.0/2 + return 5 if j < 5 else 9.0 / 2 + def b_rule(model, i): if i == 4: i = 5 elif i == 5: i = 4 - return 5 if i<5 else 7.0/2 + return 5 if i < 5 else 7.0 / 2 + def A_rule(model, i, j): if i == 4: i = 5 elif i == 5: i = 4 - return 2 if i==j else 1 + return 2 if i == j else 1 + def primalcon_rule(model, i): - return sum(model.A[i,j]*model.x[j] for j in model.N) >= model.b[i] + return sum(model.A[i, j] * model.x[j] for j in model.N) >= model.b[i] @register_model @@ -44,7 +58,7 @@ class LP_unique_duals(_BaseTestModel): def __init__(self): _BaseTestModel.__init__(self) - self.add_results(self.description+".json") + self.add_results(self.description + ".json") def _generate_model(self): self.model = None @@ -54,8 +68,8 @@ def _generate_model(self): n = 7 m = 7 - model.N = RangeSet(1,n) - model.M = RangeSet(1,m) + model.N = RangeSet(1, n) + model.M = RangeSet(1, m) model.c = Param(model.N, rule=c_rule) @@ -70,8 +84,8 @@ def _generate_model(self): model.primalcon = Constraint(model.M, rule=primalcon_rule) - #model.dual = Suffix(direction=Suffix.IMPORT) - #model.rc = Suffix(direction=Suffix.IMPORT) + # model.dual = Suffix(direction=Suffix.IMPORT) + # model.rc = Suffix(direction=Suffix.IMPORT) model.slack = Suffix(direction=Suffix.IMPORT) model.urc = Suffix(direction=Suffix.IMPORT) model.lrc = Suffix(direction=Suffix.IMPORT) @@ -83,4 +97,3 @@ def warmstart_model(self): model.x[i] = None for i in model.y: model.y[i] = None - diff --git a/pyomo/solvers/tests/models/LP_unused_vars.py b/pyomo/solvers/tests/models/LP_unused_vars.py index 52e2a6af523..265db19817a 100644 --- a/pyomo/solvers/tests/models/LP_unused_vars.py +++ b/pyomo/solvers/tests/models/LP_unused_vars.py @@ -10,9 +10,18 @@ # ___________________________________________________________________________ import pyomo.kernel as pmo -from pyomo.core import ConcreteModel, Var, Objective, Set, ConstraintList, sum_product, Block +from pyomo.core import ( + ConcreteModel, + Var, + Objective, + Set, + ConstraintList, + sum_product, + Block, +) from pyomo.solvers.tests.models.base import _BaseTestModel, register_model + @register_model class LP_unused_vars(_BaseTestModel): """ @@ -26,7 +35,7 @@ class LP_unused_vars(_BaseTestModel): def __init__(self): _BaseTestModel.__init__(self) self.disable_suffix_tests = True - self.add_results(self.description+".json") + self.add_results(self.description + ".json") def _generate_model(self): self.model = None @@ -34,7 +43,7 @@ def _generate_model(self): model = self.model model._name = self.description - model.s = Set(initialize=[1,2]) + model.s = Set(initialize=[1, 2]) model.x_unused = Var() model.x_unused.stale = False @@ -60,18 +69,20 @@ def _generate_model(self): model.X[i].stale = False model.X_initialy_stale[i].stale = True - model.obj = Objective(expr= model.x + \ - model.x_initialy_stale + \ - sum_product(model.X) + \ - sum_product(model.X_initialy_stale)) + model.obj = Objective( + expr=model.x + + model.x_initialy_stale + + sum_product(model.X) + + sum_product(model.X_initialy_stale) + ) model.c = ConstraintList() - model.c.add( model.x >= 1 ) - model.c.add( model.x_initialy_stale >= 1 ) - model.c.add( model.X[1] >= 0 ) - model.c.add( model.X[2] >= 1 ) - model.c.add( model.X_initialy_stale[1] >= 0 ) - model.c.add( model.X_initialy_stale[2] >= 1 ) + model.c.add(model.x >= 1) + model.c.add(model.x_initialy_stale >= 1) + model.c.add(model.X[1] >= 0) + model.c.add(model.X[2] >= 1) + model.c.add(model.X_initialy_stale[1] >= 0) + model.c.add(model.X_initialy_stale[2] >= 1) # Test that stale flags get set # on inactive blocks (where "inactive blocks" mean blocks @@ -115,26 +126,26 @@ def warmstart_model(self): model.X_initialy_stale[i].value = -1.0 model.X_initialy_stale[i].stale = True + @register_model class LP_unused_vars_kernel(LP_unused_vars): - def _generate_model(self): self.model = None self.model = pmo.block() model = self.model model._name = self.description - model.s = [1,2] + model.s = [1, 2] model.x_unused = pmo.variable() model.x_unused.stale = False model.x_unused_initialy_stale = pmo.variable() model.x_unused_initialy_stale.stale = True - model.X_unused = pmo.variable_dict( - (i, pmo.variable()) for i in model.s) + model.X_unused = pmo.variable_dict((i, pmo.variable()) for i in model.s) model.X_unused_initialy_stale = pmo.variable_dict( - (i, pmo.variable()) for i in model.s) + (i, pmo.variable()) for i in model.s + ) for i in model.X_unused: model.X_unused[i].stale = False @@ -146,24 +157,24 @@ def _generate_model(self): model.x_initialy_stale = pmo.variable() model.x_initialy_stale.stale = True - model.X = pmo.variable_dict( - (i, pmo.variable()) for i in model.s) - model.X_initialy_stale = pmo.variable_dict( - (i, pmo.variable()) for i in model.s) + model.X = pmo.variable_dict((i, pmo.variable()) for i in model.s) + model.X_initialy_stale = pmo.variable_dict((i, pmo.variable()) for i in model.s) for i in model.X: model.X[i].stale = False model.X_initialy_stale[i].stale = True - model.obj = pmo.objective(model.x + \ - model.x_initialy_stale + \ - sum(model.X.values()) + \ - sum(model.X_initialy_stale.values())) + model.obj = pmo.objective( + model.x + + model.x_initialy_stale + + sum(model.X.values()) + + sum(model.X_initialy_stale.values()) + ) model.c = pmo.constraint_dict() - model.c[1] = pmo.constraint(model.x >= 1) - model.c[2] = pmo.constraint(model.x_initialy_stale >= 1) - model.c[3] = pmo.constraint(model.X[1] >= 0) - model.c[4] = pmo.constraint(model.X[2] >= 1) + model.c[1] = pmo.constraint(model.x >= 1) + model.c[2] = pmo.constraint(model.x_initialy_stale >= 1) + model.c[3] = pmo.constraint(model.X[1] >= 0) + model.c[4] = pmo.constraint(model.X[2] >= 1) model.c[5] = pmo.constraint(model.X_initialy_stale[1] >= 0) model.c[6] = pmo.constraint(model.X_initialy_stale[2] >= 1) diff --git a/pyomo/solvers/tests/models/MILP_discrete_var_bounds.py b/pyomo/solvers/tests/models/MILP_discrete_var_bounds.py index 75d7e675581..8fef69ef76a 100644 --- a/pyomo/solvers/tests/models/MILP_discrete_var_bounds.py +++ b/pyomo/solvers/tests/models/MILP_discrete_var_bounds.py @@ -13,6 +13,7 @@ from pyomo.core import ConcreteModel, Var, Objective, Constraint, Binary, Integers from pyomo.solvers.tests.models.base import _BaseTestModel, register_model + @register_model class MILP_discrete_var_bounds(_BaseTestModel): """ @@ -25,7 +26,7 @@ class MILP_discrete_var_bounds(_BaseTestModel): def __init__(self): _BaseTestModel.__init__(self) self.disable_suffix_tests = True - self.add_results(self.description+".json") + self.add_results(self.description + ".json") def _generate_model(self): self.model = ConcreteModel() @@ -34,15 +35,14 @@ def _generate_model(self): model.w2 = Var(within=Binary) model.x2 = Var(within=Binary) - model.yb = Var(within=Binary, bounds=(1,1)) - model.zb = Var(within=Binary, bounds=(0,0)) - model.yi = Var(within=Integers, bounds=(-1,None)) - model.zi = Var(within=Integers, bounds=(None,1)) + model.yb = Var(within=Binary, bounds=(1, 1)) + model.zb = Var(within=Binary, bounds=(0, 0)) + model.yi = Var(within=Integers, bounds=(-1, None)) + model.zi = Var(within=Integers, bounds=(None, 1)) - model.obj = Objective(expr=\ - model.w2 - model.x2 +\ - model.yb - model.zb +\ - model.yi - model.zi) + model.obj = Objective( + expr=model.w2 - model.x2 + model.yb - model.zb + model.yi - model.zi + ) model.c3 = Constraint(expr=model.w2 >= 0) model.c4 = Constraint(expr=model.x2 <= 1) @@ -57,27 +57,24 @@ def warmstart_model(self): model.yi.value = None model.zi.value = 0 + @register_model class MILP_discrete_var_bounds_kernel(MILP_discrete_var_bounds): - def _generate_model(self): self.model = pmo.block() model = self.model model._name = self.description model.w2 = pmo.variable(domain=pmo.BooleanSet) - model.x2 = pmo.variable(domain_type=pmo.IntegerSet, - lb=0, ub=1) - model.yb = pmo.variable(domain_type=pmo.IntegerSet, - lb=1, ub=1) - model.zb = pmo.variable(domain_type=pmo.IntegerSet, - lb=0, ub=0) + model.x2 = pmo.variable(domain_type=pmo.IntegerSet, lb=0, ub=1) + model.yb = pmo.variable(domain_type=pmo.IntegerSet, lb=1, ub=1) + model.zb = pmo.variable(domain_type=pmo.IntegerSet, lb=0, ub=0) model.yi = pmo.variable(domain=pmo.IntegerSet, lb=-1) model.zi = pmo.variable(domain=pmo.IntegerSet, ub=1) - model.obj = pmo.objective(model.w2 - model.x2 +\ - model.yb - model.zb +\ - model.yi - model.zi) + model.obj = pmo.objective( + model.w2 - model.x2 + model.yb - model.zb + model.yi - model.zi + ) model.c3 = pmo.constraint(model.w2 >= 0) model.c4 = pmo.constraint(model.x2 <= 1) diff --git a/pyomo/solvers/tests/models/MILP_infeasible1.py b/pyomo/solvers/tests/models/MILP_infeasible1.py index 9b09cf4c586..2a0bf1bd188 100644 --- a/pyomo/solvers/tests/models/MILP_infeasible1.py +++ b/pyomo/solvers/tests/models/MILP_infeasible1.py @@ -14,6 +14,7 @@ from pyomo.opt import TerminationCondition from pyomo.solvers.tests.models.base import _BaseTestModel, register_model + @register_model class MILP_infeasible1(_BaseTestModel): """ @@ -26,7 +27,7 @@ class MILP_infeasible1(_BaseTestModel): def __init__(self): _BaseTestModel.__init__(self) self.solve_should_fail = True - self.add_results(self.description+".json") + self.add_results(self.description + ".json") def _generate_model(self): self.model = ConcreteModel() @@ -37,12 +38,12 @@ def _generate_model(self): model.y = Var(within=Binary) model.z = Var(within=Binary) - model.o = Objective(expr=-model.x-model.y-model.z) + model.o = Objective(expr=-model.x - model.y - model.z) - model.c1 = Constraint(expr=model.x+model.y <= 1) - model.c2 = Constraint(expr=model.x+model.z <= 1) - model.c3 = Constraint(expr=model.y+model.z <= 1) - model.c4 = Constraint(expr=model.x+model.y+model.z >= 1.5) + model.c1 = Constraint(expr=model.x + model.y <= 1) + model.c2 = Constraint(expr=model.x + model.z <= 1) + model.c3 = Constraint(expr=model.y + model.z <= 1) + model.c4 = Constraint(expr=model.x + model.y + model.z >= 1.5) def warmstart_model(self): assert self.model is not None @@ -53,15 +54,19 @@ def warmstart_model(self): def post_solve_test_validation(self, tester, results): if tester is None: - assert results['Solver'][0]['termination condition'] == \ - TerminationCondition.infeasible + assert ( + results['Solver'][0]['termination condition'] + == TerminationCondition.infeasible + ) else: - tester.assertEqual(results['Solver'][0]['termination condition'], - TerminationCondition.infeasible) + tester.assertEqual( + results['Solver'][0]['termination condition'], + TerminationCondition.infeasible, + ) + @register_model class MILP_infeasible1_kernel(MILP_infeasible1): - def _generate_model(self): self.model = pmo.block() model = self.model @@ -71,9 +76,9 @@ def _generate_model(self): model.y = pmo.variable(domain=Binary) model.z = pmo.variable(domain=Binary) - model.o = pmo.objective(-model.x-model.y-model.z) + model.o = pmo.objective(-model.x - model.y - model.z) - model.c1 = pmo.constraint(model.x+model.y <= 1) - model.c2 = pmo.constraint(model.x+model.z <= 1) - model.c3 = pmo.constraint(model.y+model.z <= 1) - model.c4 = pmo.constraint(model.x+model.y+model.z >= 1.5) + model.c1 = pmo.constraint(model.x + model.y <= 1) + model.c2 = pmo.constraint(model.x + model.z <= 1) + model.c3 = pmo.constraint(model.y + model.z <= 1) + model.c4 = pmo.constraint(model.x + model.y + model.z >= 1.5) diff --git a/pyomo/solvers/tests/models/MILP_simple.py b/pyomo/solvers/tests/models/MILP_simple.py index 62e7f0fa5d4..fb157ea6555 100644 --- a/pyomo/solvers/tests/models/MILP_simple.py +++ b/pyomo/solvers/tests/models/MILP_simple.py @@ -10,9 +10,18 @@ # ___________________________________________________________________________ import pyomo.kernel as pmo -from pyomo.core import ConcreteModel, Param, Var, Objective, Constraint, Binary, NonNegativeReals +from pyomo.core import ( + ConcreteModel, + Param, + Var, + Objective, + Constraint, + Binary, + NonNegativeReals, +) from pyomo.solvers.tests.models.base import _BaseTestModel, register_model + @register_model class MILP_simple(_BaseTestModel): """ @@ -25,7 +34,7 @@ class MILP_simple(_BaseTestModel): def __init__(self): _BaseTestModel.__init__(self) - self.add_results(self.description+".json") + self.add_results(self.description + ".json") def _generate_model(self): self.model = ConcreteModel() @@ -36,9 +45,9 @@ def _generate_model(self): model.x = Var(within=NonNegativeReals) model.y = Var(within=Binary) - model.obj = Objective(expr=model.x + 3.0*model.y) + model.obj = Objective(expr=model.x + 3.0 * model.y) model.c1 = Constraint(expr=model.a <= model.y) - model.c2 = Constraint(expr=(2.0, model.x/model.a - model.y, 10)) + model.c2 = Constraint(expr=(2.0, model.x / model.a - model.y, 10)) def warmstart_model(self): assert self.model is not None @@ -46,9 +55,9 @@ def warmstart_model(self): model.x.value = 0.1 model.y.value = 0 + @register_model class MILP_simple_kernel(MILP_simple): - def _generate_model(self): self.model = pmo.block() model = self.model @@ -58,6 +67,6 @@ def _generate_model(self): model.x = pmo.variable(domain=NonNegativeReals) model.y = pmo.variable(domain=Binary) - model.obj = pmo.objective(model.x + 3.0*model.y) + model.obj = pmo.objective(model.x + 3.0 * model.y) model.c1 = pmo.constraint(model.a <= model.y) - model.c2 = pmo.constraint((2.0, model.x/model.a - model.y, 10)) + model.c2 = pmo.constraint((2.0, model.x / model.a - model.y, 10)) diff --git a/pyomo/solvers/tests/models/MILP_unbounded.py b/pyomo/solvers/tests/models/MILP_unbounded.py index 8acc548ab1e..364f3ffeb86 100644 --- a/pyomo/solvers/tests/models/MILP_unbounded.py +++ b/pyomo/solvers/tests/models/MILP_unbounded.py @@ -14,6 +14,7 @@ from pyomo.opt import TerminationCondition from pyomo.solvers.tests.models.base import _BaseTestModel, register_model + @register_model class MILP_unbounded(_BaseTestModel): """ @@ -26,7 +27,7 @@ class MILP_unbounded(_BaseTestModel): def __init__(self): _BaseTestModel.__init__(self) self.solve_should_fail = True - self.add_results(self.description+".json") + self.add_results(self.description + ".json") def _generate_model(self): self.model = ConcreteModel() @@ -36,7 +37,7 @@ def _generate_model(self): model.x = Var(within=Integers) model.y = Var(within=Integers) - model.o = Objective(expr=model.x+model.y) + model.o = Objective(expr=model.x + model.y) def warmstart_model(self): assert self.model is not None @@ -46,17 +47,22 @@ def warmstart_model(self): def post_solve_test_validation(self, tester, results): if tester is None: - assert results['Solver'][0]['termination condition'] in \ - (TerminationCondition.unbounded, - TerminationCondition.infeasibleOrUnbounded) + assert results['Solver'][0]['termination condition'] in ( + TerminationCondition.unbounded, + TerminationCondition.infeasibleOrUnbounded, + ) else: - tester.assertIn(results['Solver'][0]['termination condition'], - (TerminationCondition.unbounded, - TerminationCondition.infeasibleOrUnbounded)) + tester.assertIn( + results['Solver'][0]['termination condition'], + ( + TerminationCondition.unbounded, + TerminationCondition.infeasibleOrUnbounded, + ), + ) + @register_model class MILP_unbounded_kernel(MILP_unbounded): - def _generate_model(self): self.model = pmo.block() model = self.model @@ -65,4 +71,4 @@ def _generate_model(self): model.x = pmo.variable(domain=pmo.IntegerSet) model.y = pmo.variable(domain=pmo.IntegerSet) - model.o = pmo.objective(model.x+model.y) + model.o = pmo.objective(model.x + model.y) diff --git a/pyomo/solvers/tests/models/MILP_unused_vars.py b/pyomo/solvers/tests/models/MILP_unused_vars.py index 36b021224d5..9574feb523c 100644 --- a/pyomo/solvers/tests/models/MILP_unused_vars.py +++ b/pyomo/solvers/tests/models/MILP_unused_vars.py @@ -10,9 +10,20 @@ # ___________________________________________________________________________ import pyomo.kernel as pmo -from pyomo.core import ConcreteModel, Var, Objective, ConstraintList, Set, Integers, RangeSet, sum_product, Block +from pyomo.core import ( + ConcreteModel, + Var, + Objective, + ConstraintList, + Set, + Integers, + RangeSet, + sum_product, + Block, +) from pyomo.solvers.tests.models.base import _BaseTestModel, register_model + @register_model class MILP_unused_vars(_BaseTestModel): """ @@ -26,14 +37,14 @@ class MILP_unused_vars(_BaseTestModel): def __init__(self): _BaseTestModel.__init__(self) self.disable_suffix_tests = True - self.add_results(self.description+".json") + self.add_results(self.description + ".json") def _generate_model(self): self.model = ConcreteModel() model = self.model model._name = self.description - model.s = Set(initialize=[1,2]) + model.s = Set(initialize=[1, 2]) model.x_unused = Var(within=Integers) model.x_unused.stale = False @@ -47,7 +58,7 @@ def _generate_model(self): model.X_unused[i].stale = False model.X_unused_initialy_stale[i].stale = True - model.x = Var(within=RangeSet(None,None)) + model.x = Var(within=RangeSet(None, None)) model.x.stale = False model.x_initialy_stale = Var(within=Integers) @@ -59,18 +70,20 @@ def _generate_model(self): model.X[i].stale = False model.X_initialy_stale[i].stale = True - model.obj = Objective(expr= model.x + \ - model.x_initialy_stale + \ - sum_product(model.X) + \ - sum_product(model.X_initialy_stale)) + model.obj = Objective( + expr=model.x + + model.x_initialy_stale + + sum_product(model.X) + + sum_product(model.X_initialy_stale) + ) model.c = ConstraintList() - model.c.add( model.x >= 1 ) - model.c.add( model.x_initialy_stale >= 1 ) - model.c.add( model.X[1] >= 0 ) - model.c.add( model.X[2] >= 1 ) - model.c.add( model.X_initialy_stale[1] >= 0 ) - model.c.add( model.X_initialy_stale[2] >= 1 ) + model.c.add(model.x >= 1) + model.c.add(model.x_initialy_stale >= 1) + model.c.add(model.X[1] >= 0) + model.c.add(model.X[2] >= 1) + model.c.add(model.X_initialy_stale[1] >= 0) + model.c.add(model.X_initialy_stale[2] >= 1) # Test that stale flags get set # on inactive blocks (where "inactive blocks" mean blocks @@ -114,15 +127,15 @@ def warmstart_model(self): model.X_initialy_stale[i].value = -1 model.X_initialy_stale[i].stale = True + @register_model class MILP_unused_vars_kernel(MILP_unused_vars): - def _generate_model(self): self.model = pmo.block() model = self.model model._name = self.description - model.s = [1,2] + model.s = [1, 2] model.x_unused = pmo.variable(domain=pmo.IntegerSet) model.x_unused.stale = False @@ -131,37 +144,43 @@ def _generate_model(self): model.x_unused_initialy_stale.stale = True model.X_unused = pmo.variable_dict( - (i, pmo.variable(domain=pmo.IntegerSet)) for i in model.s) + (i, pmo.variable(domain=pmo.IntegerSet)) for i in model.s + ) model.X_unused_initialy_stale = pmo.variable_dict( - (i, pmo.variable(domain=pmo.IntegerSet)) for i in model.s) + (i, pmo.variable(domain=pmo.IntegerSet)) for i in model.s + ) for i in model.s: model.X_unused[i].stale = False model.X_unused_initialy_stale[i].stale = True - model.x = pmo.variable(domain=RangeSet(None,None)) + model.x = pmo.variable(domain=RangeSet(None, None)) model.x.stale = False model.x_initialy_stale = pmo.variable(domain=pmo.IntegerSet) model.x_initialy_stale.stale = True model.X = pmo.variable_dict( - (i, pmo.variable(domain=pmo.IntegerSet)) for i in model.s) + (i, pmo.variable(domain=pmo.IntegerSet)) for i in model.s + ) model.X_initialy_stale = pmo.variable_dict( - (i, pmo.variable(domain=pmo.IntegerSet)) for i in model.s) + (i, pmo.variable(domain=pmo.IntegerSet)) for i in model.s + ) for i in model.s: model.X[i].stale = False model.X_initialy_stale[i].stale = True - model.obj = pmo.objective(model.x + \ - model.x_initialy_stale + \ - sum(model.X.values()) + \ - sum(model.X_initialy_stale.values())) + model.obj = pmo.objective( + model.x + + model.x_initialy_stale + + sum(model.X.values()) + + sum(model.X_initialy_stale.values()) + ) model.c = pmo.constraint_dict() - model.c[1] = pmo.constraint(model.x >= 1) - model.c[2] = pmo.constraint(model.x_initialy_stale >= 1) - model.c[3] = pmo.constraint(model.X[1] >= 0) - model.c[4] = pmo.constraint(model.X[2] >= 1) + model.c[1] = pmo.constraint(model.x >= 1) + model.c[2] = pmo.constraint(model.x_initialy_stale >= 1) + model.c[3] = pmo.constraint(model.X[1] >= 0) + model.c[4] = pmo.constraint(model.X[2] >= 1) model.c[5] = pmo.constraint(model.X_initialy_stale[1] >= 0) model.c[6] = pmo.constraint(model.X_initialy_stale[2] >= 1) diff --git a/pyomo/solvers/tests/models/MIQCP_simple.py b/pyomo/solvers/tests/models/MIQCP_simple.py index 3c08db40823..46c1293b23c 100644 --- a/pyomo/solvers/tests/models/MIQCP_simple.py +++ b/pyomo/solvers/tests/models/MIQCP_simple.py @@ -13,6 +13,7 @@ from pyomo.core import ConcreteModel, Var, Objective, Constraint, Binary, maximize from pyomo.solvers.tests.models.base import _BaseTestModel, register_model + @register_model class MIQCP_simple(_BaseTestModel): """ @@ -25,7 +26,7 @@ class MIQCP_simple(_BaseTestModel): def __init__(self): _BaseTestModel.__init__(self) - self.add_results(self.description+".json") + self.add_results(self.description + ".json") def _generate_model(self): self.model = ConcreteModel() @@ -36,10 +37,10 @@ def _generate_model(self): model.y = Var(within=Binary) model.z = Var(within=Binary) - model.obj = Objective(expr=model.x,sense=maximize) - model.c0 = Constraint(expr=model.x+model.y+model.z == 1) + model.obj = Objective(expr=model.x, sense=maximize) + model.c0 = Constraint(expr=model.x + model.y + model.z == 1) model.qc0 = Constraint(expr=model.x**2 + model.y**2 <= model.z**2) - model.qc1 = Constraint(expr=model.x**2 <= model.y*model.z) + model.qc1 = Constraint(expr=model.x**2 <= model.y * model.z) def warmstart_model(self): assert self.model is not None @@ -48,9 +49,9 @@ def warmstart_model(self): model.y.value = None model.z.value = None + @register_model class MIQCP_simple_kernel(MIQCP_simple): - def _generate_model(self): self.model = pmo.block() model = self.model @@ -60,7 +61,7 @@ def _generate_model(self): model.y = pmo.variable(domain=Binary) model.z = pmo.variable(domain=Binary) - model.obj = pmo.objective(model.x,sense=maximize) - model.c0 = pmo.constraint(model.x+model.y+model.z == 1) + model.obj = pmo.objective(model.x, sense=maximize) + model.c0 = pmo.constraint(model.x + model.y + model.z == 1) model.qc0 = pmo.constraint(model.x**2 + model.y**2 <= model.z**2) - model.qc1 = pmo.constraint(model.x**2 <= model.y*model.z) + model.qc1 = pmo.constraint(model.x**2 <= model.y * model.z) diff --git a/pyomo/solvers/tests/models/MIQP_simple.py b/pyomo/solvers/tests/models/MIQP_simple.py index 8001c5c056d..1d43d96ab8b 100644 --- a/pyomo/solvers/tests/models/MIQP_simple.py +++ b/pyomo/solvers/tests/models/MIQP_simple.py @@ -10,10 +10,19 @@ # ___________________________________________________________________________ import pyomo.kernel as pmo -from pyomo.core import ConcreteModel, Param, Var, Objective, Constraint, NonNegativeReals, Binary +from pyomo.core import ( + ConcreteModel, + Param, + Var, + Objective, + Constraint, + NonNegativeReals, + Binary, +) from pyomo.opt import TerminationCondition from pyomo.solvers.tests.models.base import _BaseTestModel, register_model + @register_model class MIQP_simple(_BaseTestModel): """ @@ -26,7 +35,7 @@ class MIQP_simple(_BaseTestModel): def __init__(self): _BaseTestModel.__init__(self) - self.add_results(self.description+".json") + self.add_results(self.description + ".json") def _generate_model(self): self.model = ConcreteModel() @@ -37,9 +46,9 @@ def _generate_model(self): model.x = Var(within=NonNegativeReals) model.y = Var(within=Binary) - model.obj = Objective(expr=model.x**2 + 3.0*model.y**2) + model.obj = Objective(expr=model.x**2 + 3.0 * model.y**2) model.c1 = Constraint(expr=model.a <= model.y) - model.c2 = Constraint(expr=(2.0, model.x/model.a - model.y, 10)) + model.c2 = Constraint(expr=(2.0, model.x / model.a - model.y, 10)) def warmstart_model(self): assert self.model is not None @@ -49,17 +58,19 @@ def warmstart_model(self): def post_solve_test_validation(self, tester, results): if tester is None: - assert results['Solver'][0]['termination condition'] in \ - (TerminationCondition.optimal, - TerminationCondition.locallyOptimal) + assert results['Solver'][0]['termination condition'] in ( + TerminationCondition.optimal, + TerminationCondition.locallyOptimal, + ) else: - tester.assertIn(results['Solver'][0]['termination condition'], - (TerminationCondition.optimal, - TerminationCondition.locallyOptimal)) + tester.assertIn( + results['Solver'][0]['termination condition'], + (TerminationCondition.optimal, TerminationCondition.locallyOptimal), + ) + @register_model class MIQP_simple_kernel(MIQP_simple): - def _generate_model(self): self.model = pmo.block() model = self.model @@ -69,6 +80,6 @@ def _generate_model(self): model.x = pmo.variable(domain=NonNegativeReals) model.y = pmo.variable(domain=Binary) - model.obj = pmo.objective(model.x**2 + 3.0*model.y**2) + model.obj = pmo.objective(model.x**2 + 3.0 * model.y**2) model.c1 = pmo.constraint(model.a <= model.y) - model.c2 = pmo.constraint((2.0, model.x/model.a - model.y, 10)) + model.c2 = pmo.constraint((2.0, model.x / model.a - model.y, 10)) diff --git a/pyomo/solvers/tests/models/QCP_simple.py b/pyomo/solvers/tests/models/QCP_simple.py index ef7d3569406..1f040cd3a8e 100644 --- a/pyomo/solvers/tests/models/QCP_simple.py +++ b/pyomo/solvers/tests/models/QCP_simple.py @@ -10,10 +10,19 @@ # ___________________________________________________________________________ import pyomo.kernel as pmo -from pyomo.core import ConcreteModel, Var, Objective, Constraint, NonNegativeReals, maximize, ConstraintList +from pyomo.core import ( + ConcreteModel, + Var, + Objective, + Constraint, + NonNegativeReals, + maximize, + ConstraintList, +) from pyomo.opt import TerminationCondition from pyomo.solvers.tests.models.base import _BaseTestModel, register_model + @register_model class QCP_simple(_BaseTestModel): """ @@ -26,7 +35,7 @@ class QCP_simple(_BaseTestModel): def __init__(self): _BaseTestModel.__init__(self) - self.add_results(self.description+".json") + self.add_results(self.description + ".json") def _generate_model(self): self.model = ConcreteModel() @@ -39,11 +48,13 @@ def _generate_model(self): model.fixed_var = Var() model.fixed_var.fix(0.2) model.q1 = Var(bounds=(None, 0.2)) - model.q2 = Var(bounds=(-2,None)) - model.obj = Objective(expr=model.x+model.q1-model.q2,sense=maximize) - model.c0 = Constraint(expr=model.x+model.y+model.z == 1) - model.qc0 = Constraint(expr=model.x**2 + model.y**2 + model.fixed_var <= model.z**2) - model.qc1 = Constraint(expr=model.x**2 <= model.y*model.z) + model.q2 = Var(bounds=(-2, None)) + model.obj = Objective(expr=model.x + model.q1 - model.q2, sense=maximize) + model.c0 = Constraint(expr=model.x + model.y + model.z == 1) + model.qc0 = Constraint( + expr=model.x**2 + model.y**2 + model.fixed_var <= model.z**2 + ) + model.qc1 = Constraint(expr=model.x**2 <= model.y * model.z) model.c = ConstraintList() model.c.add((0, -model.q1**2 + model.fixed_var, None)) model.c.add((None, model.q2**2 + model.fixed_var, 5)) @@ -57,13 +68,16 @@ def warmstart_model(self): def post_solve_test_validation(self, tester, results): if tester is None: - assert results['Solver'][0]['termination condition'] in \ - (TerminationCondition.optimal, - TerminationCondition.locallyOptimal) + assert results['Solver'][0]['termination condition'] in ( + TerminationCondition.optimal, + TerminationCondition.locallyOptimal, + ) else: - tester.assertIn(results['Solver'][0]['termination condition'], - (TerminationCondition.optimal, - TerminationCondition.locallyOptimal)) + tester.assertIn( + results['Solver'][0]['termination condition'], + (TerminationCondition.optimal, TerminationCondition.locallyOptimal), + ) + @register_model class QCP_simple_nosuffixes(QCP_simple): @@ -76,9 +90,9 @@ def __init__(self): self.disable_suffix_tests = True self.add_results("QCP_simple.json") + @register_model class QCP_simple_kernel(QCP_simple): - def _generate_model(self): self.model = pmo.block() model = self.model @@ -91,14 +105,17 @@ def _generate_model(self): model.fixed_var.fix(0.2) model.q1 = pmo.variable(ub=0.2) model.q2 = pmo.variable(lb=-2) - model.obj = pmo.objective(model.x+model.q1-model.q2,sense=maximize) - model.c0 = pmo.constraint(model.x+model.y+model.z == 1) - model.qc0 = pmo.constraint(model.x**2 + model.y**2 + model.fixed_var <= model.z**2) - model.qc1 = pmo.constraint(model.x**2 <= model.y*model.z) + model.obj = pmo.objective(model.x + model.q1 - model.q2, sense=maximize) + model.c0 = pmo.constraint(model.x + model.y + model.z == 1) + model.qc0 = pmo.constraint( + model.x**2 + model.y**2 + model.fixed_var <= model.z**2 + ) + model.qc1 = pmo.constraint(model.x**2 <= model.y * model.z) model.c = pmo.constraint_dict() model.c[1] = pmo.constraint(lb=0, body=-model.q1**2 + model.fixed_var) model.c[2] = pmo.constraint(body=model.q2**2 + model.fixed_var, ub=5) + @register_model class QCP_simple_nosuffixes_kernel(QCP_simple_kernel): diff --git a/pyomo/solvers/tests/models/QP_constant_objective.py b/pyomo/solvers/tests/models/QP_constant_objective.py index 0a87f6e7403..2769fe07556 100644 --- a/pyomo/solvers/tests/models/QP_constant_objective.py +++ b/pyomo/solvers/tests/models/QP_constant_objective.py @@ -16,6 +16,7 @@ # linear objectives IF we could get some proper preprocessing # in place for the canonical_repn + @register_model class QP_constant_objective(_BaseTestModel): """ @@ -28,7 +29,7 @@ class QP_constant_objective(_BaseTestModel): def __init__(self): _BaseTestModel.__init__(self) - self.add_results(self.description+".json") + self.add_results(self.description + ".json") def _generate_model(self): self.model = ConcreteModel() @@ -36,7 +37,7 @@ def _generate_model(self): model._name = self.description model.x = Var(within=NonNegativeReals) - model.obj = Objective(expr=model.x**2-model.x**2) + model.obj = Objective(expr=model.x**2 - model.x**2) model.con = Constraint(expr=model.x == 1.0) def warmstart_model(self): @@ -44,14 +45,14 @@ def warmstart_model(self): model = self.model model.x.value = 1.0 + @register_model class QP_constant_objective_kernel(QP_constant_objective): - def _generate_model(self): self.model = ConcreteModel() model = self.model model._name = self.description model.x = Var(within=NonNegativeReals) - model.obj = Objective(expr=model.x**2-model.x**2) + model.obj = Objective(expr=model.x**2 - model.x**2) model.con = Constraint(expr=model.x == 1.0) diff --git a/pyomo/solvers/tests/models/QP_simple.py b/pyomo/solvers/tests/models/QP_simple.py index ec9d6cc1a77..bf5f4b8d8d6 100644 --- a/pyomo/solvers/tests/models/QP_simple.py +++ b/pyomo/solvers/tests/models/QP_simple.py @@ -10,10 +10,18 @@ # ___________________________________________________________________________ import pyomo.kernel as pmo -from pyomo.core import ConcreteModel, Param, Var, Objective, Constraint, NonNegativeReals +from pyomo.core import ( + ConcreteModel, + Param, + Var, + Objective, + Constraint, + NonNegativeReals, +) from pyomo.opt import TerminationCondition from pyomo.solvers.tests.models.base import _BaseTestModel, register_model + @register_model class QP_simple(_BaseTestModel): """ @@ -26,7 +34,7 @@ class QP_simple(_BaseTestModel): def __init__(self): _BaseTestModel.__init__(self) - self.add_results(self.description+".json") + self.add_results(self.description + ".json") def _generate_model(self): self.model = None @@ -40,9 +48,9 @@ def _generate_model(self): model.inactive_obj = Objective(expr=model.y) model.inactive_obj.deactivate() - model.obj = Objective(expr=model.x**2 + 3.0*model.inactive_obj**2 + 1.0) + model.obj = Objective(expr=model.x**2 + 3.0 * model.inactive_obj**2 + 1.0) model.c1 = Constraint(expr=model.a <= model.y) - model.c2 = Constraint(expr=(2.0, model.x/model.a - model.y, 10)) + model.c2 = Constraint(expr=(2.0, model.x / model.a - model.y, 10)) def warmstart_model(self): assert self.model is not None @@ -52,13 +60,16 @@ def warmstart_model(self): def post_solve_test_validation(self, tester, results): if tester is None: - assert results['Solver'][0]['termination condition'] in \ - (TerminationCondition.optimal, - TerminationCondition.locallyOptimal) + assert results['Solver'][0]['termination condition'] in ( + TerminationCondition.optimal, + TerminationCondition.locallyOptimal, + ) else: - tester.assertIn(results['Solver'][0]['termination condition'], - (TerminationCondition.optimal, - TerminationCondition.locallyOptimal)) + tester.assertIn( + results['Solver'][0]['termination condition'], + (TerminationCondition.optimal, TerminationCondition.locallyOptimal), + ) + @register_model class QP_simple_nosuffixes(QP_simple): @@ -71,9 +82,9 @@ def __init__(self): self.disable_suffix_tests = True self.add_results("QP_simple.json") + @register_model class QP_simple_kernel(QP_simple): - def _generate_model(self): self.model = None self.model = pmo.block() @@ -86,9 +97,10 @@ def _generate_model(self): model.inactive_obj = pmo.objective(model.y) model.inactive_obj.deactivate() - model.obj = pmo.objective(model.x**2 + 3.0*model.inactive_obj**2 + 1.0) + model.obj = pmo.objective(model.x**2 + 3.0 * model.inactive_obj**2 + 1.0) model.c1 = pmo.constraint(model.a <= model.y) - model.c2 = pmo.constraint((2.0, model.x/model.a - model.y, 10)) + model.c2 = pmo.constraint((2.0, model.x / model.a - model.y, 10)) + @register_model class QP_simple_nosuffixes_kernel(QP_simple_kernel): diff --git a/pyomo/solvers/tests/models/SOS1_simple.py b/pyomo/solvers/tests/models/SOS1_simple.py index dc40e0e2425..e6156ad5c32 100644 --- a/pyomo/solvers/tests/models/SOS1_simple.py +++ b/pyomo/solvers/tests/models/SOS1_simple.py @@ -10,9 +10,19 @@ # ___________________________________________________________________________ import pyomo.kernel as pmo -from pyomo.core import ConcreteModel, Param, Var, Objective, Constraint, NonNegativeReals, SOSConstraint, sum_product +from pyomo.core import ( + ConcreteModel, + Param, + Var, + Objective, + Constraint, + NonNegativeReals, + SOSConstraint, + sum_product, +) from pyomo.solvers.tests.models.base import _BaseTestModel, register_model + @register_model class SOS1_simple(_BaseTestModel): """ @@ -25,7 +35,7 @@ class SOS1_simple(_BaseTestModel): def __init__(self): _BaseTestModel.__init__(self) - self.add_results(self.description+".json") + self.add_results(self.description + ".json") def _generate_model(self): self.model = ConcreteModel() @@ -34,17 +44,17 @@ def _generate_model(self): model.a = Param(initialize=0.1) model.x = Var(within=NonNegativeReals) - model.y = Var([1,2],within=NonNegativeReals) + model.y = Var([1, 2], within=NonNegativeReals) - model.obj = Objective(expr=model.x + model.y[1]+2*model.y[2]) + model.obj = Objective(expr=model.x + model.y[1] + 2 * model.y[2]) model.c1 = Constraint(expr=model.a <= model.y[2]) model.c2 = Constraint(expr=(2.0, model.x, 10.0)) - model.c3 = SOSConstraint(var=model.y, index=[1,2], sos=1) + model.c3 = SOSConstraint(var=model.y, index=[1, 2], sos=1) model.c4 = Constraint(expr=sum_product(model.y) == 1) # Make an empty SOSConstraint - model.c5 = SOSConstraint(var=model.y, index=[1,2], sos=1) - model.c5.set_items([],[]) + model.c5 = SOSConstraint(var=model.y, index=[1, 2], sos=1) + model.c5.set_items([], []) assert len(list(model.c5.get_items())) == 0 def warmstart_model(self): @@ -54,9 +64,9 @@ def warmstart_model(self): model.y[1].value = 1 model.y[2].value = None + @register_model class SOS1_simple_kernel(SOS1_simple): - def _generate_model(self): self.model = pmo.block() model = self.model @@ -68,7 +78,7 @@ def _generate_model(self): model.y[1] = pmo.variable(domain=NonNegativeReals) model.y[2] = pmo.variable(domain=NonNegativeReals) - model.obj = pmo.objective(model.x + model.y[1]+2*model.y[2]) + model.obj = pmo.objective(model.x + model.y[1] + 2 * model.y[2]) model.c1 = pmo.constraint(model.a <= model.y[2]) model.c2 = pmo.constraint((2.0, model.x, 10.0)) model.c3 = pmo.sos1(model.y.values()) diff --git a/pyomo/solvers/tests/models/SOS2_simple.py b/pyomo/solvers/tests/models/SOS2_simple.py index c077e2b251d..4f192773ca4 100644 --- a/pyomo/solvers/tests/models/SOS2_simple.py +++ b/pyomo/solvers/tests/models/SOS2_simple.py @@ -10,9 +10,20 @@ # ___________________________________________________________________________ import pyomo.kernel as pmo -from pyomo.core import ConcreteModel, Param, Var, Objective, Constraint, SOSConstraint, NonNegativeReals, ConstraintList, sum_product +from pyomo.core import ( + ConcreteModel, + Param, + Var, + Objective, + Constraint, + SOSConstraint, + NonNegativeReals, + ConstraintList, + sum_product, +) from pyomo.solvers.tests.models.base import _BaseTestModel, register_model + @register_model class SOS2_simple(_BaseTestModel): """ @@ -25,7 +36,7 @@ class SOS2_simple(_BaseTestModel): def __init__(self): _BaseTestModel.__init__(self) - self.add_results(self.description+".json") + self.add_results(self.description + ".json") def _generate_model(self): self.model = ConcreteModel() @@ -33,56 +44,56 @@ def _generate_model(self): model._name = self.description model.f = Var() - model.x = Var(bounds=(1,3)) - model.fi = Param([1,2,3],mutable=True) + model.x = Var(bounds=(1, 3)) + model.fi = Param([1, 2, 3], mutable=True) model.fi[1] = 1.0 model.fi[2] = 2.0 model.fi[3] = 0.0 - model.xi = Param([1,2,3],mutable=True) + model.xi = Param([1, 2, 3], mutable=True) model.xi[1] = 1.0 model.xi[2] = 2.0 model.xi[3] = 3.0 model.p = Var(within=NonNegativeReals) model.n = Var(within=NonNegativeReals) - model.lmbda = Var([1,2,3]) - model.obj = Objective(expr=model.p+model.n) + model.lmbda = Var([1, 2, 3]) + model.obj = Objective(expr=model.p + model.n) model.c1 = ConstraintList() model.c1.add((0.0, model.lmbda[1], 1.0)) model.c1.add((0.0, model.lmbda[2], 1.0)) model.c1.add(0.0 <= model.lmbda[3]) - model.c2 = SOSConstraint(var=model.lmbda, index=[1,2,3], sos=2) + model.c2 = SOSConstraint(var=model.lmbda, index=[1, 2, 3], sos=2) model.c3 = Constraint(expr=sum_product(model.lmbda) == 1) - model.c4 = Constraint(expr=model.f==sum_product(model.fi,model.lmbda)) - model.c5 = Constraint(expr=model.x==sum_product(model.xi,model.lmbda)) + model.c4 = Constraint(expr=model.f == sum_product(model.fi, model.lmbda)) + model.c5 = Constraint(expr=model.x == sum_product(model.xi, model.lmbda)) model.x = 2.75 model.x.fixed = True # Make an empty SOSConstraint - model.c6 = SOSConstraint(var=model.lmbda, index=[1,2,3], sos=2) - model.c6.set_items([],[]) + model.c6 = SOSConstraint(var=model.lmbda, index=[1, 2, 3], sos=2) + model.c6.set_items([], []) assert len(list(model.c6.get_items())) == 0 def warmstart_model(self): assert self.model is not None model = self.model model.f.value = 0 - assert model.x.value == 2.75 # Fixed + assert model.x.value == 2.75 # Fixed model.p.value = 1 model.n.value = 0 model.lmbda[1].value = None model.lmbda[2].value = None model.lmbda[3].value = 1 + @register_model class SOS2_simple_kernel(SOS2_simple): - def _generate_model(self): self.model = pmo.block() model = self.model model._name = self.description model.f = pmo.variable() - model.x = pmo.variable(lb=1,ub=3) + model.x = pmo.variable(lb=1, ub=3) model.fi = pmo.parameter_dict() model.fi[1] = pmo.parameter(value=1.0) model.fi[2] = pmo.parameter(value=2.0) @@ -93,19 +104,20 @@ def _generate_model(self): model.xi[3] = pmo.parameter(value=3.0) model.p = pmo.variable(domain=NonNegativeReals) model.n = pmo.variable(domain=NonNegativeReals) - model.lmbda = pmo.variable_dict( - (i, pmo.variable()) for i in range(1,4)) - model.obj = pmo.objective(model.p+model.n) + model.lmbda = pmo.variable_dict((i, pmo.variable()) for i in range(1, 4)) + model.obj = pmo.objective(model.p + model.n) model.c1 = pmo.constraint_dict() model.c1[1] = pmo.constraint((0.0, model.lmbda[1], 1.0)) model.c1[2] = pmo.constraint((0.0, model.lmbda[2], 1.0)) model.c1[3] = pmo.constraint(0.0 <= model.lmbda[3]) model.c2 = pmo.sos2(model.lmbda.values()) model.c3 = pmo.constraint(sum(model.lmbda.values()) == 1) - model.c4 = pmo.constraint(model.f==sum(model.fi[i]*model.lmbda[i] - for i in model.lmbda)) - model.c5 = pmo.constraint(model.x==sum(model.xi[i]*model.lmbda[i] - for i in model.lmbda)) + model.c4 = pmo.constraint( + model.f == sum(model.fi[i] * model.lmbda[i] for i in model.lmbda) + ) + model.c5 = pmo.constraint( + model.x == sum(model.xi[i] * model.lmbda[i] for i in model.lmbda) + ) model.x.fix(2.75) # Make an empty SOS constraint diff --git a/pyomo/solvers/tests/models/__init__.py b/pyomo/solvers/tests/models/__init__.py index 157b7512887..c6a550397d5 100644 --- a/pyomo/solvers/tests/models/__init__.py +++ b/pyomo/solvers/tests/models/__init__.py @@ -25,8 +25,9 @@ import pyomo.solvers.tests.models.LP_trivial_constraints import pyomo.solvers.tests.models.LP_unbounded import pyomo.solvers.tests.models.LP_unused_vars + # WEH - Omitting this for because it's not reliably solved by ipopt -#import pyomo.solvers.tests.models.LP_unique_duals +# import pyomo.solvers.tests.models.LP_unique_duals import pyomo.solvers.tests.models.MILP_discrete_var_bounds import pyomo.solvers.tests.models.MILP_infeasible1 diff --git a/pyomo/solvers/tests/models/base.py b/pyomo/solvers/tests/models/base.py index d7115b378af..a75a78a7b86 100644 --- a/pyomo/solvers/tests/models/base.py +++ b/pyomo/solvers/tests/models/base.py @@ -20,7 +20,7 @@ from pyomo.opt import ProblemFormat, SolverFactory, TerminationCondition from pyomo.solvers.plugins.solvers.persistent_solver import PersistentSolver -thisDir = dirname(abspath( __file__ )) +thisDir = dirname(abspath(__file__)) _test_models = {} @@ -36,7 +36,7 @@ def all_models(arg=None): def register_model(cls): - """ Decorator for test model classes """ + """Decorator for test model classes""" global _test_models assert cls.__name__ not in _test_models _test_models[cls.__name__] = cls @@ -62,15 +62,14 @@ def __init__(self): self.solve_should_fail = False def add_results(self, filename): - """ Add results file """ + """Add results file""" self.results_file = join(thisDir, filename) def generate_model(self, import_suffixes=[]): - """ Generate the model """ + """Generate the model""" self._generate_model() # Add suffixes - self.test_suffixes = [] if self.disable_suffix_tests else \ - import_suffixes + self.test_suffixes = [] if self.disable_suffix_tests else import_suffixes if isinstance(self.model, IBlock): for suffix in self.test_suffixes: setattr(self.model, suffix, pmo.suffix(direction=pmo.suffix.IMPORT)) @@ -78,14 +77,10 @@ def generate_model(self, import_suffixes=[]): for suffix in self.test_suffixes: setattr(self.model, suffix, Suffix(direction=Suffix.IMPORT)) - def solve(self, - solver, - io, - io_options, - solver_options, - symbolic_labels, - load_solutions): - """ Optimize the model """ + def solve( + self, solver, io, io_options, solver_options, symbolic_labels, load_solutions + ): + """Optimize the model""" assert self.model is not None if not io_options: @@ -102,7 +97,7 @@ def solve(self, assert opt.problem_format() == ProblemFormat.cpxlp elif io == 'mps': assert opt.problem_format() == ProblemFormat.mps - #elif io == 'python': + # elif io == 'python': # print opt.problem_format() # assert opt.problem_format() is None @@ -110,12 +105,11 @@ def solve(self, if isinstance(opt, PersistentSolver): opt.set_instance(self.model, symbolic_solver_labels=symbolic_labels) if opt.warm_start_capable(): - results = opt.solve(warmstart=True, - load_solutions=load_solutions, - **io_options) + results = opt.solve( + warmstart=True, load_solutions=load_solutions, **io_options + ) else: - results = opt.solve(load_solutions=load_solutions, - **io_options) + results = opt.solve(load_solutions=load_solutions, **io_options) else: if opt.warm_start_capable(): results = opt.solve( @@ -123,36 +117,39 @@ def solve(self, symbolic_solver_labels=symbolic_labels, warmstart=True, load_solutions=load_solutions, - **io_options) + **io_options + ) else: results = opt.solve( self.model, symbolic_solver_labels=symbolic_labels, load_solutions=load_solutions, - **io_options) + **io_options + ) return opt, results finally: pass - #opt.deactivate() + # opt.deactivate() del opt return None, None def save_current_solution(self, filename, **kwds): - """ Save the solution in a specified file name """ + """Save the solution in a specified file name""" assert self.model is not None model = self.model - suffixes = dict((suffix, getattr(model,suffix)) - for suffix in kwds.pop('suffixes',[])) + suffixes = dict( + (suffix, getattr(model, suffix)) for suffix in kwds.pop('suffixes', []) + ) for suf in suffixes.values(): if isinstance(self.model, IBlock): - assert isinstance(suf,pmo.suffix) + assert isinstance(suf, pmo.suffix) assert suf.import_enabled else: - assert isinstance(suf,Suffix) + assert isinstance(suf, Suffix) assert suf.import_enabled() - with open(filename,'w') as f: + with open(filename, 'w') as f: # # Collect Block, Variable, Constraint, Objective and Suffix data # @@ -195,231 +192,287 @@ def validate_current_solution(self, **kwds): assert self.model is not None assert self.results_file is not None model = self.model - suffixes = dict((suffix, getattr(model,suffix)) - for suffix in kwds.pop('suffixes',[])) - exclude = kwds.pop('exclude_suffixes',set()) + suffixes = dict( + (suffix, getattr(model, suffix)) for suffix in kwds.pop('suffixes', []) + ) + exclude = kwds.pop('exclude_suffixes', set()) for suf in suffixes.values(): if isinstance(self.model, IBlock): - assert isinstance(suf,pmo.suffix) + assert isinstance(suf, pmo.suffix) assert suf.import_enabled else: - assert isinstance(suf,Suffix) + assert isinstance(suf, Suffix) assert suf.import_enabled() solution = None - error_str = ("Difference in solution for {0}.{1}:\n\tBaseline " - "- {2}\n\tCurrent - {3}") + error_str = ( + "Difference in solution for {0}.{1}:\n\tBaseline " "- {2}\n\tCurrent - {3}" + ) - with open(self.results_file,'r') as f: + with open(self.results_file, 'r') as f: try: solution = json.load(f) except: - return (False,"Problem reading file "+self.results_file) + return (False, "Problem reading file " + self.results_file) for var in model.component_data_objects(Var): var_value_sol = solution[var.name]['value'] var_value = var.value if not ((var_value is None) and (var_value_sol is None)): - if ((var_value is None) ^ (var_value_sol is None)) or \ - (abs(var_value_sol - var_value) > self.diff_tol): - return (False, - error_str.format(var.name, - 'value', - var_value_sol, - var_value)) + if ((var_value is None) ^ (var_value_sol is None)) or ( + abs(var_value_sol - var_value) > self.diff_tol + ): + return ( + False, + error_str.format(var.name, 'value', var_value_sol, var_value), + ) if not (solution[var.name]['stale'] is var.stale): - return (False, - error_str.format(var.name, - 'stale', - solution[var.name]['stale'], - var.stale)) + return ( + False, + error_str.format( + var.name, 'stale', solution[var.name]['stale'], var.stale + ), + ) for suffix_name, suffix in suffixes.items(): _ex = exclude.get(suffix_name, None) if suffix_name in solution[var.name]: if suffix.get(var) is None: - if _ex is not None and ( - not _ex[1] or var.name in _ex[1] ): + if _ex is not None and (not _ex[1] or var.name in _ex[1]): continue - if not(solution[var.name][suffix_name] in \ - solution["suffix defaults"][suffix_name]): - return (False, - error_str.format( - var.name, - suffix, - solution[var.name][suffix_name], - "none defined")) - elif _ex is not None and _ex[0] and ( - not _ex[1] or var.name in _ex[1] ): - return ( - False, - "Expected solution to be missing suffix %s" - % suffix_name) - elif not abs(solution[var.name][suffix_name] - \ - suffix.get(var)) < self.diff_tol: - return (False, + if not ( + solution[var.name][suffix_name] + in solution["suffix defaults"][suffix_name] + ): + return ( + False, error_str.format( var.name, suffix, solution[var.name][suffix_name], - suffix.get(var))) + "none defined", + ), + ) + elif ( + _ex is not None + and _ex[0] + and (not _ex[1] or var.name in _ex[1]) + ): + return ( + False, + "Expected solution to be missing suffix %s" % suffix_name, + ) + elif ( + not abs(solution[var.name][suffix_name] - suffix.get(var)) + < self.diff_tol + ): + return ( + False, + error_str.format( + var.name, + suffix, + solution[var.name][suffix_name], + suffix.get(var), + ), + ) for con in model.component_data_objects(Constraint): con_value_sol = solution[con.name]['value'] con_value = con(exception=False) if not ((con_value is None) and (con_value_sol is None)): - if ((con_value is None) ^ (con_value_sol is None)) or \ - (abs(con_value_sol - con_value) > self.diff_tol): - return (False, - error_str.format(con.name, - 'value', - con_value_sol, - con_value)) + if ((con_value is None) ^ (con_value_sol is None)) or ( + abs(con_value_sol - con_value) > self.diff_tol + ): + return ( + False, + error_str.format(con.name, 'value', con_value_sol, con_value), + ) for suffix_name, suffix in suffixes.items(): _ex = exclude.get(suffix_name, None) if suffix_name in solution[con.name]: if suffix.get(con) is None: - if _ex is not None and ( - not _ex[1] or con.name in _ex[1] ): + if _ex is not None and (not _ex[1] or con.name in _ex[1]): continue - if not (solution[con.name][suffix_name] in \ - solution["suffix defaults"][suffix_name]): - return (False, - error_str.format( - con.name, - suffix, - solution[con.name][suffix_name], - "none defined")) - elif _ex is not None and _ex[0] and ( - not _ex[1] or con.name in _ex[1] ): - return ( - False, - "Expected solution to be missing suffix %s" - % suffix_name) - elif not abs(solution[con.name][suffix_name] - \ - suffix.get(con)) < self.diff_tol: - return (False, + if not ( + solution[con.name][suffix_name] + in solution["suffix defaults"][suffix_name] + ): + return ( + False, error_str.format( con.name, suffix, solution[con.name][suffix_name], - suffix.get(con))) + "none defined", + ), + ) + elif ( + _ex is not None + and _ex[0] + and (not _ex[1] or con.name in _ex[1]) + ): + return ( + False, + "Expected solution to be missing suffix %s" % suffix_name, + ) + elif ( + not abs(solution[con.name][suffix_name] - suffix.get(con)) + < self.diff_tol + ): + return ( + False, + error_str.format( + con.name, + suffix, + solution[con.name][suffix_name], + suffix.get(con), + ), + ) for obj in model.component_data_objects(Objective): obj_value_sol = solution[obj.name]['value'] obj_value = obj(exception=False) if not ((obj_value is None) and (obj_value_sol is None)): - if ((obj_value is None) ^ (obj_value_sol is None)) or \ - (abs(obj_value_sol - obj_value) > self.diff_tol): - return (False, - error_str.format(obj.name, - 'value', - obj_value_sol, - obj_value)) + if ((obj_value is None) ^ (obj_value_sol is None)) or ( + abs(obj_value_sol - obj_value) > self.diff_tol + ): + return ( + False, + error_str.format(obj.name, 'value', obj_value_sol, obj_value), + ) for suffix_name, suffix in suffixes.items(): _ex = exclude.get(suffix_name, None) if suffix_name in solution[obj.name]: if suffix.get(obj) is None: - if _ex is not None and ( - not _ex[1] or obj.name in _ex[1] ): + if _ex is not None and (not _ex[1] or obj.name in _ex[1]): continue - if not(solution[obj.name][suffix_name] in \ - solution["suffix defaults"][suffix_name]): - return (False, - error_str.format( - obj.name, - suffix, - solution[obj.name][suffix_name], - "none defined")) - elif _ex is not None and _ex[0] and ( - not _ex[1] or obj.name in _ex[1] ): - return ( - False, - "Expected solution to be missing suffix %s" - % suffix_name) - elif not abs(solution[obj.name][suffix_name] - \ - suffix.get(obj)) < self.diff_tol: - return (False, + if not ( + solution[obj.name][suffix_name] + in solution["suffix defaults"][suffix_name] + ): + return ( + False, error_str.format( obj.name, suffix, solution[obj.name][suffix_name], - suffix.get(obj))) - - first=True + "none defined", + ), + ) + elif ( + _ex is not None + and _ex[0] + and (not _ex[1] or obj.name in _ex[1]) + ): + return ( + False, + "Expected solution to be missing suffix %s" % suffix_name, + ) + elif ( + not abs(solution[obj.name][suffix_name] - suffix.get(obj)) + < self.diff_tol + ): + return ( + False, + error_str.format( + obj.name, + suffix, + solution[obj.name][suffix_name], + suffix.get(obj), + ), + ) + + first = True for block in model.block_data_objects(): if first: - first=False + first = False continue for suffix_name, suffix in suffixes.items(): _ex = exclude.get(suffix_name, None) - if (solution[block.name] is not None) and \ - (suffix_name in solution[block.name]): + if (solution[block.name] is not None) and ( + suffix_name in solution[block.name] + ): if suffix.get(block) is None: - if _ex is not None and ( - not _ex[1] or block.name in _ex[1] ): + if _ex is not None and (not _ex[1] or block.name in _ex[1]): continue - if not(solution[block.name][suffix_name] in \ - solution["suffix defaults"][suffix_name]): - return (False, - error_str.format( - block.name, - suffix, - solution[block.name][suffix_name], - "none defined")) - elif _ex is not None and _ex[0] and ( - not _ex[1] or block.name in _ex[1] ): - return ( - False, - "Expected solution to be missing suffix %s" - % suffix_name) - elif not abs(solution[block.name][suffix_name] - \ - suffix.get(block)) < self.diff_tol: - return (False, + if not ( + solution[block.name][suffix_name] + in solution["suffix defaults"][suffix_name] + ): + return ( + False, error_str.format( block.name, suffix, solution[block.name][suffix_name], - suffix.get(block))) - return (True,"") + "none defined", + ), + ) + elif ( + _ex is not None + and _ex[0] + and (not _ex[1] or block.name in _ex[1]) + ): + return ( + False, + "Expected solution to be missing suffix %s" % suffix_name, + ) + elif ( + not abs(solution[block.name][suffix_name] - suffix.get(block)) + < self.diff_tol + ): + return ( + False, + error_str.format( + block.name, + suffix, + solution[block.name][suffix_name], + suffix.get(block), + ), + ) + return (True, "") def validate_capabilities(self, opt): - """ Validate the capabilites of the optimizer """ - if (self.linear is True) and \ - (not opt.has_capability('linear') is True): + """Validate the capabilites of the optimizer""" + if (self.linear is True) and (not opt.has_capability('linear') is True): return False - if (self.integer is True) and \ - (not opt.has_capability('integer') is True): + if (self.integer is True) and (not opt.has_capability('integer') is True): return False - if (self.quadratic_objective is True) and \ - (not opt.has_capability('quadratic_objective') is True): + if (self.quadratic_objective is True) and ( + not opt.has_capability('quadratic_objective') is True + ): return False - if (self.quadratic_constraint is True) and \ - (not opt.has_capability('quadratic_constraint') is True): + if (self.quadratic_constraint is True) and ( + not opt.has_capability('quadratic_constraint') is True + ): return False - if (self.sos1 is True) and \ - (not opt.has_capability('sos1') is True): + if (self.sos1 is True) and (not opt.has_capability('sos1') is True): return False - if (self.sos2 is True) and \ - (not opt.has_capability('sos2') is True): + if (self.sos2 is True) and (not opt.has_capability('sos2') is True): return False return True def post_solve_test_validation(self, tester, results): - """ Perform post-solve validation tests """ + """Perform post-solve validation tests""" if tester is None: - assert results['Solver'][0]['termination condition'] == TerminationCondition.optimal + assert ( + results['Solver'][0]['termination condition'] + == TerminationCondition.optimal + ) else: - tester.assertEqual(results['Solver'][0]['termination condition'], TerminationCondition.optimal) + tester.assertEqual( + results['Solver'][0]['termination condition'], + TerminationCondition.optimal, + ) def warmstart_model(self): - """ Initialize model parameters """ + """Initialize model parameters""" pass if __name__ == "__main__": import pyomo.solvers.tests.models + for key, value in _test_models.items(): print(key) obj = value() obj.generate_model() obj.warmstart_model() - diff --git a/pyomo/solvers/tests/piecewise_linear/kernel_problems/concave_var.py b/pyomo/solvers/tests/piecewise_linear/kernel_problems/concave_var.py index ab548b09060..a8024992d61 100644 --- a/pyomo/solvers/tests/piecewise_linear/kernel_problems/concave_var.py +++ b/pyomo/solvers/tests/piecewise_linear/kernel_problems/concave_var.py @@ -9,10 +9,20 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.kernel import block, variable, variable_list, block_list, piecewise, objective, constraint, constraint_list +from pyomo.kernel import ( + block, + variable, + variable_list, + block_list, + piecewise, + objective, + constraint, + constraint_list, +) + +breakpoints = list(range(-5, 0)) + list(range(1, 5)) +values = [-(x**2) for x in breakpoints] -breakpoints = list(range(-5,0))+list(range(1,5)) -values = [-x**2 for x in breakpoints] def define_model(**kwds): @@ -27,13 +37,10 @@ def define_model(**kwds): m.x.append(variable(lb=-5, ub=4)) m.Fx.append(variable()) m.piecewise.append( - piecewise(breakpoints, values, - input=m.x[i], - output=m.Fx[i], - **kwds)) + piecewise(breakpoints, values, input=m.x[i], output=m.Fx[i], **kwds) + ) - m.obj = objective(expr=sum(m.Fx), - sense=sense) + m.obj = objective(expr=sum(m.Fx), sense=sense) # fix the answer for testing purposes m.set_answer = constraint_list() diff --git a/pyomo/solvers/tests/piecewise_linear/kernel_problems/convex_var.py b/pyomo/solvers/tests/piecewise_linear/kernel_problems/convex_var.py index 9de3a614b06..76c9634d5dc 100644 --- a/pyomo/solvers/tests/piecewise_linear/kernel_problems/convex_var.py +++ b/pyomo/solvers/tests/piecewise_linear/kernel_problems/convex_var.py @@ -9,11 +9,21 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.kernel import block, variable, variable_list, block_list, piecewise, objective, constraint, constraint_list - -breakpoints = list(range(-5,0))+list(range(1,5)) +from pyomo.kernel import ( + block, + variable, + variable_list, + block_list, + piecewise, + objective, + constraint, + constraint_list, +) + +breakpoints = list(range(-5, 0)) + list(range(1, 5)) values = [x**2 for x in breakpoints] + def define_model(**kwds): sense = kwds.pop("sense") @@ -27,13 +37,10 @@ def define_model(**kwds): m.x.append(variable(lb=-5, ub=4)) m.Fx.append(variable()) m.piecewise.append( - piecewise(breakpoints, values, - input=m.x[i], - output=m.Fx[i], - **kwds)) + piecewise(breakpoints, values, input=m.x[i], output=m.Fx[i], **kwds) + ) - m.obj = objective(expr=sum(m.Fx), - sense=sense) + m.obj = objective(expr=sum(m.Fx), sense=sense) # fix the answer for testing purposes m.set_answer = constraint_list() diff --git a/pyomo/solvers/tests/piecewise_linear/kernel_problems/piecewise_var.py b/pyomo/solvers/tests/piecewise_linear/kernel_problems/piecewise_var.py index 37c6e585268..7172f3b70a5 100644 --- a/pyomo/solvers/tests/piecewise_linear/kernel_problems/piecewise_var.py +++ b/pyomo/solvers/tests/piecewise_linear/kernel_problems/piecewise_var.py @@ -9,10 +9,20 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.kernel import block, variable, variable_list, block_list, piecewise, objective, constraint, constraint_list +from pyomo.kernel import ( + block, + variable, + variable_list, + block_list, + piecewise, + objective, + constraint, + constraint_list, +) + +breakpoints = [0, 1, 3, 5, 6] +values = [0, 2, 3, -3, -1] -breakpoints = [0,1,3,5,6] -values = [0,2,3,-3,-1] def define_model(**kwds): @@ -27,13 +37,10 @@ def define_model(**kwds): m.x.append(variable(lb=0, ub=6)) m.Fx.append(variable()) m.piecewise.append( - piecewise(breakpoints, values, - input=m.x[i], - output=m.Fx[i], - **kwds)) + piecewise(breakpoints, values, input=m.x[i], output=m.Fx[i], **kwds) + ) - m.obj = objective(expr=sum(m.Fx), - sense=sense) + m.obj = objective(expr=sum(m.Fx), sense=sense) # fix the answer for testing purposes m.set_answer = constraint_list() diff --git a/pyomo/solvers/tests/piecewise_linear/kernel_problems/step_var.py b/pyomo/solvers/tests/piecewise_linear/kernel_problems/step_var.py index b237130eeb9..c1964ec64c8 100644 --- a/pyomo/solvers/tests/piecewise_linear/kernel_problems/step_var.py +++ b/pyomo/solvers/tests/piecewise_linear/kernel_problems/step_var.py @@ -9,11 +9,21 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.kernel import block, variable, variable_list, block_list, piecewise, objective, constraint, constraint_list +from pyomo.kernel import ( + block, + variable, + variable_list, + block_list, + piecewise, + objective, + constraint, + constraint_list, +) breakpoints = [0, 1, 1, 2, 3] values = [0, 0, 1, 1, 2] + def define_model(**kwds): sense = kwds.pop("sense") @@ -27,22 +37,19 @@ def define_model(**kwds): m.x.append(variable(lb=0, ub=3)) m.Fx.append(variable()) m.piecewise.append( - piecewise(breakpoints, values, - input=m.x[i], - output=m.Fx[i], - **kwds)) - m.obj = objective(expr=sum(m.Fx) + sum(m.x), - sense=sense) + piecewise(breakpoints, values, input=m.x[i], output=m.Fx[i], **kwds) + ) + m.obj = objective(expr=sum(m.Fx) + sum(m.x), sense=sense) # fix the answer for testing purposes m.set_answer = constraint_list() # Fx1 should solve to 0 - m.set_answer.append(constraint(expr= m.x[0] == 0.5)) - m.set_answer.append(constraint(expr= m.x[1] == 1.0)) - m.set_answer.append(constraint(expr= m.Fx[1] == 0.5)) + m.set_answer.append(constraint(expr=m.x[0] == 0.5)) + m.set_answer.append(constraint(expr=m.x[1] == 1.0)) + m.set_answer.append(constraint(expr=m.Fx[1] == 0.5)) # Fx[2] should solve to 1 - m.set_answer.append(constraint(expr= m.x[2] == 1.5)) + m.set_answer.append(constraint(expr=m.x[2] == 1.5)) # Fx[3] should solve to 1.5 - m.set_answer.append(constraint(expr= m.x[3] == 2.5)) + m.set_answer.append(constraint(expr=m.x[3] == 2.5)) return m diff --git a/pyomo/solvers/tests/piecewise_linear/problems/concave_multi_vararray1.py b/pyomo/solvers/tests/piecewise_linear/problems/concave_multi_vararray1.py index 587cd650243..31d3a9ebf1d 100644 --- a/pyomo/solvers/tests/piecewise_linear/problems/concave_multi_vararray1.py +++ b/pyomo/solvers/tests/piecewise_linear/problems/concave_multi_vararray1.py @@ -20,43 +20,60 @@ \ -7x+12, 3 <= x <= 4 """ -from pyomo.core import ConcreteModel, Var, Objective, Param, Piecewise, Constraint, maximize, sum_product +from pyomo.core import ( + ConcreteModel, + Var, + Objective, + Param, + Piecewise, + Constraint, + maximize, + sum_product, +) -INDEX_SET1 = range(1,8) # There will be two copies of this function -INDEX_SET2 = range(0,2) -DOMAIN_PTS = dict([((t1,t2),[float(i) for i in (list(range(-5,0))+list(range(1,5)))]) for t1 in INDEX_SET1 for t2 in INDEX_SET2]) +INDEX_SET1 = range(1, 8) # There will be two copies of this function +INDEX_SET2 = range(0, 2) +DOMAIN_PTS = dict( + [ + ((t1, t2), [float(i) for i in (list(range(-5, 0)) + list(range(1, 5)))]) + for t1 in INDEX_SET1 + for t2 in INDEX_SET2 + ] +) + + +def F(model, t1, t2, x): + return -(x**2) * model.p[t1, t2] -def F(model,t1,t2,x): - return -(x**2)*model.p[t1,t2] def define_model(**kwds): model = ConcreteModel() - model.x = Var(INDEX_SET1, INDEX_SET2, bounds=(-5,4)) # domain variable - model.Fx = Var(INDEX_SET1, INDEX_SET2) # range variable + model.x = Var(INDEX_SET1, INDEX_SET2, bounds=(-5, 4)) # domain variable + model.Fx = Var(INDEX_SET1, INDEX_SET2) # range variable model.p = Param(INDEX_SET1, INDEX_SET2, initialize=1.0) - model.obj = Objective(expr=sum_product(model.Fx), sense=kwds.pop('sense',maximize)) - - model.piecewise = Piecewise(INDEX_SET1,INDEX_SET2,model.Fx,model.x, - pw_pts=DOMAIN_PTS, - f_rule=F, **kwds) - - #Fix the answer for testing purpose - model.set_answer_constraint1 = Constraint(expr= model.x[1,0] == -5.0) - model.set_answer_constraint2 = Constraint(expr= model.x[2,0] == -3.0) - model.set_answer_constraint3 = Constraint(expr= model.x[3,0] == -2.5) - model.set_answer_constraint4 = Constraint(expr= model.x[4,0] == -1.5) - model.set_answer_constraint5 = Constraint(expr= model.x[5,0] == 2.0) - model.set_answer_constraint6 = Constraint(expr= model.x[6,0] == 3.5) - model.set_answer_constraint7 = Constraint(expr= model.x[7,0] == 4.0) - model.set_answer_constraint8 = Constraint(expr= model.x[1,1] == -5.0) - model.set_answer_constraint9 = Constraint(expr= model.x[2,1] == -3.0) - model.set_answer_constraint10 = Constraint(expr= model.x[3,1] == -2.5) - model.set_answer_constraint11 = Constraint(expr= model.x[4,1] == -1.5) - model.set_answer_constraint12 = Constraint(expr= model.x[5,1] == 2.0) - model.set_answer_constraint13 = Constraint(expr= model.x[6,1] == 3.5) - model.set_answer_constraint14 = Constraint(expr= model.x[7,1] == 4.0) + model.obj = Objective(expr=sum_product(model.Fx), sense=kwds.pop('sense', maximize)) + + model.piecewise = Piecewise( + INDEX_SET1, INDEX_SET2, model.Fx, model.x, pw_pts=DOMAIN_PTS, f_rule=F, **kwds + ) + + # Fix the answer for testing purpose + model.set_answer_constraint1 = Constraint(expr=model.x[1, 0] == -5.0) + model.set_answer_constraint2 = Constraint(expr=model.x[2, 0] == -3.0) + model.set_answer_constraint3 = Constraint(expr=model.x[3, 0] == -2.5) + model.set_answer_constraint4 = Constraint(expr=model.x[4, 0] == -1.5) + model.set_answer_constraint5 = Constraint(expr=model.x[5, 0] == 2.0) + model.set_answer_constraint6 = Constraint(expr=model.x[6, 0] == 3.5) + model.set_answer_constraint7 = Constraint(expr=model.x[7, 0] == 4.0) + model.set_answer_constraint8 = Constraint(expr=model.x[1, 1] == -5.0) + model.set_answer_constraint9 = Constraint(expr=model.x[2, 1] == -3.0) + model.set_answer_constraint10 = Constraint(expr=model.x[3, 1] == -2.5) + model.set_answer_constraint11 = Constraint(expr=model.x[4, 1] == -1.5) + model.set_answer_constraint12 = Constraint(expr=model.x[5, 1] == 2.0) + model.set_answer_constraint13 = Constraint(expr=model.x[6, 1] == 3.5) + model.set_answer_constraint14 = Constraint(expr=model.x[7, 1] == 4.0) return model diff --git a/pyomo/solvers/tests/piecewise_linear/problems/concave_multi_vararray2.py b/pyomo/solvers/tests/piecewise_linear/problems/concave_multi_vararray2.py index 9610cf9e414..2d13ca4ff0f 100644 --- a/pyomo/solvers/tests/piecewise_linear/problems/concave_multi_vararray2.py +++ b/pyomo/solvers/tests/piecewise_linear/problems/concave_multi_vararray2.py @@ -20,43 +20,59 @@ \ -7x+12, 3 <= x <= 4 """ -from pyomo.core import ConcreteModel, Var, Objective, Param, Piecewise, Constraint, maximize, sum_product +from pyomo.core import ( + ConcreteModel, + Var, + Objective, + Param, + Piecewise, + Constraint, + maximize, + sum_product, +) -INDEX_SET = [(t1,t2) for t1 in range(1,8) for t2 in range(0,2)] -DOMAIN_PTS = dict([(t,[float(i) for i in (list(range(-5,0))+list(range(1,5)))]) for t in INDEX_SET]) +INDEX_SET = [(t1, t2) for t1 in range(1, 8) for t2 in range(0, 2)] +DOMAIN_PTS = dict( + [ + (t, [float(i) for i in (list(range(-5, 0)) + list(range(1, 5)))]) + for t in INDEX_SET + ] +) + + +def F(model, t1, t2, x): + return -(x**2) * model.p[t1, t2] -def F(model,t1,t2,x): - return -(x**2)*model.p[t1,t2] def define_model(**kwds): model = ConcreteModel() - model.x = Var(INDEX_SET,bounds=(-5,4)) # domain variable - model.Fx = Var(INDEX_SET) # range variable + model.x = Var(INDEX_SET, bounds=(-5, 4)) # domain variable + model.Fx = Var(INDEX_SET) # range variable model.p = Param(INDEX_SET, initialize=1.0) - model.obj = Objective(expr=sum_product(model.Fx), sense=kwds.pop('sense',maximize)) - - model.piecewise = Piecewise(INDEX_SET,model.Fx,model.x, - pw_pts=DOMAIN_PTS, - f_rule=F, **kwds) - - #Fix the answer for testing purpose - model.set_answer_constraint1 = Constraint(expr= model.x[1,0] == -5.0) - model.set_answer_constraint2 = Constraint(expr= model.x[2,0] == -3.0) - model.set_answer_constraint3 = Constraint(expr= model.x[3,0] == -2.5) - model.set_answer_constraint4 = Constraint(expr= model.x[4,0] == -1.5) - model.set_answer_constraint5 = Constraint(expr= model.x[5,0] == 2.0) - model.set_answer_constraint6 = Constraint(expr= model.x[6,0] == 3.5) - model.set_answer_constraint7 = Constraint(expr= model.x[7,0] == 4.0) - model.set_answer_constraint8 = Constraint(expr= model.x[1,1] == -5.0) - model.set_answer_constraint9 = Constraint(expr= model.x[2,1] == -3.0) - model.set_answer_constraint10 = Constraint(expr= model.x[3,1] == -2.5) - model.set_answer_constraint11 = Constraint(expr= model.x[4,1] == -1.5) - model.set_answer_constraint12 = Constraint(expr= model.x[5,1] == 2.0) - model.set_answer_constraint13 = Constraint(expr= model.x[6,1] == 3.5) - model.set_answer_constraint14 = Constraint(expr= model.x[7,1] == 4.0) + model.obj = Objective(expr=sum_product(model.Fx), sense=kwds.pop('sense', maximize)) + + model.piecewise = Piecewise( + INDEX_SET, model.Fx, model.x, pw_pts=DOMAIN_PTS, f_rule=F, **kwds + ) + + # Fix the answer for testing purpose + model.set_answer_constraint1 = Constraint(expr=model.x[1, 0] == -5.0) + model.set_answer_constraint2 = Constraint(expr=model.x[2, 0] == -3.0) + model.set_answer_constraint3 = Constraint(expr=model.x[3, 0] == -2.5) + model.set_answer_constraint4 = Constraint(expr=model.x[4, 0] == -1.5) + model.set_answer_constraint5 = Constraint(expr=model.x[5, 0] == 2.0) + model.set_answer_constraint6 = Constraint(expr=model.x[6, 0] == 3.5) + model.set_answer_constraint7 = Constraint(expr=model.x[7, 0] == 4.0) + model.set_answer_constraint8 = Constraint(expr=model.x[1, 1] == -5.0) + model.set_answer_constraint9 = Constraint(expr=model.x[2, 1] == -3.0) + model.set_answer_constraint10 = Constraint(expr=model.x[3, 1] == -2.5) + model.set_answer_constraint11 = Constraint(expr=model.x[4, 1] == -1.5) + model.set_answer_constraint12 = Constraint(expr=model.x[5, 1] == 2.0) + model.set_answer_constraint13 = Constraint(expr=model.x[6, 1] == 3.5) + model.set_answer_constraint14 = Constraint(expr=model.x[7, 1] == 4.0) return model diff --git a/pyomo/solvers/tests/piecewise_linear/problems/concave_var.py b/pyomo/solvers/tests/piecewise_linear/problems/concave_var.py index c1b64577287..4caf06d3ce8 100644 --- a/pyomo/solvers/tests/piecewise_linear/problems/concave_var.py +++ b/pyomo/solvers/tests/piecewise_linear/problems/concave_var.py @@ -20,64 +20,75 @@ \ -7x+12, 3 <= x <= 4 """ -from pyomo.core import ConcreteModel, Var, Objective, Piecewise, Constraint, maximize +from pyomo.core import ConcreteModel, Var, Objective, Piecewise, Constraint, maximize -DOMAIN_PTS = [float(i) for i in (list(range(-5,0))+list(range(1,5)))] +DOMAIN_PTS = [float(i) for i in (list(range(-5, 0)) + list(range(1, 5)))] -def F(model,x): + +def F(model, x): return -(x**2) + def define_model(**kwds): model = ConcreteModel() - model.x1 = Var(bounds=(-5,4)) # domain variable - model.x2 = Var(bounds=(-5,4)) # domain variable - model.x3 = Var(bounds=(-5,4)) # domain variable - model.x4 = Var(bounds=(-5,4)) # domain variable - model.x5 = Var(bounds=(-5,4)) # domain variable - model.x6 = Var(bounds=(-5,4)) # domain variable - model.x7 = Var(bounds=(-5,4)) # domain variable + model.x1 = Var(bounds=(-5, 4)) # domain variable + model.x2 = Var(bounds=(-5, 4)) # domain variable + model.x3 = Var(bounds=(-5, 4)) # domain variable + model.x4 = Var(bounds=(-5, 4)) # domain variable + model.x5 = Var(bounds=(-5, 4)) # domain variable + model.x6 = Var(bounds=(-5, 4)) # domain variable + model.x7 = Var(bounds=(-5, 4)) # domain variable + + model.Fx1 = Var() # range variable + model.Fx2 = Var() # range variable + model.Fx3 = Var() # range variable + model.Fx4 = Var() # range variable + model.Fx5 = Var() # range variable + model.Fx6 = Var() # range variable + model.Fx7 = Var() # range variable - model.Fx1 = Var() # range variable - model.Fx2 = Var() # range variable - model.Fx3 = Var() # range variable - model.Fx4 = Var() # range variable - model.Fx5 = Var() # range variable - model.Fx6 = Var() # range variable - model.Fx7 = Var() # range variable + model.obj = Objective( + expr=model.Fx1 + + model.Fx2 + + model.Fx3 + + model.Fx4 + + model.Fx5 + + model.Fx6 + + model.Fx7, + sense=kwds.pop('sense', maximize), + ) - model.obj = Objective(expr=model.Fx1+model.Fx2+model.Fx3+model.Fx4+model.Fx5+model.Fx6+model.Fx7, sense=kwds.pop('sense',maximize)) + model.piecewise1 = Piecewise( + model.Fx1, model.x1, pw_pts=DOMAIN_PTS, f_rule=F, **kwds + ) + model.piecewise2 = Piecewise( + model.Fx2, model.x2, pw_pts=DOMAIN_PTS, f_rule=F, **kwds + ) + model.piecewise3 = Piecewise( + model.Fx3, model.x3, pw_pts=DOMAIN_PTS, f_rule=F, **kwds + ) + model.piecewise4 = Piecewise( + model.Fx4, model.x4, pw_pts=DOMAIN_PTS, f_rule=F, **kwds + ) + model.piecewise5 = Piecewise( + model.Fx5, model.x5, pw_pts=DOMAIN_PTS, f_rule=F, **kwds + ) + model.piecewise6 = Piecewise( + model.Fx6, model.x6, pw_pts=DOMAIN_PTS, f_rule=F, **kwds + ) + model.piecewise7 = Piecewise( + model.Fx7, model.x7, pw_pts=DOMAIN_PTS, f_rule=F, **kwds + ) - model.piecewise1 = Piecewise(model.Fx1,model.x1, - pw_pts=DOMAIN_PTS, - f_rule=F, **kwds) - model.piecewise2 = Piecewise(model.Fx2,model.x2, - pw_pts=DOMAIN_PTS, - f_rule=F, **kwds) - model.piecewise3 = Piecewise(model.Fx3,model.x3, - pw_pts=DOMAIN_PTS, - f_rule=F, **kwds) - model.piecewise4 = Piecewise(model.Fx4,model.x4, - pw_pts=DOMAIN_PTS, - f_rule=F, **kwds) - model.piecewise5 = Piecewise(model.Fx5,model.x5, - pw_pts=DOMAIN_PTS, - f_rule=F, **kwds) - model.piecewise6 = Piecewise(model.Fx6,model.x6, - pw_pts=DOMAIN_PTS, - f_rule=F, **kwds) - model.piecewise7 = Piecewise(model.Fx7,model.x7, - pw_pts=DOMAIN_PTS, - f_rule=F, **kwds) + # Fix the answer for testing purposes + model.set_answer_constraint1 = Constraint(expr=model.x1 == -5.0) + model.set_answer_constraint2 = Constraint(expr=model.x2 == -3.0) + model.set_answer_constraint3 = Constraint(expr=model.x3 == -2.5) + model.set_answer_constraint4 = Constraint(expr=model.x4 == -1.5) + model.set_answer_constraint5 = Constraint(expr=model.x5 == 2.0) + model.set_answer_constraint6 = Constraint(expr=model.x6 == 3.5) + model.set_answer_constraint7 = Constraint(expr=model.x7 == 4.0) - #Fix the answer for testing purposes - model.set_answer_constraint1 = Constraint(expr= model.x1 == -5.0) - model.set_answer_constraint2 = Constraint(expr= model.x2 == -3.0) - model.set_answer_constraint3 = Constraint(expr= model.x3 == -2.5) - model.set_answer_constraint4 = Constraint(expr= model.x4 == -1.5) - model.set_answer_constraint5 = Constraint(expr= model.x5 == 2.0) - model.set_answer_constraint6 = Constraint(expr= model.x6 == 3.5) - model.set_answer_constraint7 = Constraint(expr= model.x7 == 4.0) - return model diff --git a/pyomo/solvers/tests/piecewise_linear/problems/concave_vararray.py b/pyomo/solvers/tests/piecewise_linear/problems/concave_vararray.py index 5f682410897..3037961b4b5 100644 --- a/pyomo/solvers/tests/piecewise_linear/problems/concave_vararray.py +++ b/pyomo/solvers/tests/piecewise_linear/problems/concave_vararray.py @@ -20,35 +20,51 @@ \ -7x+12, 3 <= x <= 4 """ -from pyomo.core import ConcreteModel, Var, Objective, Param, Piecewise, Constraint, maximize, sum_product +from pyomo.core import ( + ConcreteModel, + Var, + Objective, + Param, + Piecewise, + Constraint, + maximize, + sum_product, +) -INDEX_SET = range(1,8) # There will be two copies of this function -DOMAIN_PTS = dict([(t,[float(i) for i in (list(range(-5,0))+list(range(1,5)))]) for t in INDEX_SET]) +INDEX_SET = range(1, 8) # There will be two copies of this function +DOMAIN_PTS = dict( + [ + (t, [float(i) for i in (list(range(-5, 0)) + list(range(1, 5)))]) + for t in INDEX_SET + ] +) + + +def F(model, t, x): + return -(x**2) * model.p[t] -def F(model,t,x): - return -(x**2)*model.p[t] def define_model(**kwds): model = ConcreteModel() - model.x = Var(INDEX_SET, bounds=(-5,4)) # domain variable - model.Fx = Var(INDEX_SET) # range variable + model.x = Var(INDEX_SET, bounds=(-5, 4)) # domain variable + model.Fx = Var(INDEX_SET) # range variable model.p = Param(INDEX_SET, initialize=1.0) - model.obj = Objective(expr=sum_product(model.Fx), sense=kwds.pop('sense',maximize)) - - model.piecewise = Piecewise(INDEX_SET,model.Fx,model.x, - pw_pts=DOMAIN_PTS, - f_rule=F, **kwds) - - #Fix the answer for testing purposes - model.set_answer_constraint1 = Constraint(expr= model.x[1] == -5.0) - model.set_answer_constraint2 = Constraint(expr= model.x[2] == -3.0) - model.set_answer_constraint3 = Constraint(expr= model.x[3] == -2.5) - model.set_answer_constraint4 = Constraint(expr= model.x[4] == -1.5) - model.set_answer_constraint5 = Constraint(expr= model.x[5] == 2.0) - model.set_answer_constraint6 = Constraint(expr= model.x[6] == 3.5) - model.set_answer_constraint7 = Constraint(expr= model.x[7] == 4.0) - + model.obj = Objective(expr=sum_product(model.Fx), sense=kwds.pop('sense', maximize)) + + model.piecewise = Piecewise( + INDEX_SET, model.Fx, model.x, pw_pts=DOMAIN_PTS, f_rule=F, **kwds + ) + + # Fix the answer for testing purposes + model.set_answer_constraint1 = Constraint(expr=model.x[1] == -5.0) + model.set_answer_constraint2 = Constraint(expr=model.x[2] == -3.0) + model.set_answer_constraint3 = Constraint(expr=model.x[3] == -2.5) + model.set_answer_constraint4 = Constraint(expr=model.x[4] == -1.5) + model.set_answer_constraint5 = Constraint(expr=model.x[5] == 2.0) + model.set_answer_constraint6 = Constraint(expr=model.x[6] == 3.5) + model.set_answer_constraint7 = Constraint(expr=model.x[7] == 4.0) + return model diff --git a/pyomo/solvers/tests/piecewise_linear/problems/convex_multi_vararray1.py b/pyomo/solvers/tests/piecewise_linear/problems/convex_multi_vararray1.py index b4dba4f2b43..5ef7357e896 100644 --- a/pyomo/solvers/tests/piecewise_linear/problems/convex_multi_vararray1.py +++ b/pyomo/solvers/tests/piecewise_linear/problems/convex_multi_vararray1.py @@ -21,44 +21,60 @@ """ -from pyomo.core import ConcreteModel, Var, Objective, Param, Piecewise, Constraint, maximize, sum_product +from pyomo.core import ( + ConcreteModel, + Var, + Objective, + Param, + Piecewise, + Constraint, + maximize, + sum_product, +) -INDEX_SET1 = range(1,8) -INDEX_SET2 = range(0,2) -DOMAIN_PTS = dict([((t1,t2),[float(i) for i in (list(range(-5,0))+list(range(1,5)))]) for t1 in INDEX_SET1 for t2 in INDEX_SET2]) +INDEX_SET1 = range(1, 8) +INDEX_SET2 = range(0, 2) +DOMAIN_PTS = dict( + [ + ((t1, t2), [float(i) for i in (list(range(-5, 0)) + list(range(1, 5)))]) + for t1 in INDEX_SET1 + for t2 in INDEX_SET2 + ] +) + + +def F(model, t1, t2, x): + return (x**2) * model.p[t1, t2] - -def F(model,t1,t2,x): - return (x**2)*model.p[t1,t2] def define_model(**kwds): model = ConcreteModel() - model.x = Var(INDEX_SET1, INDEX_SET2, bounds=(-5,4)) # domain variable - model.Fx = Var(INDEX_SET1, INDEX_SET2) # range variable + model.x = Var(INDEX_SET1, INDEX_SET2, bounds=(-5, 4)) # domain variable + model.Fx = Var(INDEX_SET1, INDEX_SET2) # range variable model.p = Param(INDEX_SET1, INDEX_SET2, initialize=1.0) - model.obj = Objective(expr=sum_product(model.Fx), sense=kwds.pop('sense',maximize)) + model.obj = Objective(expr=sum_product(model.Fx), sense=kwds.pop('sense', maximize)) + + model.piecewise = Piecewise( + INDEX_SET1, INDEX_SET2, model.Fx, model.x, pw_pts=DOMAIN_PTS, f_rule=F, **kwds + ) - model.piecewise = Piecewise(INDEX_SET1,INDEX_SET2,model.Fx,model.x, - pw_pts=DOMAIN_PTS, - f_rule=F, **kwds) + # Fix the answer for testing purposes + model.set_answer_constraint1 = Constraint(expr=model.x[1, 0] == -5.0) + model.set_answer_constraint2 = Constraint(expr=model.x[2, 0] == -3.0) + model.set_answer_constraint3 = Constraint(expr=model.x[3, 0] == -2.5) + model.set_answer_constraint4 = Constraint(expr=model.x[4, 0] == -1.5) + model.set_answer_constraint5 = Constraint(expr=model.x[5, 0] == 2.0) + model.set_answer_constraint6 = Constraint(expr=model.x[6, 0] == 3.5) + model.set_answer_constraint7 = Constraint(expr=model.x[7, 0] == 4.0) + model.set_answer_constraint8 = Constraint(expr=model.x[1, 1] == -5.0) + model.set_answer_constraint9 = Constraint(expr=model.x[2, 1] == -3.0) + model.set_answer_constraint10 = Constraint(expr=model.x[3, 1] == -2.5) + model.set_answer_constraint11 = Constraint(expr=model.x[4, 1] == -1.5) + model.set_answer_constraint12 = Constraint(expr=model.x[5, 1] == 2.0) + model.set_answer_constraint13 = Constraint(expr=model.x[6, 1] == 3.5) + model.set_answer_constraint14 = Constraint(expr=model.x[7, 1] == 4.0) - #Fix the answer for testing purposes - model.set_answer_constraint1 = Constraint(expr= model.x[1,0] == -5.0) - model.set_answer_constraint2 = Constraint(expr= model.x[2,0] == -3.0) - model.set_answer_constraint3 = Constraint(expr= model.x[3,0] == -2.5) - model.set_answer_constraint4 = Constraint(expr= model.x[4,0] == -1.5) - model.set_answer_constraint5 = Constraint(expr= model.x[5,0] == 2.0) - model.set_answer_constraint6 = Constraint(expr= model.x[6,0] == 3.5) - model.set_answer_constraint7 = Constraint(expr= model.x[7,0] == 4.0) - model.set_answer_constraint8 = Constraint(expr= model.x[1,1] == -5.0) - model.set_answer_constraint9 = Constraint(expr= model.x[2,1] == -3.0) - model.set_answer_constraint10 = Constraint(expr= model.x[3,1] == -2.5) - model.set_answer_constraint11 = Constraint(expr= model.x[4,1] == -1.5) - model.set_answer_constraint12 = Constraint(expr= model.x[5,1] == 2.0) - model.set_answer_constraint13 = Constraint(expr= model.x[6,1] == 3.5) - model.set_answer_constraint14 = Constraint(expr= model.x[7,1] == 4.0) - return model diff --git a/pyomo/solvers/tests/piecewise_linear/problems/convex_multi_vararray2.py b/pyomo/solvers/tests/piecewise_linear/problems/convex_multi_vararray2.py index 6c031f3078e..c379d76dcc1 100644 --- a/pyomo/solvers/tests/piecewise_linear/problems/convex_multi_vararray2.py +++ b/pyomo/solvers/tests/piecewise_linear/problems/convex_multi_vararray2.py @@ -21,42 +21,58 @@ """ -from pyomo.core import ConcreteModel, Var, Objective, Param, Piecewise, Constraint, maximize, sum_product +from pyomo.core import ( + ConcreteModel, + Var, + Objective, + Param, + Piecewise, + Constraint, + maximize, + sum_product, +) + +INDEX_SET = [(t1, t2) for t1 in range(1, 8) for t2 in range(0, 2)] +DOMAIN_PTS = dict( + [ + (t, [float(i) for i in (list(range(-5, 0)) + list(range(1, 5)))]) + for t in INDEX_SET + ] +) + + +def F(model, t1, t2, x): + return (x**2) * model.p[t1, t2] -INDEX_SET = [(t1,t2) for t1 in range(1,8) for t2 in range(0,2)] -DOMAIN_PTS = dict([(t,[float(i) for i in (list(range(-5,0))+list(range(1,5)))]) for t in INDEX_SET]) - -def F(model,t1,t2,x): - return (x**2)*model.p[t1,t2] def define_model(**kwds): model = ConcreteModel() - model.x = Var(INDEX_SET, bounds=(-5,4)) # domain variable - model.Fx = Var(INDEX_SET) # range variable + model.x = Var(INDEX_SET, bounds=(-5, 4)) # domain variable + model.Fx = Var(INDEX_SET) # range variable model.p = Param(INDEX_SET, initialize=1.0) - model.obj = Objective(expr=sum_product(model.Fx), sense=kwds.pop('sense',maximize)) - - model.piecewise = Piecewise(INDEX_SET,model.Fx,model.x, - pw_pts=DOMAIN_PTS, - f_rule=F, **kwds) - - #Fix the answer for testing purposes - model.set_answer_constraint1 = Constraint(expr= model.x[1,0] == -5.0) - model.set_answer_constraint2 = Constraint(expr= model.x[2,0] == -3.0) - model.set_answer_constraint3 = Constraint(expr= model.x[3,0] == -2.5) - model.set_answer_constraint4 = Constraint(expr= model.x[4,0] == -1.5) - model.set_answer_constraint5 = Constraint(expr= model.x[5,0] == 2.0) - model.set_answer_constraint6 = Constraint(expr= model.x[6,0] == 3.5) - model.set_answer_constraint7 = Constraint(expr= model.x[7,0] == 4.0) - model.set_answer_constraint8 = Constraint(expr= model.x[1,1] == -5.0) - model.set_answer_constraint9 = Constraint(expr= model.x[2,1] == -3.0) - model.set_answer_constraint10 = Constraint(expr= model.x[3,1] == -2.5) - model.set_answer_constraint11 = Constraint(expr= model.x[4,1] == -1.5) - model.set_answer_constraint12 = Constraint(expr= model.x[5,1] == 2.0) - model.set_answer_constraint13 = Constraint(expr= model.x[6,1] == 3.5) - model.set_answer_constraint14 = Constraint(expr= model.x[7,1] == 4.0) - + model.obj = Objective(expr=sum_product(model.Fx), sense=kwds.pop('sense', maximize)) + + model.piecewise = Piecewise( + INDEX_SET, model.Fx, model.x, pw_pts=DOMAIN_PTS, f_rule=F, **kwds + ) + + # Fix the answer for testing purposes + model.set_answer_constraint1 = Constraint(expr=model.x[1, 0] == -5.0) + model.set_answer_constraint2 = Constraint(expr=model.x[2, 0] == -3.0) + model.set_answer_constraint3 = Constraint(expr=model.x[3, 0] == -2.5) + model.set_answer_constraint4 = Constraint(expr=model.x[4, 0] == -1.5) + model.set_answer_constraint5 = Constraint(expr=model.x[5, 0] == 2.0) + model.set_answer_constraint6 = Constraint(expr=model.x[6, 0] == 3.5) + model.set_answer_constraint7 = Constraint(expr=model.x[7, 0] == 4.0) + model.set_answer_constraint8 = Constraint(expr=model.x[1, 1] == -5.0) + model.set_answer_constraint9 = Constraint(expr=model.x[2, 1] == -3.0) + model.set_answer_constraint10 = Constraint(expr=model.x[3, 1] == -2.5) + model.set_answer_constraint11 = Constraint(expr=model.x[4, 1] == -1.5) + model.set_answer_constraint12 = Constraint(expr=model.x[5, 1] == 2.0) + model.set_answer_constraint13 = Constraint(expr=model.x[6, 1] == 3.5) + model.set_answer_constraint14 = Constraint(expr=model.x[7, 1] == 4.0) + return model diff --git a/pyomo/solvers/tests/piecewise_linear/problems/convex_var.py b/pyomo/solvers/tests/piecewise_linear/problems/convex_var.py index 13ac8040446..6b3ddb8dce9 100644 --- a/pyomo/solvers/tests/piecewise_linear/problems/convex_var.py +++ b/pyomo/solvers/tests/piecewise_linear/problems/convex_var.py @@ -21,64 +21,75 @@ """ -from pyomo.core import ConcreteModel, Var, Objective, Piecewise, Constraint, maximize +from pyomo.core import ConcreteModel, Var, Objective, Piecewise, Constraint, maximize -DOMAIN_PTS = [float(i) for i in (list(range(-5,0))+list(range(1,5)))] +DOMAIN_PTS = [float(i) for i in (list(range(-5, 0)) + list(range(1, 5)))] -def F(model,x): + +def F(model, x): return x**2 + def define_model(**kwds): model = ConcreteModel() - model.x1 = Var(bounds=(-5,4)) # domain variable - model.x2 = Var(bounds=(-5,4)) # domain variable - model.x3 = Var(bounds=(-5,4)) # domain variable - model.x4 = Var(bounds=(-5,4)) # domain variable - model.x5 = Var(bounds=(-5,4)) # domain variable - model.x6 = Var(bounds=(-5,4)) # domain variable - model.x7 = Var(bounds=(-5,4)) # domain variable + model.x1 = Var(bounds=(-5, 4)) # domain variable + model.x2 = Var(bounds=(-5, 4)) # domain variable + model.x3 = Var(bounds=(-5, 4)) # domain variable + model.x4 = Var(bounds=(-5, 4)) # domain variable + model.x5 = Var(bounds=(-5, 4)) # domain variable + model.x6 = Var(bounds=(-5, 4)) # domain variable + model.x7 = Var(bounds=(-5, 4)) # domain variable + + model.Fx1 = Var() # range variable + model.Fx2 = Var() # range variable + model.Fx3 = Var() # range variable + model.Fx4 = Var() # range variable + model.Fx5 = Var() # range variable + model.Fx6 = Var() # range variable + model.Fx7 = Var() # range variable - model.Fx1 = Var() # range variable - model.Fx2 = Var() # range variable - model.Fx3 = Var() # range variable - model.Fx4 = Var() # range variable - model.Fx5 = Var() # range variable - model.Fx6 = Var() # range variable - model.Fx7 = Var() # range variable + model.obj = Objective( + expr=model.Fx1 + + model.Fx2 + + model.Fx3 + + model.Fx4 + + model.Fx5 + + model.Fx6 + + model.Fx7, + sense=kwds.pop('sense', maximize), + ) - model.obj = Objective(expr=model.Fx1+model.Fx2+model.Fx3+model.Fx4+model.Fx5+model.Fx6+model.Fx7, sense=kwds.pop('sense',maximize)) + model.piecewise1 = Piecewise( + model.Fx1, model.x1, pw_pts=DOMAIN_PTS, f_rule=F, **kwds + ) + model.piecewise2 = Piecewise( + model.Fx2, model.x2, pw_pts=DOMAIN_PTS, f_rule=F, **kwds + ) + model.piecewise3 = Piecewise( + model.Fx3, model.x3, pw_pts=DOMAIN_PTS, f_rule=F, **kwds + ) + model.piecewise4 = Piecewise( + model.Fx4, model.x4, pw_pts=DOMAIN_PTS, f_rule=F, **kwds + ) + model.piecewise5 = Piecewise( + model.Fx5, model.x5, pw_pts=DOMAIN_PTS, f_rule=F, **kwds + ) + model.piecewise6 = Piecewise( + model.Fx6, model.x6, pw_pts=DOMAIN_PTS, f_rule=F, **kwds + ) + model.piecewise7 = Piecewise( + model.Fx7, model.x7, pw_pts=DOMAIN_PTS, f_rule=F, **kwds + ) - model.piecewise1 = Piecewise(model.Fx1,model.x1, - pw_pts=DOMAIN_PTS, - f_rule=F, **kwds) - model.piecewise2 = Piecewise(model.Fx2,model.x2, - pw_pts=DOMAIN_PTS, - f_rule=F, **kwds) - model.piecewise3 = Piecewise(model.Fx3,model.x3, - pw_pts=DOMAIN_PTS, - f_rule=F, **kwds) - model.piecewise4 = Piecewise(model.Fx4,model.x4, - pw_pts=DOMAIN_PTS, - f_rule=F, **kwds) - model.piecewise5 = Piecewise(model.Fx5,model.x5, - pw_pts=DOMAIN_PTS, - f_rule=F, **kwds) - model.piecewise6 = Piecewise(model.Fx6,model.x6, - pw_pts=DOMAIN_PTS, - f_rule=F, **kwds) - model.piecewise7 = Piecewise(model.Fx7,model.x7, - pw_pts=DOMAIN_PTS, - f_rule=F, **kwds) + # Fix the answer for testing purposes + model.set_answer_constraint1 = Constraint(expr=model.x1 == -5.0) + model.set_answer_constraint2 = Constraint(expr=model.x2 == -3.0) + model.set_answer_constraint3 = Constraint(expr=model.x3 == -2.5) + model.set_answer_constraint4 = Constraint(expr=model.x4 == -1.5) + model.set_answer_constraint5 = Constraint(expr=model.x5 == 2.0) + model.set_answer_constraint6 = Constraint(expr=model.x6 == 3.5) + model.set_answer_constraint7 = Constraint(expr=model.x7 == 4.0) - #Fix the answer for testing purposes - model.set_answer_constraint1 = Constraint(expr= model.x1 == -5.0) - model.set_answer_constraint2 = Constraint(expr= model.x2 == -3.0) - model.set_answer_constraint3 = Constraint(expr= model.x3 == -2.5) - model.set_answer_constraint4 = Constraint(expr= model.x4 == -1.5) - model.set_answer_constraint5 = Constraint(expr= model.x5 == 2.0) - model.set_answer_constraint6 = Constraint(expr= model.x6 == 3.5) - model.set_answer_constraint7 = Constraint(expr= model.x7 == 4.0) - return model diff --git a/pyomo/solvers/tests/piecewise_linear/problems/convex_vararray.py b/pyomo/solvers/tests/piecewise_linear/problems/convex_vararray.py index 132d73ded5c..9bc824660e9 100644 --- a/pyomo/solvers/tests/piecewise_linear/problems/convex_vararray.py +++ b/pyomo/solvers/tests/piecewise_linear/problems/convex_vararray.py @@ -21,36 +21,51 @@ """ -from pyomo.environ import ConcreteModel, Var, Param, Piecewise, Constraint, Objective, sum_product, maximize +from pyomo.environ import ( + ConcreteModel, + Var, + Param, + Piecewise, + Constraint, + Objective, + sum_product, + maximize, +) -INDEX_SET = range(1,8) # There will be two copies of this function -DOMAIN_PTS = dict([(t,[float(i) for i in (list(range(-5,0))+list(range(1,5)))]) for t in INDEX_SET]) +INDEX_SET = range(1, 8) # There will be two copies of this function +DOMAIN_PTS = dict( + [ + (t, [float(i) for i in (list(range(-5, 0)) + list(range(1, 5)))]) + for t in INDEX_SET + ] +) + + +def F(model, t, x): + return (x**2) * model.p[t] - -def F(model,t,x): - return (x**2)*model.p[t] def define_model(**kwds): model = ConcreteModel() - model.x = Var(INDEX_SET, bounds=(-5,4)) # domain variable - model.Fx = Var(INDEX_SET) # range variable - model.p = Param(INDEX_SET,initialize=1.0) + model.x = Var(INDEX_SET, bounds=(-5, 4)) # domain variable + model.Fx = Var(INDEX_SET) # range variable + model.p = Param(INDEX_SET, initialize=1.0) - model.obj = Objective(expr=sum_product(model.Fx), sense=kwds.pop('sense',maximize)) + model.obj = Objective(expr=sum_product(model.Fx), sense=kwds.pop('sense', maximize)) - model.piecewise = Piecewise(INDEX_SET,model.Fx,model.x, - pw_pts=DOMAIN_PTS, - f_rule=F, **kwds) + model.piecewise = Piecewise( + INDEX_SET, model.Fx, model.x, pw_pts=DOMAIN_PTS, f_rule=F, **kwds + ) - #Fix the answer for testing purposes - model.set_answer_constraint1 = Constraint(expr= model.x[1] == -5.0) - model.set_answer_constraint2 = Constraint(expr= model.x[2] == -3.0) - model.set_answer_constraint3 = Constraint(expr= model.x[3] == -2.5) - model.set_answer_constraint4 = Constraint(expr= model.x[4] == -1.5) - model.set_answer_constraint5 = Constraint(expr= model.x[5] == 2.0) - model.set_answer_constraint6 = Constraint(expr= model.x[6] == 3.5) - model.set_answer_constraint7 = Constraint(expr= model.x[7] == 4.0) + # Fix the answer for testing purposes + model.set_answer_constraint1 = Constraint(expr=model.x[1] == -5.0) + model.set_answer_constraint2 = Constraint(expr=model.x[2] == -3.0) + model.set_answer_constraint3 = Constraint(expr=model.x[3] == -2.5) + model.set_answer_constraint4 = Constraint(expr=model.x[4] == -1.5) + model.set_answer_constraint5 = Constraint(expr=model.x[5] == 2.0) + model.set_answer_constraint6 = Constraint(expr=model.x[6] == 3.5) + model.set_answer_constraint7 = Constraint(expr=model.x[7] == 4.0) return model diff --git a/pyomo/solvers/tests/piecewise_linear/problems/piecewise_multi_vararray.py b/pyomo/solvers/tests/piecewise_linear/problems/piecewise_multi_vararray.py index ed6edc59d80..4ffa4d9a68e 100644 --- a/pyomo/solvers/tests/piecewise_linear/problems/piecewise_multi_vararray.py +++ b/pyomo/solvers/tests/piecewise_linear/problems/piecewise_multi_vararray.py @@ -16,34 +16,59 @@ \ 2x-13 , 5 <= x <= 6 """ -from pyomo.core import ConcreteModel, Var, Objective, Param, Piecewise, Constraint, maximize, sum_product +from pyomo.core import ( + ConcreteModel, + Var, + Objective, + Param, + Piecewise, + Constraint, + maximize, + sum_product, +) -INDEX_SET1 = ['1','2','3','40'] # There will be two copies of this function -INDEX_SET2 = [(t1,t2) for t1 in range(1,4) for t2 in range(1,5)] -DOMAIN_PTS = dict([((t1,t2,t3),[float(i) for i in [0,1,3,5,6]]) for t1 in INDEX_SET1 for (t2,t3) in INDEX_SET2]) -RANGE_PTS = {0.0:0.0, 1.0:2.0, 3.0:3.0, 5.0:-3.0, 6.0:-1.0} +INDEX_SET1 = ['1', '2', '3', '40'] # There will be two copies of this function +INDEX_SET2 = [(t1, t2) for t1 in range(1, 4) for t2 in range(1, 5)] +DOMAIN_PTS = dict( + [ + ((t1, t2, t3), [float(i) for i in [0, 1, 3, 5, 6]]) + for t1 in INDEX_SET1 + for (t2, t3) in INDEX_SET2 + ] +) +RANGE_PTS = {0.0: 0.0, 1.0: 2.0, 3.0: 3.0, 5.0: -3.0, 6.0: -1.0} + + +def F(model, t1, t2, t3, x): + return RANGE_PTS[x] * model.p[t1, t2, t3] -def F(model,t1,t2,t3,x): - return RANGE_PTS[x]*model.p[t1,t2,t3] def define_model(**kwds): model = ConcreteModel() - model.x = Var(INDEX_SET1, INDEX_SET2, bounds=(0,6)) # domain variable - model.Fx = Var(INDEX_SET1, INDEX_SET2) # range variable + model.x = Var(INDEX_SET1, INDEX_SET2, bounds=(0, 6)) # domain variable + model.Fx = Var(INDEX_SET1, INDEX_SET2) # range variable model.p = Param(INDEX_SET1, INDEX_SET2, initialize=1.0) - model.obj = Objective(expr=sum_product(model.Fx), sense=kwds.pop('sense',maximize)) + model.obj = Objective(expr=sum_product(model.Fx), sense=kwds.pop('sense', maximize)) - model.piecewise = Piecewise(INDEX_SET1,INDEX_SET2,model.Fx,model.x, - pw_pts=DOMAIN_PTS, - f_rule=F, **kwds) + model.piecewise = Piecewise( + INDEX_SET1, INDEX_SET2, model.Fx, model.x, pw_pts=DOMAIN_PTS, f_rule=F, **kwds + ) - #Fix the answer for testing purposes - model.set_answer_constraint1 = Constraint(INDEX_SET2,rule= lambda model,t2,t3: model.x['1',t2,t3] == 0.0) - model.set_answer_constraint2 = Constraint(INDEX_SET2,rule= lambda model,t2,t3: model.x['2',t2,t3] == 3.0) - model.set_answer_constraint3 = Constraint(INDEX_SET2,rule= lambda model,t2,t3: model.x['3',t2,t3] == 5.5) - model.set_answer_constraint4 = Constraint(INDEX_SET2,rule= lambda model,t2,t3: model.x['40',t2,t3] == 6.0) + # Fix the answer for testing purposes + model.set_answer_constraint1 = Constraint( + INDEX_SET2, rule=lambda model, t2, t3: model.x['1', t2, t3] == 0.0 + ) + model.set_answer_constraint2 = Constraint( + INDEX_SET2, rule=lambda model, t2, t3: model.x['2', t2, t3] == 3.0 + ) + model.set_answer_constraint3 = Constraint( + INDEX_SET2, rule=lambda model, t2, t3: model.x['3', t2, t3] == 5.5 + ) + model.set_answer_constraint4 = Constraint( + INDEX_SET2, rule=lambda model, t2, t3: model.x['40', t2, t3] == 6.0 + ) return model diff --git a/pyomo/solvers/tests/piecewise_linear/problems/piecewise_var.py b/pyomo/solvers/tests/piecewise_linear/problems/piecewise_var.py index 52be784a164..78b2f483109 100644 --- a/pyomo/solvers/tests/piecewise_linear/problems/piecewise_var.py +++ b/pyomo/solvers/tests/piecewise_linear/problems/piecewise_var.py @@ -16,50 +16,63 @@ \ 2x-13 , 5 <= x <= 6 """ -from pyomo.core import ConcreteModel, Var, Objective, Param, Piecewise, Constraint, maximize +from pyomo.core import ( + ConcreteModel, + Var, + Objective, + Param, + Piecewise, + Constraint, + maximize, +) -DOMAIN_PTS = [float(i) for i in [0,1,3,5,6]] -RANGE_PTS = {0.0:0.0, 1.0:2.0, 3.0:3.0, 5.0:-3.0, 6.0:-1.0} +DOMAIN_PTS = [float(i) for i in [0, 1, 3, 5, 6]] +RANGE_PTS = {0.0: 0.0, 1.0: 2.0, 3.0: 3.0, 5.0: -3.0, 6.0: -1.0} + + +def F(model, x): + return RANGE_PTS[x] * model.p -def F(model,x): - return RANGE_PTS[x]*model.p def define_model(**kwds): model = ConcreteModel() - model.x1 = Var(bounds=(0,6)) # domain variable - model.x2 = Var(bounds=(0,6)) # domain variable - model.x3 = Var(bounds=(0,6)) # domain variable - model.x4 = Var(bounds=(0,6)) # domain variable - - model.Fx1 = Var() # range variable - model.Fx2 = Var() # range variable - model.Fx3 = Var() # range variable - model.Fx4 = Var() # range variable + model.x1 = Var(bounds=(0, 6)) # domain variable + model.x2 = Var(bounds=(0, 6)) # domain variable + model.x3 = Var(bounds=(0, 6)) # domain variable + model.x4 = Var(bounds=(0, 6)) # domain variable + + model.Fx1 = Var() # range variable + model.Fx2 = Var() # range variable + model.Fx3 = Var() # range variable + model.Fx4 = Var() # range variable model.p = Param(initialize=1.0) - model.obj = Objective(expr=model.Fx1+model.Fx2+model.Fx3+model.Fx4, sense=kwds.pop('sense',maximize)) - - model.piecewise1 = Piecewise(model.Fx1,model.x1, - pw_pts=DOMAIN_PTS, - f_rule=F, **kwds) - model.piecewise2 = Piecewise(model.Fx2,model.x2, - pw_pts=DOMAIN_PTS, - f_rule=F, **kwds) - - model.piecewise3 = Piecewise(model.Fx3,model.x3, - pw_pts=DOMAIN_PTS, - f_rule=F, **kwds) - - model.piecewise4 = Piecewise(model.Fx4,model.x4, - pw_pts=DOMAIN_PTS, - f_rule=F, **kwds) - - #Fix the answer for testing purposes - model.set_answer_constraint1 = Constraint(expr= model.x1 == 0.0) - model.set_answer_constraint2 = Constraint(expr= model.x2 == 3.0) - model.set_answer_constraint3 = Constraint(expr= model.x3 == 5.5) - model.set_answer_constraint4 = Constraint(expr= model.x4 == 6.0) - + model.obj = Objective( + expr=model.Fx1 + model.Fx2 + model.Fx3 + model.Fx4, + sense=kwds.pop('sense', maximize), + ) + + model.piecewise1 = Piecewise( + model.Fx1, model.x1, pw_pts=DOMAIN_PTS, f_rule=F, **kwds + ) + model.piecewise2 = Piecewise( + model.Fx2, model.x2, pw_pts=DOMAIN_PTS, f_rule=F, **kwds + ) + + model.piecewise3 = Piecewise( + model.Fx3, model.x3, pw_pts=DOMAIN_PTS, f_rule=F, **kwds + ) + + model.piecewise4 = Piecewise( + model.Fx4, model.x4, pw_pts=DOMAIN_PTS, f_rule=F, **kwds + ) + + # Fix the answer for testing purposes + model.set_answer_constraint1 = Constraint(expr=model.x1 == 0.0) + model.set_answer_constraint2 = Constraint(expr=model.x2 == 3.0) + model.set_answer_constraint3 = Constraint(expr=model.x3 == 5.5) + model.set_answer_constraint4 = Constraint(expr=model.x4 == 6.0) + return model diff --git a/pyomo/solvers/tests/piecewise_linear/problems/piecewise_vararray.py b/pyomo/solvers/tests/piecewise_linear/problems/piecewise_vararray.py index 0e87fbb9082..267f741a633 100644 --- a/pyomo/solvers/tests/piecewise_linear/problems/piecewise_vararray.py +++ b/pyomo/solvers/tests/piecewise_linear/problems/piecewise_vararray.py @@ -16,33 +16,44 @@ \ 2x-13 , 5 <= x <= 6 """ -from pyomo.core import ConcreteModel, Var, Objective, Param, Piecewise, Constraint, maximize, sum_product +from pyomo.core import ( + ConcreteModel, + Var, + Objective, + Param, + Piecewise, + Constraint, + maximize, + sum_product, +) -INDEX_SET = [1,2,3,4] # There will be two copies of this function -DOMAIN_PTS = dict([(t,[float(i) for i in [0,1,3,5,6]]) for t in INDEX_SET]) -RANGE_PTS = {0.0:0.0, 1.0:2.0, 3.0:3.0, 5.0:-3.0, 6.0:-1.0} +INDEX_SET = [1, 2, 3, 4] # There will be two copies of this function +DOMAIN_PTS = dict([(t, [float(i) for i in [0, 1, 3, 5, 6]]) for t in INDEX_SET]) +RANGE_PTS = {0.0: 0.0, 1.0: 2.0, 3.0: 3.0, 5.0: -3.0, 6.0: -1.0} + + +def F(model, t, x): + return RANGE_PTS[x] * model.p[t] -def F(model,t,x): - return RANGE_PTS[x]*model.p[t] def define_model(**kwds): model = ConcreteModel() - model.x = Var(INDEX_SET, bounds=(0,6)) # domain variable - model.Fx = Var(INDEX_SET) # range variable + model.x = Var(INDEX_SET, bounds=(0, 6)) # domain variable + model.Fx = Var(INDEX_SET) # range variable model.p = Param(INDEX_SET, initialize=1.0, mutable=True) - model.obj = Objective(expr=sum_product(model.Fx), sense=kwds.pop('sense',maximize)) + model.obj = Objective(expr=sum_product(model.Fx), sense=kwds.pop('sense', maximize)) + + model.piecewise = Piecewise( + INDEX_SET, model.Fx, model.x, pw_pts=DOMAIN_PTS, f_rule=F, **kwds + ) - model.piecewise = Piecewise(INDEX_SET,model.Fx,model.x, - pw_pts=DOMAIN_PTS, - f_rule=F, **kwds) + # Fix the answer for testing purposes + model.set_answer_constraint1 = Constraint(expr=model.x[1] == 0.0) + model.set_answer_constraint2 = Constraint(expr=model.x[2] == 3.0) + model.set_answer_constraint3 = Constraint(expr=model.x[3] == 5.5) + model.set_answer_constraint4 = Constraint(expr=model.x[4] == 6.0) - #Fix the answer for testing purposes - model.set_answer_constraint1 = Constraint(expr= model.x[1] == 0.0) - model.set_answer_constraint2 = Constraint(expr= model.x[2] == 3.0) - model.set_answer_constraint3 = Constraint(expr= model.x[3] == 5.5) - model.set_answer_constraint4 = Constraint(expr= model.x[4] == 6.0) - return model diff --git a/pyomo/solvers/tests/piecewise_linear/problems/step_var.py b/pyomo/solvers/tests/piecewise_linear/problems/step_var.py index 5f6be710b08..6ad1c56640a 100644 --- a/pyomo/solvers/tests/piecewise_linear/problems/step_var.py +++ b/pyomo/solvers/tests/piecewise_linear/problems/step_var.py @@ -17,48 +17,73 @@ \ x-1 , 2 < x <= 3 """ -from pyomo.core import ConcreteModel, Var, Objective, Param, Piecewise, Constraint, maximize +from pyomo.core import ( + ConcreteModel, + Var, + Objective, + Param, + Piecewise, + Constraint, + maximize, +) DOMAIN_PTS = [0, 1, 1, 2, 3] F = [0, 0, 1, 1, 2] + def define_model(**kwds): model = ConcreteModel() - model.x1 = Var(bounds=(0,3)) # domain variable - model.x2 = Var(bounds=(0,3)) # domain variable - model.x3 = Var(bounds=(0,3)) # domain variable - model.x4 = Var(bounds=(0,3)) # domain variable - - model.Fx1 = Var() # range variable - model.Fx2 = Var() # range variable - model.Fx3 = Var() # range variable - model.Fx4 = Var() # range variable + model.x1 = Var(bounds=(0, 3)) # domain variable + model.x2 = Var(bounds=(0, 3)) # domain variable + model.x3 = Var(bounds=(0, 3)) # domain variable + model.x4 = Var(bounds=(0, 3)) # domain variable + + model.Fx1 = Var() # range variable + model.Fx2 = Var() # range variable + model.Fx3 = Var() # range variable + model.Fx4 = Var() # range variable model.p = Param(initialize=1.0) - model.obj = Objective(expr=model.Fx1+model.Fx2+model.Fx3+model.Fx4+model.x1+model.x2+model.x3+model.x4, sense=kwds.pop('sense',maximize)) + model.obj = Objective( + expr=model.Fx1 + + model.Fx2 + + model.Fx3 + + model.Fx4 + + model.x1 + + model.x2 + + model.x3 + + model.x4, + sense=kwds.pop('sense', maximize), + ) + + model.piecewise1 = Piecewise( + model.Fx1, model.x1, pw_pts=DOMAIN_PTS, f_rule=F, **kwds + ) + model.piecewise2 = Piecewise( + model.Fx2, model.x2, pw_pts=DOMAIN_PTS, f_rule=F, **kwds + ) - model.piecewise1 = Piecewise(model.Fx1,model.x1, - pw_pts=DOMAIN_PTS, - f_rule=F, **kwds) - model.piecewise2 = Piecewise(model.Fx2,model.x2, - pw_pts=DOMAIN_PTS, - f_rule=F, **kwds) + model.piecewise3 = Piecewise( + model.Fx3, model.x3, pw_pts=DOMAIN_PTS, f_rule=F, **kwds + ) - model.piecewise3 = Piecewise(model.Fx3,model.x3, - pw_pts=DOMAIN_PTS, - f_rule=F, **kwds) + model.piecewise4 = Piecewise( + model.Fx4, model.x4, pw_pts=DOMAIN_PTS, f_rule=F, **kwds + ) - model.piecewise4 = Piecewise(model.Fx4,model.x4, - pw_pts=DOMAIN_PTS, - f_rule=F, **kwds) + # Fix the answer for testing purposes + model.set_answer_constraint1 = Constraint( + expr=model.x1 == 0.5 + ) # Fx1 should solve to 0 + model.set_answer_constraint2 = Constraint(expr=model.x2 == 1.0) # + model.set_answer_constraint3 = Constraint(expr=model.Fx2 == 0.5) # + model.set_answer_constraint4 = Constraint( + expr=model.x3 == 1.5 + ) # Fx3 should solve to 1 + model.set_answer_constraint5 = Constraint( + expr=model.x4 == 2.5 + ) # Fx4 should solve to 1.5 - #Fix the answer for testing purposes - model.set_answer_constraint1 = Constraint(expr= model.x1 == 0.5) # Fx1 should solve to 0 - model.set_answer_constraint2 = Constraint(expr= model.x2 == 1.0) # - model.set_answer_constraint3 = Constraint(expr= model.Fx2 == 0.5) # - model.set_answer_constraint4 = Constraint(expr= model.x3 == 1.5) # Fx3 should solve to 1 - model.set_answer_constraint5 = Constraint(expr= model.x4 == 2.5) # Fx4 should solve to 1.5 - return model diff --git a/pyomo/solvers/tests/piecewise_linear/problems/step_vararray.py b/pyomo/solvers/tests/piecewise_linear/problems/step_vararray.py index 418f20293a1..0ac09fb14f8 100644 --- a/pyomo/solvers/tests/piecewise_linear/problems/step_vararray.py +++ b/pyomo/solvers/tests/piecewise_linear/problems/step_vararray.py @@ -17,33 +17,55 @@ \ x-1 , 2 < x <= 3 """ -from pyomo.core import ConcreteModel, Var, Objective, Piecewise, Constraint, maximize, sum_product +from pyomo.core import ( + ConcreteModel, + Var, + Objective, + Piecewise, + Constraint, + maximize, + sum_product, +) -INDEX = [1,2,3,4] +INDEX = [1, 2, 3, 4] DOMAIN_PTS = [0, 1, 1, 2, 3] F = [0, 0, 1, 1, 2] + def define_model(**kwds): model = ConcreteModel() - model.x = Var(INDEX) # domain variable - - model.Fx = Var(INDEX) # range variable - - model.obj = Objective(expr=sum_product(model.Fx)+sum_product(model.x), sense=kwds.pop('sense',maximize)) - - model.piecewise = Piecewise(INDEX,model.Fx,model.x, - pw_pts=DOMAIN_PTS, - f_rule=F, - unbounded_domain_var=True, - **kwds) - - #Fix the answer for testing purposes - model.set_answer_constraint1 = Constraint(expr= model.x[1] == 0.5) # Fx1 should solve to 0 - model.set_answer_constraint2 = Constraint(expr= model.x[2] == 1.0) # - model.set_answer_constraint3 = Constraint(expr= model.Fx[2] == 0.5) # - model.set_answer_constraint4 = Constraint(expr= model.x[3] == 1.5) # Fx3 should solve to 1 - model.set_answer_constraint5 = Constraint(expr= model.x[4] == 2.5) # Fx4 should solve to 1.5 - + model.x = Var(INDEX) # domain variable + + model.Fx = Var(INDEX) # range variable + + model.obj = Objective( + expr=sum_product(model.Fx) + sum_product(model.x), + sense=kwds.pop('sense', maximize), + ) + + model.piecewise = Piecewise( + INDEX, + model.Fx, + model.x, + pw_pts=DOMAIN_PTS, + f_rule=F, + unbounded_domain_var=True, + **kwds + ) + + # Fix the answer for testing purposes + model.set_answer_constraint1 = Constraint( + expr=model.x[1] == 0.5 + ) # Fx1 should solve to 0 + model.set_answer_constraint2 = Constraint(expr=model.x[2] == 1.0) # + model.set_answer_constraint3 = Constraint(expr=model.Fx[2] == 0.5) # + model.set_answer_constraint4 = Constraint( + expr=model.x[3] == 1.5 + ) # Fx3 should solve to 1 + model.set_answer_constraint5 = Constraint( + expr=model.x[4] == 2.5 + ) # Fx4 should solve to 1.5 + return model diff --git a/pyomo/solvers/tests/piecewise_linear/problems/tester.py b/pyomo/solvers/tests/piecewise_linear/problems/tester.py index 4563fde2630..5911cc48db0 100644 --- a/pyomo/solvers/tests/piecewise_linear/problems/tester.py +++ b/pyomo/solvers/tests/piecewise_linear/problems/tester.py @@ -12,9 +12,9 @@ from pyomo.environ import Var, maximize, value from pyomo.opt import SolverFactory -opt = SolverFactory('cplexamp',solve_io='nl') +opt = SolverFactory('cplexamp', solve_io='nl') -kwds = {'pw_constr_type':'UB','pw_repn':'DCC','sense':maximize,'force_pw':True} +kwds = {'pw_constr_type': 'UB', 'pw_repn': 'DCC', 'sense': maximize, 'force_pw': True} problem_names = [] problem_names.append("piecewise_multi_vararray") @@ -39,7 +39,7 @@ model = p.define_model(**kwds) inst = model.create() - results = opt.solve(inst,tee=True) + results = opt.solve(inst, tee=True) inst.load(results) diff --git a/pyomo/solvers/tests/piecewise_linear/test_examples.py b/pyomo/solvers/tests/piecewise_linear/test_examples.py index 3b7364231ca..6412844175c 100644 --- a/pyomo/solvers/tests/piecewise_linear/test_examples.py +++ b/pyomo/solvers/tests/piecewise_linear/test_examples.py @@ -17,9 +17,10 @@ from filecmp import cmp import os from os.path import abspath, dirname, join -currdir = dirname(abspath(__file__))+os.sep -scriptdir = dirname(dirname(dirname(dirname(dirname(abspath(__file__))))))+os.sep -scriptdir = join(scriptdir,'examples','pyomo','piecewise') + +currdir = dirname(abspath(__file__)) + os.sep +scriptdir = dirname(dirname(dirname(dirname(dirname(abspath(__file__)))))) + os.sep +scriptdir = join(scriptdir, 'examples', 'pyomo', 'piecewise') import pyomo.common.unittest as unittest @@ -29,93 +30,96 @@ _NL_diff_tol = 1e-9 _LP_diff_tol = 1e-9 -class Test(unittest.TestCase): +class Test(unittest.TestCase): def run_convert2nl(self, name): os.chdir(currdir) - return convert.pyomo2nl(['--symbolic-solver-labels', - '--file-determinism', '1', - join(scriptdir,name)]) + return convert.pyomo2nl( + [ + '--symbolic-solver-labels', + '--file-determinism', + '1', + join(scriptdir, name), + ] + ) def run_convert2lp(self, name): os.chdir(currdir) - return convert.pyomo2lp(['--symbolic-solver-labels',join(scriptdir,name)]) + return convert.pyomo2lp(['--symbolic-solver-labels', join(scriptdir, name)]) def test_step_lp(self): """Test examples/pyomo/piecewise/step.py""" self.run_convert2lp('step.py') - _out, _log = join(currdir,'unknown.lp'), join(currdir, 'step.lp') - self.assertTrue(cmp(_out, _log), - msg="Files %s and %s differ" % (_out, _log)) + _out, _log = join(currdir, 'unknown.lp'), join(currdir, 'step.lp') + self.assertTrue(cmp(_out, _log), msg="Files %s and %s differ" % (_out, _log)) def test_step_nl(self): """Test examples/pyomo/piecewise/step.py""" self.run_convert2nl('step.py') - _test, _base = join(currdir,'unknown.nl'), join(currdir, 'step.nl') + _test, _base = join(currdir, 'unknown.nl'), join(currdir, 'step.nl') self.assertEqual(*load_and_compare_nl_baseline(_base, _test)) - os.remove(join(currdir,'unknown.row')) - os.remove(join(currdir,'unknown.col')) + os.remove(join(currdir, 'unknown.row')) + os.remove(join(currdir, 'unknown.col')) def test_nonconvex_lp(self): """Test examples/pyomo/piecewise/nonconvex.py""" self.run_convert2lp('nonconvex.py') - _out, _log = join(currdir,'unknown.lp'), join(currdir, 'nonconvex.lp') - self.assertTrue(cmp(_out, _log), - msg="Files %s and %s differ" % (_out, _log)) + _out, _log = join(currdir, 'unknown.lp'), join(currdir, 'nonconvex.lp') + self.assertTrue(cmp(_out, _log), msg="Files %s and %s differ" % (_out, _log)) def test_nonconvex_nl(self): """Test examples/pyomo/piecewise/nonconvex.py""" self.run_convert2nl('nonconvex.py') - _test, _base = join(currdir,'unknown.nl'), join(currdir, 'nonconvex.nl') + _test, _base = join(currdir, 'unknown.nl'), join(currdir, 'nonconvex.nl') self.assertEqual(*load_and_compare_nl_baseline(_base, _test)) - os.remove(join(currdir,'unknown.row')) - os.remove(join(currdir,'unknown.col')) + os.remove(join(currdir, 'unknown.row')) + os.remove(join(currdir, 'unknown.col')) def test_convex_lp(self): """Test examples/pyomo/piecewise/convex.py""" self.run_convert2lp('convex.py') - _out, _log = join(currdir,'unknown.lp'), join(currdir, 'convex.lp') - self.assertTrue(cmp(_out, _log), - msg="Files %s and %s differ" % (_out, _log)) + _out, _log = join(currdir, 'unknown.lp'), join(currdir, 'convex.lp') + self.assertTrue(cmp(_out, _log), msg="Files %s and %s differ" % (_out, _log)) def test_convex_nl(self): """Test examples/pyomo/piecewise/convex.py""" self.run_convert2nl('convex.py') - _test, _base = join(currdir,'unknown.nl'), join(currdir, 'convex.nl') + _test, _base = join(currdir, 'unknown.nl'), join(currdir, 'convex.nl') self.assertEqual(*load_and_compare_nl_baseline(_base, _test)) - os.remove(join(currdir,'unknown.row')) - os.remove(join(currdir,'unknown.col')) + os.remove(join(currdir, 'unknown.row')) + os.remove(join(currdir, 'unknown.col')) def test_indexed_lp(self): """Test examples/pyomo/piecewise/indexed.py""" self.run_convert2lp('indexed.py') - with open(join(currdir,'unknown.lp'), 'r') as f1, \ - open(join(currdir, 'indexed.lp'), 'r') as f2: - f1_contents = list(filter(None, f1.read().split())) - f2_contents = list(filter(None, f2.read().split())) - for item1, item2 in zip_longest(f1_contents, f2_contents): - try: - self.assertAlmostEqual(float(item1), float(item2)) - except: - self.assertEqual(item1, item2) + with open(join(currdir, 'unknown.lp'), 'r') as f1, open( + join(currdir, 'indexed.lp'), 'r' + ) as f2: + f1_contents = list(filter(None, f1.read().split())) + f2_contents = list(filter(None, f2.read().split())) + for item1, item2 in zip_longest(f1_contents, f2_contents): + try: + self.assertAlmostEqual(float(item1), float(item2)) + except: + self.assertEqual(item1, item2) def test_indexed_nl(self): """Test examples/pyomo/piecewise/indexed.py""" self.run_convert2nl('indexed.py') _base = join(currdir, 'indexed.nl') - _test = join(currdir,'unknown.nl') + _test = join(currdir, 'unknown.nl') self.assertEqual(*load_and_compare_nl_baseline(_base, _test)) - os.remove(join(currdir,'unknown.row')) - os.remove(join(currdir,'unknown.col')) + os.remove(join(currdir, 'unknown.row')) + os.remove(join(currdir, 'unknown.col')) def test_indexed_nonlinear_nl(self): """Test examples/pyomo/piecewise/indexed_nonlinear.py""" self.run_convert2nl('indexed_nonlinear.py') - _test = join(currdir,'unknown.nl') + _test = join(currdir, 'unknown.nl') _base = join(currdir, 'indexed_nonlinear.nl') self.assertEqual(*load_and_compare_nl_baseline(_base, _test)) - os.remove(join(currdir,'unknown.row')) - os.remove(join(currdir,'unknown.col')) + os.remove(join(currdir, 'unknown.row')) + os.remove(join(currdir, 'unknown.col')) if __name__ == "__main__": diff --git a/pyomo/solvers/tests/piecewise_linear/test_piecewise_linear.py b/pyomo/solvers/tests/piecewise_linear/test_piecewise_linear.py index 6bb76ca36f5..f9a654138d7 100644 --- a/pyomo/solvers/tests/piecewise_linear/test_piecewise_linear.py +++ b/pyomo/solvers/tests/piecewise_linear/test_piecewise_linear.py @@ -11,7 +11,8 @@ import os from os.path import dirname, abspath, join -thisDir = dirname( abspath(__file__) ) + +thisDir = dirname(abspath(__file__)) import pyomo.common.unittest as unittest @@ -23,116 +24,182 @@ from pyomo.core.base.piecewise import Bound, PWRepn from pyomo.solvers.tests.solvers import test_solver_cases -smoke_problems = ['convex_var','step_var','step_vararray'] +smoke_problems = ['convex_var', 'step_var', 'step_vararray'] -nightly_problems = ['convex_vararray', 'concave_vararray', \ - 'concave_var','piecewise_var', 'piecewise_vararray'] +nightly_problems = [ + 'convex_vararray', + 'concave_vararray', + 'concave_var', + 'piecewise_var', + 'piecewise_vararray', +] -expensive_problems = ['piecewise_multi_vararray', \ - 'convex_multi_vararray1','concave_multi_vararray1', \ - 'convex_multi_vararray2','concave_multi_vararray2'] +expensive_problems = [ + 'piecewise_multi_vararray', + 'convex_multi_vararray1', + 'concave_multi_vararray1', + 'convex_multi_vararray2', + 'concave_multi_vararray2', +] testing_solvers = {} -testing_solvers['gurobi','lp'] = False -#testing_solvers['cplex','lp'] = False -#testing_solvers['cplex','nl'] = False -#testing_solvers['ipopt','nl'] = False -#testing_solvers['cplex','python'] = False -#testing_solvers['_cplex_persistent','python'] = False +testing_solvers['gurobi', 'lp'] = False +# testing_solvers['cplex','lp'] = False +# testing_solvers['cplex','nl'] = False +# testing_solvers['ipopt','nl'] = False +# testing_solvers['cplex','python'] = False +# testing_solvers['_cplex_persistent','python'] = False for _solver, _io in test_solver_cases(): - if (_solver, _io) in testing_solvers and \ - test_solver_cases(_solver, _io).available: + if (_solver, _io) in testing_solvers and test_solver_cases(_solver, _io).available: testing_solvers[_solver, _io] = True - def createMethod(pName, problem, solver, writer, kwds): - def Method(obj): if not testing_solvers[solver, writer]: - obj.skipTest("Solver %s (interface=%s) is not available" - % (solver, writer)) + obj.skipTest("Solver %s (interface=%s) is not available" % (solver, writer)) - m = import_file(os.path.join(thisDir, 'problems', problem + '.py'), - clear_cache=True) + m = import_file( + os.path.join(thisDir, 'problems', problem + '.py'), clear_cache=True + ) model = m.define_model(**kwds) - opt = pyomo.opt.SolverFactory(solver,solver_io=writer) + opt = pyomo.opt.SolverFactory(solver, solver_io=writer) results = opt.solve(model) # non-recursive - new_results = ((var.name, var.value) - for var in model.component_data_objects(Var, - active=True, - descend_into=False)) - baseline_results = getattr(obj,problem+'_results') + new_results = ( + (var.name, var.value) + for var in model.component_data_objects( + Var, active=True, descend_into=False + ) + ) + baseline_results = getattr(obj, problem + '_results') for name, value in new_results: - if abs(baseline_results[name]-value) > 0.0001: - raise IOError("Difference in baseline solution values and " - "current solution values using:\n" + \ - "Solver: "+solver+"\n" + \ - "Writer: "+writer+"\n" + \ - "Variable: "+name+"\n" + \ - "Solution: "+str(value)+"\n" + \ - "Baseline: "+str(baseline_results[name])+"\n") + if abs(baseline_results[name] - value) > 0.0001: + raise IOError( + "Difference in baseline solution values and " + "current solution values using:\n" + + "Solver: " + + solver + + "\n" + + "Writer: " + + writer + + "\n" + + "Variable: " + + name + + "\n" + + "Solution: " + + str(value) + + "\n" + + "Baseline: " + + str(baseline_results[name]) + + "\n" + ) return Method + def assignProblems(cls, problem_list): - for solver,writer in testing_solvers: + for solver, writer in testing_solvers: for PROBLEM in problem_list: - aux_list = ['','force_pw'] + aux_list = ['', 'force_pw'] for AUX in aux_list: for REPN in PWRepn: for BOUND_TYPE in Bound: - for SENSE in [maximize,minimize]: - if not( ((BOUND_TYPE == Bound.Lower) and (SENSE == maximize)) or \ - ((BOUND_TYPE == Bound.Upper) and (SENSE == minimize)) or \ - ((REPN in [PWRepn.BIGM_BIN,PWRepn.BIGM_SOS1,PWRepn.MC]) and ('step' in PROBLEM)) ): + for SENSE in [maximize, minimize]: + if not ( + ((BOUND_TYPE == Bound.Lower) and (SENSE == maximize)) + or ((BOUND_TYPE == Bound.Upper) and (SENSE == minimize)) + or ( + ( + REPN + in [ + PWRepn.BIGM_BIN, + PWRepn.BIGM_SOS1, + PWRepn.MC, + ] + ) + and ('step' in PROBLEM) + ) + ): kwds = {} kwds['sense'] = SENSE kwds['pw_repn'] = REPN kwds['pw_constr_type'] = BOUND_TYPE if SENSE == maximize: - attrName = "test_{0}_{1}_{2}_{3}_{4}_{5}".format(PROBLEM,REPN,BOUND_TYPE,'maximize',solver,writer) + attrName = "test_{0}_{1}_{2}_{3}_{4}_{5}".format( + PROBLEM, + REPN, + BOUND_TYPE, + 'maximize', + solver, + writer, + ) elif SENSE == minimize: - attrName = "test_{0}_{1}_{2}_{3}_{4}_{5}".format(PROBLEM,REPN,BOUND_TYPE,'minimize',solver,writer) + attrName = "test_{0}_{1}_{2}_{3}_{4}_{5}".format( + PROBLEM, + REPN, + BOUND_TYPE, + 'minimize', + solver, + writer, + ) if AUX != '': kwds[AUX] = True - attrName += '_'+AUX - setattr(cls, - attrName, - createMethod(attrName, - PROBLEM, - solver, - writer, - kwds)) + attrName += '_' + AUX + setattr( + cls, + attrName, + createMethod( + attrName, PROBLEM, solver, writer, kwds + ), + ) if yaml_available: - with open(join(thisDir, - 'baselines', - PROBLEM+'_baseline_results.yml'),'r') as f: - baseline_results = yaml.load(f, **yaml_load_args) - setattr(cls,PROBLEM+'_results',baseline_results) + with open( + join( + thisDir, + 'baselines', + PROBLEM + '_baseline_results.yml', + ), + 'r', + ) as f: + baseline_results = yaml.load( + f, **yaml_load_args + ) + setattr( + cls, PROBLEM + '_results', baseline_results + ) + @unittest.skipUnless(yaml_available, "PyYAML module is not available.") -class PW_Tests(unittest.TestCase): pass +class PW_Tests(unittest.TestCase): + pass + class PiecewiseLinearTest_Smoke(PW_Tests): pass + + assignProblems(PiecewiseLinearTest_Smoke, smoke_problems) + class PiecewiseLinearTest_Nightly(PW_Tests): pass + + assignProblems(PiecewiseLinearTest_Nightly, nightly_problems) + @unittest.pytest.mark.expensive class PiecewiseLinearTest_Expensive(PW_Tests): pass + + assignProblems(PiecewiseLinearTest_Expensive, expensive_problems) if __name__ == "__main__": unittest.main() - diff --git a/pyomo/solvers/tests/piecewise_linear/test_piecewise_linear_kernel.py b/pyomo/solvers/tests/piecewise_linear/test_piecewise_linear_kernel.py index 6ab777da855..5d82be45c0f 100644 --- a/pyomo/solvers/tests/piecewise_linear/test_piecewise_linear_kernel.py +++ b/pyomo/solvers/tests/piecewise_linear/test_piecewise_linear_kernel.py @@ -12,7 +12,8 @@ import json import os from os.path import dirname, abspath, join -thisDir = dirname( abspath(__file__) ) + +thisDir = dirname(abspath(__file__)) import pyomo.common.unittest as unittest @@ -20,33 +21,29 @@ from pyomo.kernel import SolverFactory, variable, maximize, minimize from pyomo.solvers.tests.solvers import test_solver_cases -problems = ['convex_var', - 'concave_var', - 'piecewise_var', - 'step_var'] +problems = ['convex_var', 'concave_var', 'piecewise_var', 'step_var'] testing_solvers = {} -testing_solvers['gurobi','nl'] = False -#testing_solvers['cplex','lp'] = False -#testing_solvers['cplex','nl'] = False -#testing_solvers['ipopt','nl'] = False -#testing_solvers['cplex','python'] = False -#testing_solvers['_cplex_persistent','python'] = False +testing_solvers['gurobi', 'nl'] = False +# testing_solvers['cplex','lp'] = False +# testing_solvers['cplex','nl'] = False +# testing_solvers['ipopt','nl'] = False +# testing_solvers['cplex','python'] = False +# testing_solvers['_cplex_persistent','python'] = False for _solver, _io in test_solver_cases(): - if (_solver, _io) in testing_solvers and \ - test_solver_cases(_solver, _io).available: + if (_solver, _io) in testing_solvers and test_solver_cases(_solver, _io).available: testing_solvers[_solver, _io] = True -def createTestMethod(pName,problem,solver,writer,kwds): +def createTestMethod(pName, problem, solver, writer, kwds): def testMethod(obj): if not testing_solvers[solver, writer]: - obj.skipTest("Solver %s (interface=%s) is not available" - % (solver, writer)) + obj.skipTest("Solver %s (interface=%s) is not available" % (solver, writer)) - m = import_file(os.path.join(thisDir, 'kernel_problems', problem + '.py'), - clear_cache=True) + m = import_file( + os.path.join(thisDir, 'kernel_problems', problem + '.py'), clear_cache=True + ) model = m.define_model(**kwds) @@ -54,54 +51,100 @@ def testMethod(obj): results = opt.solve(model) # non-recursive - new_results = ((var.name, var.value) - for var in model.components(ctype=variable.ctype, - active=True, - descend_into=False)) - baseline_results = getattr(obj,problem+'_results') + new_results = ( + (var.name, var.value) + for var in model.components( + ctype=variable.ctype, active=True, descend_into=False + ) + ) + baseline_results = getattr(obj, problem + '_results') for name, value in new_results: - if abs(baseline_results[name]-value) > 0.00001: - raise IOError("Difference in baseline solution values and " - "current solution values using:\n" + \ - "Solver: "+solver+"\n" + \ - "Writer: "+writer+"\n" + \ - "Variable: "+name+"\n" + \ - "Solution: "+str(value)+"\n" + \ - "Baseline: "+str(baseline_results[name])+"\n") + if abs(baseline_results[name] - value) > 0.00001: + raise IOError( + "Difference in baseline solution values and " + "current solution values using:\n" + + "Solver: " + + solver + + "\n" + + "Writer: " + + writer + + "\n" + + "Variable: " + + name + + "\n" + + "Solution: " + + str(value) + + "\n" + + "Baseline: " + + str(baseline_results[name]) + + "\n" + ) return testMethod + def assignTests(cls, problem_list): - for solver,writer in testing_solvers: + for solver, writer in testing_solvers: for PROBLEM in problem_list: - aux_list = [{'simplify': True}, - {'simplify': False}] + aux_list = [{'simplify': True}, {'simplify': False}] for AUX in aux_list: - for REPN in ['sos2','mc','inc','cc','dcc','dlog','log']: - for BOUND_TYPE in ['lb','ub','eq']: + for REPN in ['sos2', 'mc', 'inc', 'cc', 'dcc', 'dlog', 'log']: + for BOUND_TYPE in ['lb', 'ub', 'eq']: for SENSE in [maximize, minimize]: - if not( ((BOUND_TYPE == 'lb') and (SENSE == maximize)) or \ - ((BOUND_TYPE == 'ub') and (SENSE == minimize)) or \ - ((REPN == 'mc') and ('step' in PROBLEM)) ): + if not ( + ((BOUND_TYPE == 'lb') and (SENSE == maximize)) + or ((BOUND_TYPE == 'ub') and (SENSE == minimize)) + or ((REPN == 'mc') and ('step' in PROBLEM)) + ): kwds = {} kwds['sense'] = SENSE kwds['repn'] = REPN kwds['bound'] = BOUND_TYPE if SENSE == maximize: - attrName = "test_{0}_{1}_{2}_{3}_{4}_{5}".format(PROBLEM,REPN,BOUND_TYPE,'maximize',solver,writer) + attrName = "test_{0}_{1}_{2}_{3}_{4}_{5}".format( + PROBLEM, + REPN, + BOUND_TYPE, + 'maximize', + solver, + writer, + ) else: assert SENSE == minimize - attrName = "test_{0}_{1}_{2}_{3}_{4}_{5}".format(PROBLEM,REPN,BOUND_TYPE,'minimize',solver,writer) + attrName = "test_{0}_{1}_{2}_{3}_{4}_{5}".format( + PROBLEM, + REPN, + BOUND_TYPE, + 'minimize', + solver, + writer, + ) assert len(AUX) == 1 kwds.update(AUX) - attrName += '_simplify_'+str(AUX['simplify']) - setattr(cls,attrName,createTestMethod(attrName,PROBLEM,solver,writer,kwds)) - with open(join(thisDir,'kernel_baselines',PROBLEM+'_baseline_results.json'),'r') as f: + attrName += '_simplify_' + str(AUX['simplify']) + setattr( + cls, + attrName, + createTestMethod( + attrName, PROBLEM, solver, writer, kwds + ), + ) + with open( + join( + thisDir, + 'kernel_baselines', + PROBLEM + '_baseline_results.json', + ), + 'r', + ) as f: baseline_results = json.load(f) - setattr(cls,PROBLEM+'_results',baseline_results) + setattr(cls, PROBLEM + '_results', baseline_results) + class PiecewiseLinearKernelTest(unittest.TestCase): pass + + assignTests(PiecewiseLinearKernelTest, problems) if __name__ == "__main__": diff --git a/pyomo/solvers/tests/solvers.py b/pyomo/solvers/tests/solvers.py index 191d548927c..fd1d2966a45 100644 --- a/pyomo/solvers/tests/solvers.py +++ b/pyomo/solvers/tests/solvers.py @@ -29,7 +29,7 @@ # ---------------------------------------------------------------- -licensed_solvers_with_demo_mode = {'baron',} +licensed_solvers_with_demo_mode = {'baron'} # # NOTE: we initialize the test case, since different @@ -51,8 +51,7 @@ def initialize(**kwds): obj.available = False elif hasattr(opt, 'executable') and opt.executable() is None: obj.available = False - elif not opt.license_is_valid() \ - and obj.name not in licensed_solvers_with_demo_mode: + elif not opt.license_is_valid() and obj.name not in licensed_solvers_with_demo_mode: obj.available = False else: obj.available = True @@ -69,7 +68,9 @@ def initialize(**kwds): if not (opt is None or isinstance(opt, UnknownSolver)): for _c in obj.capabilities: if not _c in opt._capabilities: - raise ValueError("Solver %s does not support capability %s!" % (obj.name, _c)) + raise ValueError( + "Solver %s does not support capability %s!" % (obj.name, _c) + ) # # Get version # @@ -85,66 +86,79 @@ def test_solver_cases(*args): if len(_test_solver_cases) == 0: logging.disable(logging.WARNING) - # # MOSEK # - _mosek_capabilities = set(['linear', - 'integer', - 'quadratic_objective', - 'quadratic_constraint', - 'conic_constraints']) - + _mosek_capabilities = set( + [ + 'linear', + 'integer', + 'quadratic_objective', + 'quadratic_constraint', + 'conic_constraints', + ] + ) + _test_solver_cases['mosek', 'python'] = initialize( name='mosek', io='python', capabilities=_mosek_capabilities, - import_suffixes=['dual', 'rc', 'slack']) + import_suffixes=['dual', 'rc', 'slack'], + ) # # MOSEK Persistent # - _test_solver_cases['mosek','persistent'] = initialize( - name = 'mosek', - io = 'persistent', - capabilities=_mosek_capabilities, - import_suffixes=['dual','rc','slack']) + _test_solver_cases['mosek', 'persistent'] = initialize( + name='mosek', + io='persistent', + capabilities=_mosek_capabilities, + import_suffixes=['dual', 'rc', 'slack'], + ) # # CPLEX # - _cplex_capabilities= set(['linear', - 'integer', - 'quadratic_objective', - 'quadratic_constraint', - 'sos1', - 'sos2']) + _cplex_capabilities = set( + [ + 'linear', + 'integer', + 'quadratic_objective', + 'quadratic_constraint', + 'sos1', + 'sos2', + ] + ) _test_solver_cases['cplex', 'lp'] = initialize( name='cplex', io='lp', capabilities=_cplex_capabilities, - import_suffixes=['slack','dual','rc']) + import_suffixes=['slack', 'dual', 'rc'], + ) _test_solver_cases['cplex', 'mps'] = initialize( name='cplex', io='mps', capabilities=_cplex_capabilities, - import_suffixes=['slack','dual','rc']) + import_suffixes=['slack', 'dual', 'rc'], + ) _test_solver_cases['cplex', 'nl'] = initialize( name='cplex', io='nl', capabilities=_cplex_capabilities, - import_suffixes=['dual']) + import_suffixes=['dual'], + ) _test_solver_cases['cplex', 'python'] = initialize( name='cplex', io='python', capabilities=_cplex_capabilities, - import_suffixes=['slack','dual','rc']) + import_suffixes=['slack', 'dual', 'rc'], + ) # # CPLEX PERSISTENT @@ -154,28 +168,30 @@ def test_solver_cases(*args): name='cplex_persistent', io='python', capabilities=_cplex_capabilities, - import_suffixes=['slack','dual','rc']) + import_suffixes=['slack', 'dual', 'rc'], + ) # # GAMS # - _gams_capabilities= set(['linear', - 'integer', - 'quadratic_objective', - 'quadratic_constraint']) + _gams_capabilities = set( + ['linear', 'integer', 'quadratic_objective', 'quadratic_constraint'] + ) _test_solver_cases['gams', 'gms'] = initialize( name='gams', io='gms', capabilities=_gams_capabilities, - import_suffixes=['dual','rc']) + import_suffixes=['dual', 'rc'], + ) _test_solver_cases['gams', 'python'] = initialize( name='gams', io='python', capabilities=_gams_capabilities, - import_suffixes=['dual','rc']) + import_suffixes=['dual', 'rc'], + ) # # GUROBI @@ -183,37 +199,45 @@ def test_solver_cases(*args): # **NOTE: Gurobi does not handle quadratic constraints before # Major Version 5 # - _gurobi_capabilities= set(['linear', - 'integer', - 'quadratic_objective', - 'quadratic_constraint', - 'sos1', - 'sos2']) + _gurobi_capabilities = set( + [ + 'linear', + 'integer', + 'quadratic_objective', + 'quadratic_constraint', + 'sos1', + 'sos2', + ] + ) _test_solver_cases['gurobi', 'lp'] = initialize( name='gurobi', io='lp', capabilities=_gurobi_capabilities, - import_suffixes=['slack','dual','rc']) + import_suffixes=['slack', 'dual', 'rc'], + ) _test_solver_cases['gurobi', 'mps'] = initialize( name='gurobi', io='mps', capabilities=_gurobi_capabilities, - import_suffixes=['slack','dual','rc']) + import_suffixes=['slack', 'dual', 'rc'], + ) _test_solver_cases['gurobi', 'nl'] = initialize( name='gurobi', io='nl', capabilities=_gurobi_capabilities, - options={'qcpdual':1,'simplex':1}, - import_suffixes=['dual']) + options={'qcpdual': 1, 'simplex': 1}, + import_suffixes=['dual'], + ) _test_solver_cases['gurobi', 'python'] = initialize( name='gurobi', io='python', capabilities=_gurobi_capabilities, - import_suffixes=['slack','dual','rc']) + import_suffixes=['slack', 'dual', 'rc'], + ) # # Gurobi PERSISTENT @@ -223,37 +247,40 @@ def test_solver_cases(*args): name='gurobi_persistent', io='python', capabilities=_gurobi_capabilities, - import_suffixes=['slack', 'dual', 'rc']) + import_suffixes=['slack', 'dual', 'rc'], + ) # # GLPK # - _glpk_capabilities= set(['linear', - 'integer']) + _glpk_capabilities = set(['linear', 'integer']) if 'GLPKSHELL_old' in str(pyomo.solvers.plugins.solvers.GLPK.GLPK().__class__): glpk_import_suffixes = ['dual'] else: - glpk_import_suffixes = ['rc','dual'] + glpk_import_suffixes = ['rc', 'dual'] _test_solver_cases['glpk', 'lp'] = initialize( name='glpk', io='lp', capabilities=_glpk_capabilities, - import_suffixes=glpk_import_suffixes) + import_suffixes=glpk_import_suffixes, + ) _test_solver_cases['glpk', 'mps'] = initialize( name='glpk', io='mps', capabilities=_glpk_capabilities, import_suffixes=glpk_import_suffixes, - io_options={"skip_objective_sense": True}) + io_options={"skip_objective_sense": True}, + ) _test_solver_cases['glpk', 'python'] = initialize( name='glpk', io='python', capabilities=_glpk_capabilities, - import_suffixes=[]) + import_suffixes=[], + ) # # CBC @@ -264,7 +291,8 @@ def test_solver_cases(*args): name='cbc', io='lp', capabilities=_cbc_lp_capabilities, - import_suffixes=['dual','rc']) + import_suffixes=['dual', 'rc'], + ) _cbc_nl_capabilities = set(['linear', 'integer', 'sos1', 'sos2']) @@ -272,35 +300,41 @@ def test_solver_cases(*args): name='cbc', io='nl', capabilities=_cbc_nl_capabilities, - import_suffixes=['dual']) + import_suffixes=['dual'], + ) - #_cbc_mps_capabilities = set(['linear', 'integer', 'sos1', 'sos2']) + # _cbc_mps_capabilities = set(['linear', 'integer', 'sos1', 'sos2']) - #_test_solver_cases['cbc', 'mps'] = initialize( - #name='cbc', - #io='mps', - #capabilities=_cbc_mps_capabilities, - #import_suffixes=['dual', 'rc']) + # _test_solver_cases['cbc', 'mps'] = initialize( + # name='cbc', + # io='mps', + # capabilities=_cbc_mps_capabilities, + # import_suffixes=['dual', 'rc']) # # XPRESS # - _xpress_capabilities= set(['linear', - 'integer', - 'quadratic_objective', - 'quadratic_constraint', - 'sos1', - 'sos2']) + _xpress_capabilities = set( + [ + 'linear', + 'integer', + 'quadratic_objective', + 'quadratic_constraint', + 'sos1', + 'sos2', + ] + ) _test_solver_cases['xpress', 'python'] = initialize( name='xpress_direct', io='python', capabilities=_xpress_capabilities, import_suffixes=['dual', 'rc', 'slack'], - options={'bargapstop':1e-9,}) + options={'bargapstop': 1e-9}, + ) # - # XPRESS PERSISTENT + # XPRESS PERSISTENT # _test_solver_cases['xpress_persistent', 'python'] = initialize( @@ -308,80 +342,88 @@ def test_solver_cases(*args): io='python', capabilities=_xpress_capabilities, import_suffixes=['slack', 'dual', 'rc'], - options={'bargapstop':1e-9,}) + options={'bargapstop': 1e-9}, + ) # # IPOPT # - _ipopt_capabilities= set(['linear', - 'quadratic_objective', - 'quadratic_constraint']) + _ipopt_capabilities = set( + ['linear', 'quadratic_objective', 'quadratic_constraint'] + ) _test_solver_cases['ipopt', 'nl'] = initialize( name='ipopt', io='nl', capabilities=_ipopt_capabilities, - import_suffixes=['dual']) + import_suffixes=['dual'], + ) # # SCIP # - _scip_capabilities= set(['linear', - 'integer', - 'quadratic_objective', - 'quadratic_constraint', - 'sos1', - 'sos2']) + _scip_capabilities = set( + [ + 'linear', + 'integer', + 'quadratic_objective', + 'quadratic_constraint', + 'sos1', + 'sos2', + ] + ) _test_solver_cases['scip', 'nl'] = initialize( - name='scip', - io='nl', - capabilities=_scip_capabilities, - import_suffixes=[]) + name='scip', io='nl', capabilities=_scip_capabilities, import_suffixes=[] + ) # # CONOPT # - _conopt_capabilities= set(['linear', - 'integer', - 'quadratic_objective', - 'quadratic_constraint', - 'sos1', - 'sos2']) + _conopt_capabilities = set( + [ + 'linear', + 'integer', + 'quadratic_objective', + 'quadratic_constraint', + 'sos1', + 'sos2', + ] + ) _test_solver_cases['conopt', 'nl'] = initialize( name='conopt', io='nl', capabilities=_conopt_capabilities, - import_suffixes=[]) + import_suffixes=[], + ) # # BARON # - _baron_capabilities= set(['linear', - 'integer', - 'quadratic_objective', - 'quadratic_constraint']) + _baron_capabilities = set( + ['linear', 'integer', 'quadratic_objective', 'quadratic_constraint'] + ) _test_solver_cases['baron', 'bar'] = initialize( name='baron', io='bar', capabilities=_baron_capabilities, - import_suffixes=['rc','dual']) + import_suffixes=['rc', 'dual'], + ) # # KNITROAMPL # - _knitroampl_capabilities= set(['linear', - 'integer', - 'quadratic_objective', - 'quadratic_constraint']) + _knitroampl_capabilities = set( + ['linear', 'integer', 'quadratic_objective', 'quadratic_constraint'] + ) _test_solver_cases['knitroampl', 'nl'] = initialize( name='knitroampl', io='nl', capabilities=_knitroampl_capabilities, - import_suffixes=['dual']) - + import_suffixes=['dual'], + ) logging.disable(logging.NOTSET) @@ -400,8 +442,8 @@ def test_solver_cases(*args): if sc.io_options is None: sc.io_options = {} assert (sc.io is not None) and (type(sc.io) is str) - assert type(sc.export_suffixes) in [list,tuple] - assert type(sc.import_suffixes) in [list,tuple] + assert type(sc.export_suffixes) in [list, tuple] + assert type(sc.import_suffixes) in [list, tuple] assert type(sc.options) is dict for tag in sc.export_suffixes: assert type(tag) is str @@ -414,4 +456,3 @@ def test_solver_cases(*args): if len(args) == 0: return _test_solver_cases.keys() return _test_solver_cases[args] - diff --git a/pyomo/solvers/tests/testcases.py b/pyomo/solvers/tests/testcases.py index 96dc6c3a143..6828666e480 100644 --- a/pyomo/solvers/tests/testcases.py +++ b/pyomo/solvers/tests/testcases.py @@ -19,7 +19,7 @@ from pyomo.core.kernel.block import IBlock # For expected failures that appear in all known version -_trunk_version = (float('inf'), float('inf'), float('inf'), float('inf')) +_trunk_version = (float('inf'), float('inf'), float('inf'), float('inf')) # These are usually due to a bug in the latest version of the # thirdparty solver. Tests will be expected to fail. If they do not, @@ -53,12 +53,14 @@ for _test in ('QCP_simple', 'QCP_simple_nosuffixes', 'MIQCP_simple'): ExpectedFailures['mosek', _io, _test] = ( lambda v: True, - "Mosek does not handle nonconvex quadratic constraints") + "Mosek does not handle nonconvex quadratic constraints", + ) - for _test in ('MIQP_simple', ): + for _test in ('MIQP_simple',): SkipTests['mosek', _io, _test] = ( lambda v: v[0] == 10 and v < (10, 0, 30), - "Mosek 10 fails on assertion warmstarting MIQP models; see #2613") + "Mosek 10 fails on assertion warmstarting MIQP models; see #2613", + ) # # CPLEX @@ -66,33 +68,39 @@ MissingSuffixFailures['cplex', 'lp', 'QCP_simple'] = ( lambda v: v <= _trunk_version, - {'dual': (True, {'qc0','qc1'})}, - "Cplex does not report duals of quadratic constraints.") + {'dual': (True, {'qc0', 'qc1'})}, + "Cplex does not report duals of quadratic constraints.", +) MissingSuffixFailures['cplex', 'mps', 'QCP_simple'] = ( lambda v: v <= _trunk_version, - {'dual': (True, {'qc0','qc1'})}, - "Cplex does not report duals of quadratic constraints.") + {'dual': (True, {'qc0', 'qc1'})}, + "Cplex does not report duals of quadratic constraints.", +) MissingSuffixFailures['cplex', 'python', 'QCP_simple'] = ( lambda v: v <= _trunk_version, - {'dual': (True, {'qc0','qc1'})}, - "Cplex does not report duals of quadratic constraints.") + {'dual': (True, {'qc0', 'qc1'})}, + "Cplex does not report duals of quadratic constraints.", +) MissingSuffixFailures['cplex_persistent', 'python', 'QCP_simple'] = ( lambda v: v <= _trunk_version, - {'dual': (True, {'qc0','qc1'})}, - "Cplex does not report duals of quadratic constraints.") + {'dual': (True, {'qc0', 'qc1'})}, + "Cplex does not report duals of quadratic constraints.", +) MissingSuffixFailures['cplex', 'nl', 'QCP_simple'] = ( - lambda v: v < (12,6,0,0), - {'dual': (True, {'qc0','qc1'})}, - "Cplex does not report duals of quadratic constraints.") + lambda v: v < (12, 6, 0, 0), + {'dual': (True, {'qc0', 'qc1'})}, + "Cplex does not report duals of quadratic constraints.", +) SkipTests['cplex', 'nl', 'QCP_simple'] = ( - lambda v: v == (12,6,0,0), + lambda v: v == (12, 6, 0, 0), "Cplex 12.6.0.0 produces inconsistent dual values based on " - "NL variable ordering (which changes between the NLv1 and NLv2 writers") + "NL variable ordering (which changes between the NLv1 and NLv2 writers", +) # # GUROBI @@ -104,44 +112,50 @@ # GLPK # -ExpectedFailures['glpk', 'lp', 'MILP_discrete_var_bounds'] = \ - (lambda v: v <= (4,52,0,0), +ExpectedFailures['glpk', 'lp', 'MILP_discrete_var_bounds'] = ( + lambda v: v <= (4, 52, 0, 0), "Glpk ignores bounds on Binary variables through the " - "LP file interface. A ticket has been filed.") + "LP file interface. A ticket has been filed.", +) -ExpectedFailures['glpk', 'mps', 'LP_duals_maximize'] = \ - (lambda v: v <= _trunk_version, +ExpectedFailures['glpk', 'mps', 'LP_duals_maximize'] = ( + lambda v: v <= _trunk_version, "Glpk does not accept the OBJSENSE section of the Free MPS format. " - "Therefore maximization models are not explicitly handled.") + "Therefore maximization models are not explicitly handled.", +) # # CBC # -ExpectedFailures['cbc', 'nl', 'MILP_unbounded'] = \ - (lambda v: v <= _trunk_version, - "Cbc fails to report an unbounded MILP model as unbounded through " - "the NL interface (through 2.9.x), and fails with invalid free() " - "(in 2.10.x).") +ExpectedFailures['cbc', 'nl', 'MILP_unbounded'] = ( + lambda v: v <= _trunk_version, + "Cbc fails to report an unbounded MILP model as unbounded through " + "the NL interface (through 2.9.x), and fails with invalid free() " + "(in 2.10.x).", +) # The following is due to a bug introduced into Clp as part of CBC 2.10 # and was resolved by Clp commit 130dd199 (13 Feb 2021), and included # in the CBC 2.10.6 release -ExpectedFailures['cbc', 'nl', 'LP_unbounded'] = \ - (lambda v: v > (2, 10) and v < (2, 10, 6), - "Cbc fails (invalid free()) for unbounded LP models through " - "the NL interface in 2.10.x versions through 2.10.5 " - "(reported upstream as coin-or/Cbc#389)") - -ExpectedFailures['cbc', 'nl', 'SOS1_simple'] = \ - (lambda v: v[:2] == (2, 10), - "Cbc segfaults for SOS constraints in the NL interface " - "(reported upstream as coin-or/Cbc#388)") - -ExpectedFailures['cbc', 'nl', 'SOS2_simple'] = \ - (lambda v: v[:2] == (2, 10), - "Cbc segfaults for SOS constraints in the NL interface " - "(reported upstream as coin-or/Cbc#388)") +ExpectedFailures['cbc', 'nl', 'LP_unbounded'] = ( + lambda v: v > (2, 10) and v < (2, 10, 6), + "Cbc fails (invalid free()) for unbounded LP models through " + "the NL interface in 2.10.x versions through 2.10.5 " + "(reported upstream as coin-or/Cbc#389)", +) + +ExpectedFailures['cbc', 'nl', 'SOS1_simple'] = ( + lambda v: v[:2] == (2, 10), + "Cbc segfaults for SOS constraints in the NL interface " + "(reported upstream as coin-or/Cbc#388)", +) + +ExpectedFailures['cbc', 'nl', 'SOS2_simple'] = ( + lambda v: v[:2] == (2, 10), + "Cbc segfaults for SOS constraints in the NL interface " + "(reported upstream as coin-or/Cbc#388)", +) # # XPRESS @@ -153,88 +167,98 @@ # IPOPT # -ExpectedFailures['ipopt', 'nl', 'LP_duals_maximize'] = \ - (lambda v: v == (3,10,3,0), - "Ipopt returns duals with a different sign convention. " - "Fixed in Ipopt 3.10.4") - -ExpectedFailures['ipopt', 'nl', 'QCP_simple'] = \ - (lambda v: v == (3,10,3,0), - "Ipopt returns duals with a different sign convention. " - "Fixed in Ipopt 3.10.4") - -ExpectedFailures['ipopt', 'nl', 'LP_block'] = \ - (lambda v: v <= (3,10,2,0), - "Ipopt returns duals with a different sign convention. " - "Fixed in Ipopt 3.10.3") - -ExpectedFailures['ipopt', 'nl', 'LP_duals_minimize'] = \ - (lambda v: v <= (3,10,2,0), - "Ipopt returns duals with a different sign convention. " - "Fixed in Ipopt 3.10.3") - -ExpectedFailures['ipopt', 'nl', 'LP_inactive_index'] = \ - (lambda v: v <= (3,10,2,0), - "Ipopt returns duals with a different sign convention. " - "Fixed in Ipopt 3.10.3") - -ExpectedFailures['ipopt', 'nl', 'LP_piecewise'] = \ - (lambda v: v <= (3,10,2,0), - "Ipopt returns duals with a different sign convention. " - "Fixed in Ipopt 3.10.3") - -ExpectedFailures['ipopt', 'nl', 'LP_simple'] = \ - (lambda v: v <= (3,10,2,0), - "Ipopt returns duals with a different sign convention. " - "Fixed in Ipopt 3.10.3") - -ExpectedFailures['ipopt', 'nl', 'QP_simple'] = \ - (lambda v: v <= (3,10,2,0), - "Ipopt returns duals with a different sign convention. " - "Fixed in Ipopt 3.10.3") - -ExpectedFailures['ipopt', 'nl', 'LP_trivial_constraints'] = \ - (lambda v: v <= (3,10,2,0), - "Ipopt returns duals with a different sign convention. " - "Fixed in Ipopt 3.10.3") +ExpectedFailures['ipopt', 'nl', 'LP_duals_maximize'] = ( + lambda v: v == (3, 10, 3, 0), + "Ipopt returns duals with a different sign convention. " "Fixed in Ipopt 3.10.4", +) + +ExpectedFailures['ipopt', 'nl', 'QCP_simple'] = ( + lambda v: v == (3, 10, 3, 0), + "Ipopt returns duals with a different sign convention. " "Fixed in Ipopt 3.10.4", +) + +ExpectedFailures['ipopt', 'nl', 'LP_block'] = ( + lambda v: v <= (3, 10, 2, 0), + "Ipopt returns duals with a different sign convention. " "Fixed in Ipopt 3.10.3", +) + +ExpectedFailures['ipopt', 'nl', 'LP_duals_minimize'] = ( + lambda v: v <= (3, 10, 2, 0), + "Ipopt returns duals with a different sign convention. " "Fixed in Ipopt 3.10.3", +) + +ExpectedFailures['ipopt', 'nl', 'LP_inactive_index'] = ( + lambda v: v <= (3, 10, 2, 0), + "Ipopt returns duals with a different sign convention. " "Fixed in Ipopt 3.10.3", +) + +ExpectedFailures['ipopt', 'nl', 'LP_piecewise'] = ( + lambda v: v <= (3, 10, 2, 0), + "Ipopt returns duals with a different sign convention. " "Fixed in Ipopt 3.10.3", +) + +ExpectedFailures['ipopt', 'nl', 'LP_simple'] = ( + lambda v: v <= (3, 10, 2, 0), + "Ipopt returns duals with a different sign convention. " "Fixed in Ipopt 3.10.3", +) + +ExpectedFailures['ipopt', 'nl', 'QP_simple'] = ( + lambda v: v <= (3, 10, 2, 0), + "Ipopt returns duals with a different sign convention. " "Fixed in Ipopt 3.10.3", +) + +ExpectedFailures['ipopt', 'nl', 'LP_trivial_constraints'] = ( + lambda v: v <= (3, 10, 2, 0), + "Ipopt returns duals with a different sign convention. " "Fixed in Ipopt 3.10.3", +) # # SCIP # -ExpectedFailures['scip', 'nl', 'SOS2_simple'] = \ - (lambda v: v <= (3, 1, 0, 9), +ExpectedFailures['scip', 'nl', 'SOS2_simple'] = ( + lambda v: v <= (3, 1, 0, 9), "SCIP (scip) does not recognize sos2 constraints " - "inside NL files. A ticket has been filed.") + "inside NL files. A ticket has been filed.", +) -ExpectedFailures['scip', 'nl', 'SOS1_simple'] = \ - (lambda v: v <= (3, 1, 0, 9), +ExpectedFailures['scip', 'nl', 'SOS1_simple'] = ( + lambda v: v <= (3, 1, 0, 9), "SCIP (scip) does not recognize sos1 constraints " - "inside NL files. A ticket has been filed.") + "inside NL files. A ticket has been filed.", +) # # BARON # SkipTests['baron', 'bar', 'LP_trivial_constraints'] = ( lambda v: v[:3] == (22, 1, 19), - 'BARON 22.1.19 hits an infinite loop for this test case' + 'BARON 22.1.19 hits an infinite loop for this test case', ) -for prob in ('QP_simple_nosuffixes', 'QP_simple_nosuffixes_kernel', - 'QP_simple', 'QP_simple_kernel', - 'MIQP_simple', 'MIQP_simple_kernel', - 'MILP_simple', 'MILP_simple_kernel', - 'LP_simple', 'LP_simple_kernel', - 'LP_block', 'LP_block_kernel'): +for prob in ( + 'QP_simple_nosuffixes', + 'QP_simple_nosuffixes_kernel', + 'QP_simple', + 'QP_simple_kernel', + 'MIQP_simple', + 'MIQP_simple_kernel', + 'MILP_simple', + 'MILP_simple_kernel', + 'LP_simple', + 'LP_simple_kernel', + 'LP_block', + 'LP_block_kernel', +): ExpectedFailures['baron', 'bar', prob] = ( lambda v: v[:3] == (22, 1, 19), - 'BARON 22.1.19 reports model as infeasible' + 'BARON 22.1.19 reports model as infeasible', ) for prob in ('LP_unbounded', 'LP_unbounded_kernel'): ExpectedFailures['baron', 'bar', prob] = ( lambda v: v[:3] == (22, 1, 19), - 'BARON 22.1.19 reports model as optimal' + 'BARON 22.1.19 reports model as optimal', ) # @@ -315,8 +339,6 @@ # "found during preprocessing.") - - # # KNITROAMPL # @@ -338,7 +360,7 @@ def generate_scenarios(arg=None): # Skip this test case if the solver doesn't support the # capabilities required by the model - if not _model.capabilities.issubset( _solver_case.capabilities ): + if not _model.capabilities.issubset(_solver_case.capabilities): continue # Set status values for expected failures @@ -347,23 +369,22 @@ def generate_scenarios(arg=None): msg = "" case_skip = SkipTests.get((solver, io, _model.description), None) case_suffix = MissingSuffixFailures.get( - (solver, io, _model.description), None) - case_fail = ExpectedFailures.get( - (solver, io, _model.description), None) + (solver, io, _model.description), None + ) + case_fail = ExpectedFailures.get((solver, io, _model.description), None) if not _solver_case.available: status = 'skip' - msg = ("Skipping test because solver %s (%s) is unavailable" - % (solver, io)) - elif (case_skip is not None and - _ver is not None and case_skip[0](_ver)): + msg = "Skipping test because solver %s (%s) is unavailable" % ( + solver, + io, + ) + elif case_skip is not None and _ver is not None and case_skip[0](_ver): status = 'skip' msg = case_skip[1] - elif (case_fail is not None and - _ver is not None and case_fail[0](_ver)): + elif case_fail is not None and _ver is not None and case_fail[0](_ver): status = 'expected failure' msg = case_fail[1] - elif (case_suffix is not None and - _ver is not None and case_suffix[0](_ver)): + elif case_suffix is not None and _ver is not None and case_suffix[0](_ver): if type(case_suffix[1]) is dict: exclude_suffixes.update(case_suffix[1]) else: @@ -373,9 +394,14 @@ def generate_scenarios(arg=None): # Return scenario dimensions and scenario information yield (model, solver, io), Bunch( - status=status, msg=msg, model=_model, solver=None, - testcase=_solver_case, demo_limits=_solver_case.demo_limits, - exclude_suffixes=exclude_suffixes) + status=status, + msg=msg, + model=_model, + solver=None, + testcase=_solver_case, + demo_limits=_solver_case.demo_limits, + exclude_suffixes=exclude_suffixes, + ) def run_scenarios(options): @@ -405,7 +431,8 @@ def run_scenarios(options): test_case.testcase.io_options, {}, symbolic_labels, - load_solutions) + load_solutions, + ) termination_condition = results['Solver'][0]['termination condition'] # Validate solution status @@ -415,10 +442,15 @@ def run_scenarios(options): if test_case.status == 'expected failure': stat[key] = (True, "Expected failure") else: - stat[key] = (False, "Unexpected termination condition: %s" % str(termination_condition)) + stat[key] = ( + False, + "Unexpected termination condition: %s" % str(termination_condition), + ) continue - if termination_condition == TerminationCondition.unbounded or \ - termination_condition == TerminationCondition.infeasible: + if ( + termination_condition == TerminationCondition.unbounded + or termination_condition == TerminationCondition.infeasible + ): # Unbounded or Infeasible stat[key] = (True, "") else: @@ -427,9 +459,11 @@ def run_scenarios(options): model_class.model.load_solution(results.solution) else: model_class.model.solutions.load_from( - results, - default_variable_value=opt.default_variable_value()) - rc = model_class.validate_current_solution(suffixes=model_class.test_suffixes) + results, default_variable_value=opt.default_variable_value() + ) + rc = model_class.validate_current_solution( + suffixes=model_class.test_suffixes + ) if test_case.status == 'expected failure': if rc[0] is True: @@ -455,8 +489,7 @@ def run_scenarios(options): for key in stat: model, solver, io = key if not solver in summary: - summary[solver] = Bunch(NumEPass=0, NumEFail=0, - NumUPass=0, NumUFail=0) + summary[solver] = Bunch(NumEPass=0, NumEFail=0, NumUPass=0, NumUFail=0) _pass, _str = stat[key] if _pass: if _str == "Expected failure": @@ -468,11 +501,15 @@ def run_scenarios(options): if _str == "Unexpected failure": summary[solver].NumUFail += 1 if options.verbose: - print("- Unexpected Test Failure: "+", ".join((model, solver, io))) + print( + "- Unexpected Test Failure: " + ", ".join((model, solver, io)) + ) else: summary[solver].NumUPass += 1 if options.verbose: - print("- Unexpected Test Success: "+", ".join((model, solver, io))) + print( + "- Unexpected Test Success: " + ", ".join((model, solver, io)) + ) if options.verbose: if nfail == 0: print("- NONE") @@ -480,12 +517,18 @@ def run_scenarios(options): stream = sys.stdout maxSolverNameLen = max([max(len(name) for name in summary), len("Solver")]) - fmtStr = "{{0:<{0}}}| {{1:>8}} | {{2:>8}} | {{3:>10}} | {{4:>10}} | {{5:>13}}\n".format(maxSolverNameLen + 2) + fmtStr = ( + "{{0:<{0}}}| {{1:>8}} | {{2:>8}} | {{3:>10}} | {{4:>10}} | {{5:>13}}\n".format( + maxSolverNameLen + 2 + ) + ) # stream.write("\n") stream.write("Solver Test Summary\n") stream.write("=" * (maxSolverNameLen + 66) + "\n") - stream.write(fmtStr.format("Solver", "# Pass", "# Fail", "# OK Fail", "# Bad Pass", "% OK")) + stream.write( + fmtStr.format("Solver", "# Pass", "# Fail", "# OK Fail", "# Bad Pass", "% OK") + ) stream.write("=" * (maxSolverNameLen + 66) + "\n") # for _solver in sorted(summary): @@ -494,10 +537,45 @@ def run_scenarios(options): total.NumEFail += ans.NumEFail total.NumUPass += ans.NumUPass total.NumUFail += ans.NumUFail - stream.write(fmtStr.format(_solver, str(ans.NumEPass), str(ans.NumUFail), str(ans.NumEFail), str(ans.NumUPass), str(int(100.0*(ans.NumEPass+ans.NumEFail)/(ans.NumEPass+ans.NumEFail+ans.NumUFail+ans.NumUPass))))) + stream.write( + fmtStr.format( + _solver, + str(ans.NumEPass), + str(ans.NumUFail), + str(ans.NumEFail), + str(ans.NumUPass), + str( + int( + 100.0 + * (ans.NumEPass + ans.NumEFail) + / (ans.NumEPass + ans.NumEFail + ans.NumUFail + ans.NumUPass) + ) + ), + ) + ) # stream.write("=" * (maxSolverNameLen + 66) + "\n") - stream.write(fmtStr.format("TOTALS", str(total.NumEPass), str(total.NumUFail), str(total.NumEFail), str(total.NumUPass), str(int(100.0*(total.NumEPass+total.NumEFail)/(total.NumEPass+total.NumEFail+total.NumUFail+total.NumUPass))))) + stream.write( + fmtStr.format( + "TOTALS", + str(total.NumEPass), + str(total.NumUFail), + str(total.NumEFail), + str(total.NumUPass), + str( + int( + 100.0 + * (total.NumEPass + total.NumEFail) + / ( + total.NumEPass + + total.NumEFail + + total.NumUFail + + total.NumUPass + ) + ) + ), + ) + ) stream.write("=" * (maxSolverNameLen + 66) + "\n") logging.disable(logging.NOTSET) @@ -506,7 +584,7 @@ def run_scenarios(options): if __name__ == "__main__": print("") print("Testing model generation") - print("-"*30) + print("-" * 30) for key in sorted(all_models()): print(key) obj = all_models(key)() @@ -515,8 +593,7 @@ def run_scenarios(options): print("") print("Testing scenario generation") - print("-"*30) + print("-" * 30) for key, value in generate_scenarios(): print(", ".join(key)) print(" %s: %s" % (value.status, value.msg)) - diff --git a/pyomo/solvers/wrappers.py b/pyomo/solvers/wrappers.py index 29d07e64b6d..3b083f7a14f 100644 --- a/pyomo/solvers/wrappers.py +++ b/pyomo/solvers/wrappers.py @@ -9,9 +9,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -class MIPSolverWrapper(object): - def add(self, constraint): #pragma:nocover +class MIPSolverWrapper(object): + def add(self, constraint): # pragma:nocover pass - - From 72b2dbfe34e59f60b2668e2b65bc1a439fb50b3e Mon Sep 17 00:00:00 2001 From: Miranda Mundt Date: Tue, 14 Feb 2023 07:40:08 -0700 Subject: [PATCH 2/2] Fix odd spacing ffrom black --- pyomo/solvers/plugins/solvers/GAMS.py | 4 ++-- pyomo/solvers/plugins/solvers/GUROBI.py | 4 ++-- pyomo/solvers/plugins/solvers/GUROBI_RUN.py | 2 +- .../solvers/direct_or_persistent_solver.py | 16 ++++++++-------- pyomo/solvers/plugins/solvers/gurobi_direct.py | 8 ++++---- pyomo/solvers/plugins/solvers/mosek_direct.py | 2 +- .../solvers/plugins/solvers/persistent_solver.py | 2 +- pyomo/solvers/plugins/solvers/xpress_direct.py | 12 +++++++----- pyomo/solvers/tests/models/base.py | 2 +- 9 files changed, 27 insertions(+), 25 deletions(-) diff --git a/pyomo/solvers/plugins/solvers/GAMS.py b/pyomo/solvers/plugins/solvers/GAMS.py index a5cc27635da..35207578052 100644 --- a/pyomo/solvers/plugins/solvers/GAMS.py +++ b/pyomo/solvers/plugins/solvers/GAMS.py @@ -254,7 +254,7 @@ def solve(self, *args, **kwds): if len(args) != 1: raise ValueError( - 'Exactly one model must be passed ' 'to solve method of GAMSSolver.' + 'Exactly one model must be passed to solve method of GAMSSolver.' ) model = args[0] @@ -768,7 +768,7 @@ def solve(self, *args, **kwds): if len(args) != 1: raise ValueError( - 'Exactly one model must be passed ' 'to solve method of GAMSSolver.' + 'Exactly one model must be passed to solve method of GAMSSolver.' ) model = args[0] diff --git a/pyomo/solvers/plugins/solvers/GUROBI.py b/pyomo/solvers/plugins/solvers/GUROBI.py index bf30b5a50f1..2dbdaf87e8d 100644 --- a/pyomo/solvers/plugins/solvers/GUROBI.py +++ b/pyomo/solvers/plugins/solvers/GUROBI.py @@ -146,7 +146,7 @@ def license_is_valid(self): try: rc = subprocess.run( [solver_exec], - input=('import gurobipy; ' 'gurobipy.Env().dispose(); quit()'), + input=('import gurobipy; gurobipy.Env().dispose(); quit()'), stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, @@ -281,7 +281,7 @@ def _get_version(self): else: results = subprocess.run( [solver_exec], - input=('import gurobipy; ' 'print(gurobipy.gurobi.version()); quit()'), + input=('import gurobipy; print(gurobipy.gurobi.version()); quit()'), stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, diff --git a/pyomo/solvers/plugins/solvers/GUROBI_RUN.py b/pyomo/solvers/plugins/solvers/GUROBI_RUN.py index 059ace780cf..dcd46e9b19f 100644 --- a/pyomo/solvers/plugins/solvers/GUROBI_RUN.py +++ b/pyomo/solvers/plugins/solvers/GUROBI_RUN.py @@ -215,7 +215,7 @@ def gurobi_run(model_file, warmstart_file, soln_file, mipgap, options, suffixes) solution_status = 'stoppedByLimit' else: status = 'error' - message = "Unhandled Gurobi solve status " "(" + str(solver_status) + ")" + message = "Unhandled Gurobi solve status (" + str(solver_status) + ")" term_cond = 'error' solution_status = 'error' assert solution_status is not None diff --git a/pyomo/solvers/plugins/solvers/direct_or_persistent_solver.py b/pyomo/solvers/plugins/solvers/direct_or_persistent_solver.py index bdba4b2b4d7..97ba96ef322 100644 --- a/pyomo/solvers/plugins/solvers/direct_or_persistent_solver.py +++ b/pyomo/solvers/plugins/solvers/direct_or_persistent_solver.py @@ -245,44 +245,44 @@ def _add_block(self, block): obj_counter += 1 if obj_counter > 1: raise ValueError( - "Solver interface does not " "support multiple objectives." + "Solver interface does not support multiple objectives." ) self._set_objective(obj) """ This method should be implemented by subclasses.""" def _set_objective(self, obj): - raise NotImplementedError("This method should be implemented " "by subclasses") + raise NotImplementedError("This method should be implemented by subclasses") """ This method should be implemented by subclasses.""" def _add_constraint(self, con): - raise NotImplementedError("This method should be implemented " "by subclasses") + raise NotImplementedError("This method should be implemented by subclasses") """ This method should be implemented by subclasses.""" def _add_sos_constraint(self, con): - raise NotImplementedError("This method should be implemented " "by subclasses") + raise NotImplementedError("This method should be implemented by subclasses") """ This method should be implemented by subclasses.""" def _add_var(self, var): - raise NotImplementedError("This method should be implemented " "by subclasses") + raise NotImplementedError("This method should be implemented by subclasses") """ This method should be implemented by subclasses.""" def _get_expr_from_pyomo_repn(self, repn, max_degree=None): - raise NotImplementedError("This method should be implemented " "by subclasses") + raise NotImplementedError("This method should be implemented by subclasses") """ This method should be implemented by subclasses.""" def _get_expr_from_pyomo_expr(self, expr, max_degree=None): - raise NotImplementedError("This method should be implemented " "by subclasses") + raise NotImplementedError("This method should be implemented by subclasses") """ This method should be implemented by subclasses.""" def _load_vars(self, vars_to_load): - raise NotImplementedError("This method should be implemented " "by subclasses") + raise NotImplementedError("This method should be implemented by subclasses") def load_vars(self, vars_to_load=None): """ diff --git a/pyomo/solvers/plugins/solvers/gurobi_direct.py b/pyomo/solvers/plugins/solvers/gurobi_direct.py index 80a0f5214bd..0123bf3c16f 100644 --- a/pyomo/solvers/plugins/solvers/gurobi_direct.py +++ b/pyomo/solvers/plugins/solvers/gurobi_direct.py @@ -362,12 +362,12 @@ def _add_constraint(self, con): if con.has_lb(): if not is_fixed(con.lower): raise ValueError( - "Lower bound of constraint {0} " "is not constant.".format(con) + "Lower bound of constraint {0} is not constant.".format(con) ) if con.has_ub(): if not is_fixed(con.upper): raise ValueError( - "Upper bound of constraint {0} " "is not constant.".format(con) + "Upper bound of constraint {0} is not constant.".format(con) ) if con.equality: @@ -422,7 +422,7 @@ def _add_sos_constraint(self, con): sos_type = gurobipy.GRB.SOS_TYPE2 else: raise ValueError( - "Solver does not support SOS " "level {0} constraints".format(level) + "Solver does not support SOS level {0} constraints".format(level) ) gurobi_vars = [] @@ -667,7 +667,7 @@ def _postsolve(self): else: self.results.solver.status = SolverStatus.error self.results.solver.termination_message = ( - "Unhandled Gurobi solve status " "(" + str(status) + ")" + "Unhandled Gurobi solve status (" + str(status) + ")" ) self.results.solver.termination_condition = TerminationCondition.error soln.status = SolutionStatus.error diff --git a/pyomo/solvers/plugins/solvers/mosek_direct.py b/pyomo/solvers/plugins/solvers/mosek_direct.py index 92518981ab3..dfe144f2f5b 100644 --- a/pyomo/solvers/plugins/solvers/mosek_direct.py +++ b/pyomo/solvers/plugins/solvers/mosek_direct.py @@ -591,7 +591,7 @@ def _add_block(self, block): obj_counter += 1 if obj_counter > 1: raise ValueError( - "Solver interface does not " "support multiple objectives." + "Solver interface does not support multiple objectives." ) self._set_objective(obj) diff --git a/pyomo/solvers/plugins/solvers/persistent_solver.py b/pyomo/solvers/plugins/solvers/persistent_solver.py index bba9a75f94a..800071a75fe 100644 --- a/pyomo/solvers/plugins/solvers/persistent_solver.py +++ b/pyomo/solvers/plugins/solvers/persistent_solver.py @@ -225,7 +225,7 @@ def add_column(self, model, var, obj_coef, constraints, coefficients): raise RuntimeError('The pyomo var must be attached to the solver model') if var in self._pyomo_var_to_solver_var_map: raise RuntimeError( - 'The pyomo var must not have been already added to ' 'the solver model' + 'The pyomo var must not have been already added to the solver model' ) if len(constraints) != len(coefficients): raise RuntimeError( diff --git a/pyomo/solvers/plugins/solvers/xpress_direct.py b/pyomo/solvers/plugins/solvers/xpress_direct.py index b2a89580860..3873a3804dd 100644 --- a/pyomo/solvers/plugins/solvers/xpress_direct.py +++ b/pyomo/solvers/plugins/solvers/xpress_direct.py @@ -298,7 +298,7 @@ def _get_mip_results(self, results, soln): else: results.solver.status = SolverStatus.error results.solver.termination_message = ( - "Unhandled Xpress solve status " "(" + str(status) + ")" + "Unhandled Xpress solve status (" + str(status) + ")" ) results.solver.termination_condition = TerminationCondition.error soln.status = SolutionStatus.error @@ -401,7 +401,7 @@ def _get_lp_results(self, results, soln): else: results.solver.status = SolverStatus.error results.solver.termination_message = ( - "Unhandled Xpress solve status " "(" + str(status) + ")" + "Unhandled Xpress solve status (" + str(status) + ")" ) results.solver.termination_condition = TerminationCondition.error soln.status = SolutionStatus.error @@ -686,12 +686,12 @@ def _add_constraint(self, con): if con.has_lb(): if not is_fixed(con.lower): raise ValueError( - "Lower bound of constraint {0} " "is not constant.".format(con) + "Lower bound of constraint {0} is not constant.".format(con) ) if con.has_ub(): if not is_fixed(con.upper): raise ValueError( - "Upper bound of constraint {0} " "is not constant.".format(con) + "Upper bound of constraint {0} is not constant.".format(con) ) if con.equality: @@ -737,7 +737,7 @@ def _add_sos_constraint(self, con): level = con.level if level not in [1, 2]: raise ValueError( - "Solver does not support SOS " "level {0} constraints".format(level) + "Solver does not support SOS level {0} constraints".format(level) ) xpress_vars = [] @@ -1039,6 +1039,8 @@ def _load_slacks(self, cons_to_load=None): if xpress_con in self._range_constraints: ## for xpress, the slack on a range constraint ## is based on the upper bound + ## FIXME: This looks like a bug - there is no variable named + ## `con` - there is, however, `xpress_con` and `pyomo_con` lb = con.lb ub = con.ub ub_s = val diff --git a/pyomo/solvers/tests/models/base.py b/pyomo/solvers/tests/models/base.py index a75a78a7b86..00434fb0af5 100644 --- a/pyomo/solvers/tests/models/base.py +++ b/pyomo/solvers/tests/models/base.py @@ -205,7 +205,7 @@ def validate_current_solution(self, **kwds): assert suf.import_enabled() solution = None error_str = ( - "Difference in solution for {0}.{1}:\n\tBaseline " "- {2}\n\tCurrent - {3}" + "Difference in solution for {0}.{1}:\n\tBaseline - {2}\n\tCurrent - {3}" ) with open(self.results_file, 'r') as f: