Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
121 commits
Select commit Hold shift + click to select a range
e552439
Add first draft of SCIP persistent solving
Opt-Mucca Mar 18, 2024
6a14f10
Add SCIPPersistent to docs
Opt-Mucca Mar 18, 2024
c107909
Add SCIp to Github action scripts
Opt-Mucca Mar 19, 2024
e00ded8
Remove 5.0.0 specific version. Add conda to workflow
Opt-Mucca Mar 19, 2024
a0b6250
Standardise string formatting to fstring
Opt-Mucca Mar 19, 2024
d0816eb
Add parameter link to docstring
Opt-Mucca Mar 19, 2024
0e11f11
Remove redundant second objective sense check
Opt-Mucca Mar 19, 2024
068ec99
Clean up _post_solve with a helper function for status handling
Opt-Mucca Mar 19, 2024
63af6d8
Remove individual skip_test option
Opt-Mucca Mar 19, 2024
4075d9a
Update pyomo/solvers/plugins/solvers/scip_persistent.py
Opt-Mucca Mar 19, 2024
f55fcc5
Update from the black command
Opt-Mucca Mar 19, 2024
e2b49b2
Merge remote-tracking branch 'origin/main'
Opt-Mucca Mar 19, 2024
91eae7b
Fix typos
Opt-Mucca Mar 19, 2024
5ee2007
Replace trySol via more safe checkSol
Opt-Mucca Mar 19, 2024
f6ff092
Adds support for partial solution loading
Opt-Mucca Mar 20, 2024
e7ac980
Add error handling for setting non-linear objective
Opt-Mucca Mar 21, 2024
c90bb23
Merge branch 'main' into main
Opt-Mucca Apr 10, 2024
2540f65
Remove dual and rc loading for SCIP. Fix bug of ranged rows
Opt-Mucca Apr 19, 2024
9e5d944
Add safe con.body.constant check
Opt-Mucca Apr 19, 2024
f90dfad
Remove slack loading for SCIP
Opt-Mucca Apr 29, 2024
f703d1f
Remove dual loading test for SCIP
Opt-Mucca Apr 29, 2024
5c02d32
Remove slack for suffix in tests
Opt-Mucca Apr 29, 2024
8ebcf88
Remove TODO for nonlinear handling
Opt-Mucca Apr 29, 2024
236427f
Merge branch 'main' into main
mrmundt Apr 30, 2024
30d8cc6
Skip LP_trivial_constraints for SCIP persistent
Opt-Mucca Jun 13, 2024
8d787ae
Merge branch 'main' into main
Opt-Mucca Jun 13, 2024
30e5e65
Add transformation for add_cons with non float/int rhs e.g. np.int
Opt-Mucca Jun 13, 2024
9104a92
Add warning if type is converted. Tidy up logic
Opt-Mucca Jun 13, 2024
a0324af
Merge branch 'main' into main
blnicho Aug 6, 2024
cbcb96d
Merge branch 'main' into main
blnicho Oct 15, 2024
fecd201
Merge branch 'main' into main
mrmundt Oct 29, 2024
1a4663d
Merge branch 'main' into pr/3200
blnicho Nov 13, 2024
cadfe5c
Merge branch 'main' into main
blnicho Nov 19, 2024
f3f2d7c
Fix num. vars and cons from transformed. Silent warm start fail
Opt-Mucca Nov 20, 2024
e4cdc6e
Merge branch 'main' into main
blnicho Dec 10, 2024
f326493
Merge branch 'main' into main
blnicho Jan 27, 2025
d0df2fc
Merge branch 'main' into main
blnicho Feb 13, 2025
7b18354
Add minor changes
Opt-Mucca Feb 19, 2025
859a1b2
Merge branch 'main' into main
mrmundt Feb 19, 2025
9a6c3eb
Merge branch 'main' into main
mrmundt Mar 20, 2025
27e3d10
Change copyright 2024 to 2025
Opt-Mucca Mar 21, 2025
e916067
Merge branch 'main' into main
mrmundt Mar 24, 2025
ed2cf25
Merge branch 'main' into main
mrmundt Apr 16, 2025
5ea53cd
Merge branch 'main' into main
mrmundt Apr 22, 2025
5aba506
Merge branch 'main' into main
mrmundt Jun 4, 2025
8caea1c
Merge branch 'main' into main
blnicho Jul 3, 2025
dbe63c0
Merge branch 'main' into main
mrmundt Jul 8, 2025
d16bee5
adding tests for trivial constraints and fixing bugs
michaelbynum Aug 14, 2025
2001d15
Merge branch 'solver_api' into trivial_constraints
michaelbynum Aug 14, 2025
d25e721
run black
michaelbynum Aug 14, 2025
16ecce0
Merge remote-tracking branch 'opt_mucca/main' into scip
michaelbynum Aug 14, 2025
9e7cd0d
moving scip to contrib solvers
michaelbynum Aug 14, 2025
bf204bb
moving scip to contrib solvers
michaelbynum Aug 14, 2025
dabf031
porting scip interface
michaelbynum Aug 15, 2025
9601768
porting scip interface
michaelbynum Aug 15, 2025
9d2f22a
porting scip interface
michaelbynum Aug 15, 2025
b483729
bugs and tests
michaelbynum Aug 15, 2025
37a31a7
scip direct
michaelbynum Aug 15, 2025
02f383d
Merge branch 'solver_api' into trivial_constraints
michaelbynum Aug 16, 2025
3e3509c
Merge branch 'trivial_constraints' into scip_port
michaelbynum Aug 16, 2025
3d302a1
Merge branch 'solver_api' into trivial_constraints
michaelbynum Aug 16, 2025
6603db6
Merge branch 'trivial_constraints' into scip_port
michaelbynum Aug 16, 2025
0b84dcc
more expression types for scip
michaelbynum Aug 18, 2025
865b622
Merge remote-tracking branch 'michaelbynum/scip_port' into scip_port
michaelbynum Aug 18, 2025
ecd602d
Merge branch 'solver_api' into trivial_constraints
michaelbynum Aug 18, 2025
efdcd69
Merge branch 'trivial_constraints' into scip_port
michaelbynum Aug 18, 2025
3180462
capture_fd for scip
michaelbynum Aug 20, 2025
b522edd
Merge remote-tracking branch 'michaelbynum/scip_port' into scip_port
michaelbynum Aug 20, 2025
72912e0
working on persistent interface to scip
michaelbynum Aug 22, 2025
cfa1e91
minor fixes
michaelbynum Sep 1, 2025
92e77ba
merge solver_api into trivial_constraints
michaelbynum Oct 5, 2025
281ddc3
merge trivial_constraints into scip_port
michaelbynum Oct 5, 2025
e6331df
persistent interface to scip
michaelbynum Oct 6, 2025
98e2c9a
update docs
michaelbynum Oct 6, 2025
f0be4ff
update docs
michaelbynum Oct 6, 2025
7ec95a8
persistent interface to scip
michaelbynum Oct 6, 2025
75903d6
persistent interface to scip
michaelbynum Oct 6, 2025
0051024
updating tests
michaelbynum Oct 6, 2025
b037b9c
forgot to delete/revert some files
michaelbynum Oct 6, 2025
c200e2e
run black
michaelbynum Oct 6, 2025
960c531
typos
michaelbynum Oct 6, 2025
2f99306
merge observer into scip_port
michaelbynum Oct 16, 2025
045f537
merge main
michaelbynum Dec 12, 2025
4487a69
merge main
michaelbynum Dec 12, 2025
253bdd9
Merge branch 'solver_api' into trivial_constraints
michaelbynum Dec 18, 2025
3d6f8bb
Merge branch 'trivial_constraints' into scip_port
michaelbynum Dec 18, 2025
879ed3a
update scip interface to use observer
michaelbynum Dec 18, 2025
705ec75
Merge remote-tracking branch 'origin/main' into scip_port
michaelbynum Jan 8, 2026
3bfa5cb
run black
michaelbynum Jan 8, 2026
c3ad9b5
Merge branch 'solver_api' into trivial_constraints
michaelbynum Jan 29, 2026
3898591
Merge branch 'trivial_constraints' into scip_port
michaelbynum Jan 29, 2026
33d490a
Merge branch 'solver_api' into scip_port
michaelbynum Feb 12, 2026
ef6dcf6
Merge branch 'solver_api' into trivial_constraints
michaelbynum Feb 13, 2026
7709a98
Merge branch 'trivial_constraints' into scip_port
michaelbynum Feb 13, 2026
fb38368
Merge branch 'solver_api' into trivial_constraints
michaelbynum Feb 13, 2026
9a581ad
Merge branch 'trivial_constraints' into scip_port
michaelbynum Feb 13, 2026
0365ae5
merge solver_api into trivial_constraints
michaelbynum Mar 23, 2026
d725038
merge trivial_constraints into scip_port
michaelbynum Mar 23, 2026
aec0e65
merge trivial_constraints into scip_port
michaelbynum Mar 23, 2026
1ae0890
bug
michaelbynum Mar 23, 2026
7293c29
Merge branch 'solver_api' into trivial_constraints
michaelbynum Mar 23, 2026
70059fa
Merge branch 'trivial_constraints' into scip_port
michaelbynum Mar 23, 2026
75fee85
Merge branch 'solver_api' into trivial_constraints
michaelbynum Mar 26, 2026
045b8ab
Merge branch 'trivial_constraints' into scip_port
michaelbynum Mar 26, 2026
29a72f7
Merge branch 'solver_api' into trivial_constraints
michaelbynum Apr 9, 2026
2b6c48e
Merge branch 'trivial_constraints' into scip_port
michaelbynum Apr 9, 2026
c216e19
Merge branch 'solver_api' into trivial_constraints
michaelbynum Apr 15, 2026
25c59ed
merge in main
michaelbynum Apr 15, 2026
8c6e488
run black
michaelbynum Apr 15, 2026
3646b41
fix typos
michaelbynum Apr 15, 2026
b754a29
merge main
michaelbynum Apr 27, 2026
ae7d2d9
run black
michaelbynum Apr 27, 2026
87a0f75
Merge remote-tracking branch 'origin/main' into trivial_constraints
michaelbynum May 1, 2026
d0c0515
remove redundant code and propagate error messages
michaelbynum May 1, 2026
bf6f6fd
fix highs interface
michaelbynum May 1, 2026
a1ed8e0
better handling of trivially feasible and infeasible constraints
michaelbynum May 3, 2026
29b9bf3
run black
michaelbynum May 3, 2026
eb7abb6
better handling of trivially infeasible constraints
michaelbynum May 3, 2026
c987a9f
run black
michaelbynum May 3, 2026
fe00fdd
better handling of trivially infeasible constraints
michaelbynum May 3, 2026
73498a8
merge trivial_constraints into scip_port
michaelbynum May 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .github/workflows/test_branches.yml
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,12 @@ jobs:
|| echo "WARNING: Xpress Community Edition is not available"
python -m pip install --cache-dir cache/pip maingopy \
|| echo "WARNING: MAiNGO is not available"
if [[ ${{matrix.python}} == pypy* ]]; then
echo "skipping SCIP for pypy"
else
python -m pip install --cache-dir cache/pip pyscipopt \
|| echo "WARNING: SCIP is not available"
fi
if [[ ${{matrix.python}} == pypy* ]]; then
echo "skipping wntr for pypy"
else
Expand Down Expand Up @@ -415,7 +421,7 @@ jobs:
else
XPRESS='xpress'
fi
for PKG in "$CPLEX" docplex gurobi "$XPRESS" cyipopt pymumps scip; do
for PKG in "$CPLEX" docplex gurobi "$XPRESS" cyipopt pymumps scip pyscipopt; do
echo ""
echo "*** Install $PKG ***"
echo ""
Expand Down
8 changes: 7 additions & 1 deletion .github/workflows/test_pr_and_main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,12 @@ jobs:
|| echo "WARNING: Xpress Community Edition is not available"
python -m pip install --cache-dir cache/pip maingopy \
|| echo "WARNING: MAiNGO is not available"
if [[ ${{matrix.python}} == pypy* ]]; then
echo "skipping SCIP for pypy"
else
python -m pip install --cache-dir cache/pip pyscipopt \
|| echo "WARNING: SCIP is not available"
fi
if [[ ${{matrix.python}} == pypy* ]]; then
echo "skipping wntr for pypy"
else
Expand Down Expand Up @@ -467,7 +473,7 @@ jobs:
else
XPRESS='xpress'
fi
for PKG in "$CPLEX" docplex gurobi "$XPRESS" cyipopt pymumps scip; do
for PKG in "$CPLEX" docplex gurobi "$XPRESS" cyipopt pymumps scip pyscipopt; do
echo ""
echo "*** Install $PKG ***"
echo ""
Expand Down
1 change: 1 addition & 0 deletions pyomo/contrib/observer/model_observer.py
Original file line number Diff line number Diff line change
Expand Up @@ -739,6 +739,7 @@ def remove_objectives(self, objs: Collection[ObjectiveData]):
def _check_for_unknown_active_components(self):
for ctype in self._model.collect_ctypes(active=True, descend_into=True):
if not issubclass(ctype, ActiveComponent):
# strangely, this is needed to skip things like Param
continue
if ctype in self._known_active_ctypes:
continue
Expand Down
20 changes: 20 additions & 0 deletions pyomo/contrib/solver/common/results.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
ADVANCED_OPTION,
DEVELOPER_OPTION,
)
from pyomo.contrib.solver.common.util import NoOptimalSolutionError, NoSolutionError
from pyomo.contrib.solver.common.solution_loader import NoSolutionSolutionLoader
from pyomo.opt.results.solution import SolutionStatus as LegacySolutionStatus
from pyomo.opt.results.solver import (
TerminationCondition as LegacyTerminationCondition,
Expand Down Expand Up @@ -242,6 +244,24 @@ def display(
return super().display(content_filter, indent_spacing, ostream, visibility)


def get_infeasible_results(config, err_msg, solver_name, solver_version):
res = Results()
res.solution_loader = NoSolutionSolutionLoader(err_msg)
res.solution_status = SolutionStatus.noSolution
res.termination_condition = TerminationCondition.provenInfeasible
res.incumbent_objective = None
res.objective_bound = None
res.timing_info.gurobi_time = None
res.solver_config = config
res.solver_name = solver_name
res.solver_version = solver_version
if config.raise_exception_on_nonoptimal_result:
raise NoOptimalSolutionError(err_msg)
if config.load_solutions:
raise NoSolutionError(err_msg)
return res


# Everything below here preserves backwards compatibility

legacy_termination_condition_map = {
Expand Down
27 changes: 27 additions & 0 deletions pyomo/contrib/solver/common/solution_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
from pyomo.core.base.var import VarData
from pyomo.core.staleflag import StaleFlagManager
from pyomo.core.base.suffix import Suffix
from .util import NoSolutionError
import logging

logger = logging.getLogger(__name__)


class SolutionLoader:
Expand Down Expand Up @@ -331,6 +335,29 @@ def load_import_suffixes(self):
return self._loader.load_import_suffixes()


class NoSolutionSolutionLoader(SolutionLoader):
def __init__(self, err_msg: str) -> None:
self.err_msg = err_msg

def get_number_of_solutions(self) -> int:
return 0

def get_vars(
self, vars_to_load: Sequence[VarData] | None = None
) -> Mapping[VarData, float]:
raise NoSolutionError(self.err_msg)

def get_duals(
self, cons_to_load: Sequence[ConstraintData] | None = None
) -> dict[ConstraintData, float]:
raise NoSolutionError(self.err_msg)

def get_reduced_costs(
self, vars_to_load: Sequence[VarData] | None = None
) -> Mapping[VarData, float]:
raise NoSolutionError(self.err_msg)


class PersistentSolutionLoader(SolutionLoader):
"""
Loader for persistent solvers
Expand Down
11 changes: 11 additions & 0 deletions pyomo/contrib/solver/plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from .solvers.gurobi.gurobi_persistent import GurobiPersistent
from .solvers.gurobi.gurobi_direct_minlp import GurobiDirectMINLP
from .solvers.highs import Highs
from .solvers.scip.scip_direct import ScipDirect, ScipPersistent
from .solvers.gams import GAMS
from .solvers.knitro.direct import KnitroDirectSolver

Expand Down Expand Up @@ -43,6 +44,16 @@ def load():
SolverFactory.register(name='gams', legacy_name='gams_v2', doc='Interface to GAMS')(
GAMS
)
SolverFactory.register(
name='scip_direct',
legacy_name='scip_direct_v2',
doc='Direct interface pyscipopt',
)(ScipDirect)
SolverFactory.register(
name='scip_persistent',
legacy_name='scip_persistent_v2',
doc='Persistent interface pyscipopt',
)(ScipPersistent)
SolverFactory.register(
name="knitro_direct",
legacy_name="knitro_direct",
Expand Down
206 changes: 110 additions & 96 deletions pyomo/contrib/solver/solvers/gams.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import pathlib

from pyomo.common.dependencies import attempt_import
from pyomo.common.errors import InfeasibleConstraintException
from pyomo.common.fileutils import Executable, ExecutableData
from pyomo.common.config import (
ConfigValue,
Expand All @@ -37,6 +38,7 @@
Results,
SolutionStatus,
TerminationCondition,
get_infeasible_results,
)
from pyomo.contrib.solver.solvers.gms_sol_reader import GMSSolutionLoader

Expand Down Expand Up @@ -292,110 +294,122 @@ def solve(self, model, **kwds):

output_filename = None
with TempfileManager.new_context() as tempfile:
# IMPORTANT - only delete the whole tmpdir if the solver was the one
# that made the directory. Otherwise, just delete the files the solver
# made, if not keepfiles. That way the user can select a directory
# they already have, like the current directory, without having to
# worry about the rest of the contents of that directory being deleted.
if not config.working_dir:
dname = tempfile.mkdtemp()
else:
dname = config.working_dir
if not os.path.exists(dname):
os.mkdir(dname)
basename = os.path.join(dname, model_name)
output_filename = basename + '.gms'
lst_filename = os.path.join(dname, lst)

timer.start(f'write_gms_file')
with open(output_filename, 'w', newline='\n', encoding='utf-8') as gms_file:
gms_info = GAMSWriter().write(
model, gms_file, config=config.writer_config
)
# NOTE: omit InfeasibleConstraintException for now
timer.stop(f'write_gms_file')
try:
# IMPORTANT - only delete the whole tmpdir if the solver was the one
# that made the directory. Otherwise, just delete the files the solver
# made, if not keepfiles. That way the user can select a directory
# they already have, like the current directory, without having to
# worry about the rest of the contents of that directory being deleted.
if not config.working_dir:
dname = tempfile.mkdtemp()
else:
dname = config.working_dir
if not os.path.exists(dname):
os.mkdir(dname)
basename = os.path.join(dname, model_name)
output_filename = basename + '.gms'
lst_filename = os.path.join(dname, lst)

timer.start(f'write_gms_file')
with open(
output_filename, 'w', newline='\n', encoding='utf-8'
) as gms_file:
gms_info = GAMSWriter().write(
model, gms_file, config=config.writer_config
)
timer.stop(f'write_gms_file')

if config.writer_config.put_results_format == 'gdx':
results_filename = os.path.join(dname, "GAMS_MODEL_p.gdx")
statresults_filename = os.path.join(
dname, "%s_s.gdx" % (config.writer_config.put_results,)
)
else:
results_filename = os.path.join(
dname, "%s.dat" % (config.writer_config.put_results,)
)
statresults_filename = os.path.join(
dname, "%sstat.dat" % (config.writer_config.put_results,)
)
if config.writer_config.put_results_format == 'gdx':
results_filename = os.path.join(dname, "GAMS_MODEL_p.gdx")
statresults_filename = os.path.join(
dname, "%s_s.gdx" % (config.writer_config.put_results,)
)
else:
results_filename = os.path.join(
dname, "%s.dat" % (config.writer_config.put_results,)
)
statresults_filename = os.path.join(
dname, "%sstat.dat" % (config.writer_config.put_results,)
)

####################################################################
# Apply solver
####################################################################
exe_path = config.executable.path()
command = [exe_path, output_filename, "o=" + lst, "curdir=" + dname]
####################################################################
# Apply solver
####################################################################
exe_path = config.executable.path()
command = [exe_path, output_filename, "o=" + lst, "curdir=" + dname]

# handled tee and logfile based on the length of list and
# string respectively
command.append(self._log_levels[(bool(config.tee), bool(config.logfile))])
# handled tee and logfile based on the length of list and
# string respectively
command.append(
self._log_levels[(bool(config.tee), bool(config.logfile))]
)

ostreams = [StringIO()]
if config.tee:
ostreams.append(sys.stdout)
ostreams = [StringIO()]
if config.tee:
ostreams.append(sys.stdout)

with TeeStream(*ostreams) as t:
timer.start('subprocess')
subprocess_result = subprocess.run(
command, stdout=t.STDOUT, stderr=t.STDERR, cwd=dname
)
timer.stop('subprocess')
rc = subprocess_result.returncode
txt = ostreams[0].getvalue()
if config.working_dir:
logger.info("\nGAMS WORKING DIRECTORY: %s\n" % config.working_dir)

if rc:
# If nothing was raised, or for all other cases, raise this
error_message = f"GAMS process encountered an error (returncode={rc})."
if rc == 3:
# Execution Error
# Run check_expr_evaluation, which errors if necessary
error_message += (
"\nError rc=3 (GAMS execution error), to be determined later."
with TeeStream(*ostreams) as t:
timer.start('subprocess')
subprocess_result = subprocess.run(
command, stdout=t.STDOUT, stderr=t.STDERR, cwd=dname
)
error_message += "\nCheck listing file for details.\n"
logger.error(error_message)
logger.error(txt.strip())
if os.path.exists(lst_filename):
with open(lst_filename, 'r') as FILE:
logger.error(
"\nGAMS Listing file:\n\n%s" % (FILE.read().strip(),)
)
raise RuntimeError(error_message)

timer.start('parse_results')
if config.writer_config.put_results_format == 'gdx':
model_soln, stat_vars = self._parse_gdx_results(
config, results_filename, statresults_filename
)
else:
model_soln, stat_vars = self._parse_dat_results(
config, results_filename, statresults_filename
)
timer.stop('parse_results')

####################################################################
# Postsolve (WIP)
results = self._postsolve(
model, timer, config, model_soln, stat_vars, gms_info
)
timer.stop('subprocess')
rc = subprocess_result.returncode
txt = ostreams[0].getvalue()
if config.working_dir:
logger.info("\nGAMS WORKING DIRECTORY: %s\n" % config.working_dir)

if rc:
# If nothing was raised, or for all other cases, raise this
error_message = (
f"GAMS process encountered an error (returncode={rc})."
)
if rc == 3:
# Execution Error
# Run check_expr_evaluation, which errors if necessary
error_message += "\nError rc=3 (GAMS execution error), to be determined later."
error_message += "\nCheck listing file for details.\n"
logger.error(error_message)
logger.error(txt.strip())
if os.path.exists(lst_filename):
with open(lst_filename, 'r') as FILE:
logger.error(
"\nGAMS Listing file:\n\n%s" % (FILE.read().strip(),)
)
raise RuntimeError(error_message)

timer.start('parse_results')
if config.writer_config.put_results_format == 'gdx':
model_soln, stat_vars = self._parse_gdx_results(
config, results_filename, statresults_filename
)
else:
model_soln, stat_vars = self._parse_dat_results(
config, results_filename, statresults_filename
)
timer.stop('parse_results')

results.solver_config = config
results.solver_log = ostreams[0].getvalue()
####################################################################
# Postsolve (WIP)
results = self._postsolve(
model, timer, config, model_soln, stat_vars, gms_info
)

tock = time.perf_counter()
results.timing_info.start_timestamp = start_timestamp
results.timing_info.wall_time = tock - tick
results.timing_info.timer = timer
results.solver_config = config
results.solver_log = ostreams[0].getvalue()

tock = time.perf_counter()
results.timing_info.start_timestamp = start_timestamp
results.timing_info.wall_time = tock - tick
results.timing_info.timer = timer
except InfeasibleConstraintException as err:
err_msg = f'Solution loader does not currently have a valid solution because the problem was proven to be infeasible ({str(err)}). Please check results.termination_condition and/or results.solution_status.'
results = get_infeasible_results(
config=config,
err_msg=err_msg,
solver_name=self.name,
solver_version=self.version(),
)
return results

def _postsolve(self, model, timer, config, model_soln, stat_vars, gms_info):
Expand Down
Loading