Skip to content
Merged
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
18 changes: 18 additions & 0 deletions pyomo/common/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
.. autosummary::

ObjectiveSense
SolverAPIVersion

"""

Expand Down Expand Up @@ -213,5 +214,22 @@ def __str__(self):
return self.name


class SolverAPIVersion(NamedIntEnum):
"""
Enum identifying Pyomo solver API version

The numeric values are intentionally a bit odd because APPSI came
between the official V1 and V2. We still want it to be chronologically
in order without sacrificing the human-logic of v1 vs. v2.
"""

#: Original Coopr/Pyomo solver interface
V1 = 10
#: Automatic Persistent Pyomo Solver Interface (experimental)
APPSI = 15
#: Redesigned solver interface (circa 2024)
V2 = 20


minimize = ObjectiveSense.minimize
maximize = ObjectiveSense.maximize
24 changes: 23 additions & 1 deletion pyomo/common/tests/test_enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

import pyomo.common.unittest as unittest

from pyomo.common.enums import ExtendedEnumType, ObjectiveSense
from pyomo.common.enums import ExtendedEnumType, ObjectiveSense, SolverAPIVersion


class ProblemSense(enum.IntEnum, metaclass=ExtendedEnumType):
Expand Down Expand Up @@ -95,3 +95,25 @@ def test_call(self):
def test_str(self):
self.assertEqual(str(ObjectiveSense.minimize), 'minimize')
self.assertEqual(str(ObjectiveSense.maximize), 'maximize')


class TestSolverAPIVersion(unittest.TestCase):
def test_members(self):
self.assertEqual(
list(SolverAPIVersion),
[SolverAPIVersion.V1, SolverAPIVersion.APPSI, SolverAPIVersion.V2],
)

def test_call(self):
self.assertIs(SolverAPIVersion(10), SolverAPIVersion.V1)
self.assertIs(SolverAPIVersion(15), SolverAPIVersion.APPSI)
self.assertIs(SolverAPIVersion(20), SolverAPIVersion.V2)

self.assertIs(SolverAPIVersion('V1'), SolverAPIVersion.V1)
self.assertIs(SolverAPIVersion('APPSI'), SolverAPIVersion.APPSI)
self.assertIs(SolverAPIVersion('V2'), SolverAPIVersion.V2)

with self.assertRaisesRegex(
ValueError, "'foo' is not a valid SolverAPIVersion"
):
SolverAPIVersion('foo')
14 changes: 13 additions & 1 deletion pyomo/contrib/appsi/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

from pyomo.common.config import ConfigDict, ConfigValue, NonNegativeFloat
from pyomo.common.errors import ApplicationError
from pyomo.common.enums import IntEnum
from pyomo.common.enums import IntEnum, SolverAPIVersion
from pyomo.common.factory import Factory
from pyomo.common.timing import HierarchicalTimer
from pyomo.core.base.constraint import ConstraintData, Constraint
Expand Down Expand Up @@ -635,6 +635,18 @@ def __str__(self):
# preserve the previous behavior
return self.name

@classmethod
def api_version(self):
"""
Return the public API supported by this interface.

Returns
-------
~pyomo.common.enums.SolverAPIVersion
A solver API enum object
"""
return SolverAPIVersion.APPSI

@abc.abstractmethod
def solve(self, model: BlockData, timer: HierarchicalTimer = None) -> Results:
"""
Expand Down
14 changes: 13 additions & 1 deletion pyomo/contrib/solver/common/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from pyomo.core.base.block import BlockData
from pyomo.core.base.objective import Objective, ObjectiveData
from pyomo.common.config import ConfigValue
from pyomo.common.enums import IntEnum
from pyomo.common.enums import IntEnum, SolverAPIVersion
from pyomo.common.errors import ApplicationError
from pyomo.common.deprecation import deprecation_warning
from pyomo.common.modeling import NOTSET
Expand Down Expand Up @@ -105,6 +105,18 @@ def __enter__(self):
def __exit__(self, exc_type, exc_value, exc_traceback):
"""Exit statement - enables `with` statements."""

@classmethod
def api_version(self):
"""
Return the public API supported by this interface.

Returns
-------
~pyomo.common.enums.SolverAPIVersion
A solver API enum object
"""
return SolverAPIVersion.V2

def solve(self, model: BlockData, **kwargs) -> Results:
"""Solve a Pyomo model.

Expand Down
1 change: 1 addition & 0 deletions pyomo/contrib/solver/tests/solvers/test_ipopt.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ def test_class_member_list(self):
expected_list = [
'CONFIG',
'config',
'api_version',
'available',
'has_linear_solver',
'is_persistent',
Expand Down
13 changes: 12 additions & 1 deletion pyomo/contrib/solver/tests/unit/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

from pyomo.common import unittest
from pyomo.common.config import ConfigDict
from pyomo.common.enums import SolverAPIVersion
from pyomo.contrib.solver.common import base


Expand All @@ -22,7 +23,14 @@ class _LegacyWrappedSolverBase(base.LegacySolverWrapper, base.SolverBase):

class TestSolverBase(unittest.TestCase):
def test_class_method_list(self):
expected_list = ['CONFIG', 'available', 'is_persistent', 'solve', 'version']
expected_list = [
'CONFIG',
'api_version',
'available',
'is_persistent',
'solve',
'version',
]
method_list = [
method for method in dir(base.SolverBase) if method.startswith('_') is False
]
Expand All @@ -32,6 +40,7 @@ def test_init(self):
instance = base.SolverBase()
self.assertFalse(instance.is_persistent())
self.assertEqual(instance.name, 'solverbase')
self.assertEqual(instance.api_version().name, 'V2')
self.assertEqual(instance.CONFIG, instance.config)
with self.assertRaises(NotImplementedError):
self.assertEqual(instance.version(), None)
Expand Down Expand Up @@ -67,6 +76,7 @@ def test_class_method_list(self):
'add_constraints',
'add_parameters',
'add_variables',
'api_version',
'available',
'is_persistent',
'remove_block',
Expand All @@ -90,6 +100,7 @@ def test_class_method_list(self):
def test_init(self):
instance = base.PersistentSolverBase()
self.assertTrue(instance.is_persistent())
self.assertEqual(instance.api_version(), SolverAPIVersion.V2)
with self.assertRaises(NotImplementedError):
self.assertEqual(instance.set_instance(None), None)
with self.assertRaises(NotImplementedError):
Expand Down
25 changes: 25 additions & 0 deletions pyomo/opt/base/solvers.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import shlex

from pyomo.common import Factory
from pyomo.common.enums import SolverAPIVersion
from pyomo.common.errors import ApplicationError
from pyomo.common.collections import Bunch

Expand Down Expand Up @@ -80,6 +81,18 @@ def __enter__(self):
def __exit__(self, t, v, traceback):
pass

@classmethod
def api_version(self):
"""
Return the public API supported by this interface.

Returns
-------
~pyomo.common.enums.SolverAPIVersion
A solver API enum object
"""
return SolverAPIVersion.V1

def available(self, exception_flag=True):
"""Determine if this optimizer is available."""
if exception_flag:
Expand Down Expand Up @@ -249,6 +262,18 @@ def __enter__(self):
def __exit__(self, t, v, traceback):
pass

@classmethod
def api_version(self):
"""
Return the public API supported by this interface.

Returns
-------
~pyomo.common.enums.SolverAPIVersion
A solver API enum object
"""
return SolverAPIVersion.V1

#
# Adding to help track down invalid code after making
# the following attributes private
Expand Down
Loading