Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
49 changes: 12 additions & 37 deletions pyomo/contrib/solver/solvers/knitro/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
NoReducedCostsError,
NoSolutionError,
)
from pyomo.contrib.solver.solvers.knitro.api import knitro
from pyomo.contrib.solver.solvers.knitro.config import KnitroConfig
from pyomo.contrib.solver.solvers.knitro.engine import Engine
from pyomo.contrib.solver.solvers.knitro.package import PackageChecker
Expand Down Expand Up @@ -204,57 +203,33 @@ def _get_items(self, item_type: type[ItemType]) -> Sequence[ItemType]:

@staticmethod
def _get_solution_status(status: int) -> SolutionStatus:
if (
status == knitro.KN_RC_OPTIMAL
or status == knitro.KN_RC_OPTIMAL_OR_SATISFACTORY
or status == knitro.KN_RC_NEAR_OPT
):
if status in {0, -100}:
return SolutionStatus.optimal
elif status == knitro.KN_RC_FEAS_NO_IMPROVE:
elif -101 >= status >= -199 or -400 >= status >= -409:
return SolutionStatus.feasible
elif (
status == knitro.KN_RC_INFEASIBLE
or status == knitro.KN_RC_INFEAS_CON_BOUNDS
or status == knitro.KN_RC_INFEAS_VAR_BOUNDS
or status == knitro.KN_RC_INFEAS_NO_IMPROVE
):
elif status in {-200, -204, -205, -206}:
return SolutionStatus.infeasible
else:
return SolutionStatus.noSolution

@staticmethod
def _get_termination_condition(status: int) -> TerminationCondition:
if (
status == knitro.KN_RC_OPTIMAL
or status == knitro.KN_RC_OPTIMAL_OR_SATISFACTORY
or status == knitro.KN_RC_NEAR_OPT
):
if status in {0, -100}:
return TerminationCondition.convergenceCriteriaSatisfied
elif status == knitro.KN_RC_INFEAS_NO_IMPROVE:
elif status == -202:
return TerminationCondition.locallyInfeasible
elif (
status == knitro.KN_RC_INFEASIBLE
or status == knitro.KN_RC_INFEAS_CON_BOUNDS
or status == knitro.KN_RC_INFEAS_VAR_BOUNDS
):
elif status in {-200, -204, -205}:
return TerminationCondition.provenInfeasible
elif (
status == knitro.KN_RC_UNBOUNDED_OR_INFEAS
or status == knitro.KN_RC_UNBOUNDED
):
elif status in {-300, -301}:
return TerminationCondition.infeasibleOrUnbounded
elif (
status == knitro.KN_RC_ITER_LIMIT_FEAS
or status == knitro.KN_RC_ITER_LIMIT_INFEAS
):
elif status in {-400, -410}:
return TerminationCondition.iterationLimit
elif (
status == knitro.KN_RC_TIME_LIMIT_FEAS
or status == knitro.KN_RC_TIME_LIMIT_INFEAS
):
elif status in {-401, -411}:
return TerminationCondition.maxTimeLimit
elif status == knitro.KN_RC_USER_TERMINATION:
elif status == -500:
return TerminationCondition.interrupted
elif -500 > status >= -599:
return TerminationCondition.error
else:
return TerminationCondition.unknown

Expand Down
51 changes: 50 additions & 1 deletion pyomo/contrib/solver/tests/solvers/test_knitro_direct.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@
from contextlib import redirect_stdout

import pyomo.common.unittest as unittest
import pyomo.environ as pyo

from pyomo.contrib.solver.common.results import SolutionStatus, TerminationCondition
from pyomo.contrib.solver.solvers.knitro.config import KnitroConfig
from pyomo.contrib.solver.solvers.knitro.direct import KnitroDirectSolver
import pyomo.environ as pyo

avail = KnitroDirectSolver().available()

Expand Down Expand Up @@ -84,6 +85,54 @@ def test_available_cache(self):
self.assertTrue(opt._available_cache)
self.assertIsNotNone(opt._available_cache)

def test_solution_status_mapping(self):
opt = KnitroDirectSolver()
for opt_status in [0, -100]:
status = opt._get_solution_status(opt_status)
self.assertEqual(status, SolutionStatus.optimal)

for opt_status in [*range(-101, -103, -1), *range(-400, -406, -1)]:
status = opt._get_solution_status(opt_status)
self.assertEqual(status, SolutionStatus.feasible)

for opt_status in [-200, -204, -205, -206]:
status = opt._get_solution_status(opt_status)
self.assertEqual(status, SolutionStatus.infeasible)

for opt_status in [-501, -99999, -1]:
status = opt._get_solution_status(opt_status)
self.assertEqual(status, SolutionStatus.noSolution)

def test_termination_condition_mapping(self):
opt = KnitroDirectSolver()
for opt_status in [0, -100]:
term_cond = opt._get_termination_condition(opt_status)
self.assertEqual(
term_cond, TerminationCondition.convergenceCriteriaSatisfied
)
term_cond = opt._get_termination_condition(-202)
self.assertEqual(term_cond, TerminationCondition.locallyInfeasible)
for opt_status in [-200, -204, -205]:
term_cond = opt._get_termination_condition(opt_status)
self.assertEqual(term_cond, TerminationCondition.provenInfeasible)
for opt_status in [-300, -301]:
term_cond = opt._get_termination_condition(opt_status)
self.assertEqual(term_cond, TerminationCondition.infeasibleOrUnbounded)
for opt_status in [-400, -410]:
term_cond = opt._get_termination_condition(opt_status)
self.assertEqual(term_cond, TerminationCondition.iterationLimit)
for opt_status in [-401, -411]:
term_cond = opt._get_termination_condition(opt_status)
self.assertEqual(term_cond, TerminationCondition.maxTimeLimit)
term_cond = opt._get_termination_condition(-500)
self.assertEqual(term_cond, TerminationCondition.interrupted)
for opt_status in [-501, -550, -599]:
term_cond = opt._get_termination_condition(opt_status)
self.assertEqual(term_cond, TerminationCondition.error)
for opt_status in [-600, -99999, -1]:
term_cond = opt._get_termination_condition(opt_status)
self.assertEqual(term_cond, TerminationCondition.unknown)


@unittest.skipIf(not avail, "KNITRO solver is not available")
class TestKnitroDirectSolver(unittest.TestCase):
Expand Down
Loading