Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Solver Refactor Pt. 2: Restructure for Future Redesign #3476

Open
wants to merge 55 commits into
base: main
Choose a base branch
from

Conversation

mrmundt
Copy link
Contributor

@mrmundt mrmundt commented Feb 18, 2025

Fixes #1030 (doesn't actually fix it; just the next step)

Summary/Motivation:

This is meant to be a "mid-point" for the ongoing solver refactor. It is currently working in this form, so this PR is intended to merge changes up to now so we can start making more fine-tuned changes that are easier to track.

Changes proposed in this PR:

  • Introduce highs
  • Restructure the directory tree
  • Rework some tests for clarity
  • Reduce duplicate code
  • Apply static analysis suggestions (pylint)

Legal Acknowledgement

By contributing to this software project, I have read the contribution guide and agree to the following terms and conditions for my contribution:

  1. I agree my contributions are submitted under the BSD license.
  2. I represent I am authorized to make the contributions and grant the license. If my employer has rights to intellectual property that includes these contributions, I represent that I have received permission to make contributions and grant the required license on behalf of that employer.

Comment on lines +58 to +62
def __format__(self, format_spec):
return format(self.name, format_spec)

def __str__(self):
return self.name
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I saw this, I thought, "I'm surprised we need this". Then I looked at the version that was removed from within SolverBase and saw that we dropped some useful comments. Why not port those comments? Also, I still find it odd that we need to override __str__ and __format__. Should we use a different type of enum?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, we need IntEnum for __bool__...

Copy link
Contributor

@michaelbynum michaelbynum left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm only about halfway done, but here are some minor comments.

:class:`BranchAndBoundConfig<pyomo.contrib.solver.config.BranchAndBoundConfig>`,
:class:`PersistentSolverConfig<pyomo.contrib.solver.config.PersistentSolverConfig>`, or
:class:`PersistentBranchAndBoundConfig<pyomo.contrib.solver.config.PersistentBranchAndBoundConfig>`.
Additionally, solvers should have a :attr:`CONFIG<SolverBase.CONFIG>` attribute that
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we specify that this is a class attribute rather than an instance attribute?

Comment on lines -135 to -146
Nominally, this will return True if the solver interface is
valid and can be used to solve problems and False if it cannot.

Note that for licensed solvers there are a number of "levels" of
available: depending on the license, the solver may be available
with limitations on problem size or runtime (e.g., 'demo'
vs. 'community' vs. 'full'). In these cases, the solver may
return a subclass of enum.IntEnum, with members that resolve to
True if the solver is available (possibly with limitations).
The Enum may also have multiple members that all resolve to
False indicating the reason why the interface is not available
(not found, bad license, unsupported version, etc).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these are useful comments? Is this captured in the documentation? If so, I 'm fine with removing it.

Comment on lines -152 to -154
Note that the enum can be cast to bool, which will
be True if the solver is runable at all and False
otherwise.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.


def __init__(self, **kwds) -> None:
super().__init__(**kwds)
self._active_config = self.config
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is also set in PersistentSolverUtils. I'm not sure where it makes the most sense to keep it.

Comment on lines -199 to +197
def _load_vars(self, vars_to_load: Optional[Sequence[VarData]] = None) -> NoReturn:
def _load_vars(self, vars_to_load: Optional[Sequence[VarData]] = None) -> None:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a difference between these two? Just curious - it does not really matter.

@@ -10,28 +10,153 @@
# ___________________________________________________________________________


Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this file go in the common directory?

Comment on lines -99 to +120
raise RuntimeError(
'Solver does not currently have a valid solution. Please '
'check the termination condition.'
)
raise NoSolutionError()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love these changes.


class GurobiDirect(GurobiSolverMixin, SolverBase):
"""
Interface to Gurobi direct (not persistent)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would change this to something like "Interface to Gurobi using gurobipy"

Comment on lines -309 to +286
Gurobi._num_instances -= 1
if Gurobi._num_instances == 0:
GurobiPersistent._num_instances -= 1
if GurobiPersistent._num_instances == 0:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should _num_instances live on the mixin class and be shared by direct and persistent???

Comment on lines +630 to +631
# NOTE: This seems like an unnecessary local variable because it's
# never used or called again. But when removed, it causes KeyErrors.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haha! The line self._symbol_map.removeSymbol(con) in _remove_sos_constraints has to be removed with it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[PEP] Redesign of Pyomo Solvers
3 participants