Skip to content

Conversation

@whart222
Copy link
Member

@whart222 whart222 commented Jul 9, 2025

Fixes N/A (partially addresses #3513) .

Summary/Motivation:

The alternative_solutions contrib package includes a rudimentary solution pool object. This PR includes a more comprehensive capability for defining and managing solution pools.

Changes proposed in this PR:

  • PyomoPoolManager - Manages pools for Pyomo solutions
  • SolutionPool* - Classes used to define various solution pools
  • Functions in pyomo.contrib.alternative_solutions now use and return PyomoPoolManager instances.
  • PyomoSolution - Function that returns a Solution object given Pyomo model data

NOTE: This is a WIP PR. I'm submitting this now to help define expectations for finalizing this new capability.

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.

@whart222 whart222 requested review from emma58 and michaelbynum July 9, 2025 09:15
@whart222 whart222 marked this pull request as draft July 9, 2025 09:16
whart222 added 3 commits July 9, 2025 05:19
Use the Pyomo Bunch class as an alias for Munch, to avoid
introducing an additional Pyomo dependency.
@whart222
Copy link
Member Author

whart222 commented Jul 9, 2025

Note that this PR now includes updates to the Bunch class defined in pyomo.common. I have been using the public Munch class, but these revisions align the Bunch API with Munch.

whart222 added 10 commits July 9, 2025 06:38
Avoiding use of KW_ONLY, which is an internal mechanism
Using new serialization API, which is simpler. :)
1. Reworking solver matrix logic

2. Fixing test to benchmark against the solution values
@codecov
Copy link

codecov bot commented Jul 10, 2025

Codecov Report

❌ Patch coverage is 91.37577% with 42 lines in your changes missing coverage. Please review.
✅ Project coverage is 87.20%. Comparing base (e173cb5) to head (0d79e42).

Files with missing lines Patch % Lines
pyomo/contrib/alternative_solutions/solnpool.py 90.19% 25 Missing ⚠️
pyomo/contrib/alternative_solutions/solution.py 94.23% 6 Missing ⚠️
...o/contrib/alternative_solutions/gurobi_solnpool.py 90.47% 4 Missing ⚠️
pyomo/contrib/alternative_solutions/balas.py 78.57% 3 Missing ⚠️
pyomo/common/collections/bunch.py 95.23% 1 Missing ⚠️
pyomo/contrib/alternative_solutions/aos_utils.py 93.33% 1 Missing ⚠️
...mo/contrib/alternative_solutions/gurobi_lp_enum.py 92.85% 1 Missing ⚠️
pyomo/contrib/alternative_solutions/lp_enum.py 90.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3657      +/-   ##
==========================================
- Coverage   89.31%   87.20%   -2.12%     
==========================================
  Files         896      826      -70     
  Lines      103682   102378    -1304     
==========================================
- Hits        92605    89280    -3325     
- Misses      11077    13098    +2021     
Flag Coverage Δ
builders ?
linux 86.97% <91.37%> (-2.08%) ⬇️
linux_other 86.97% <91.37%> (+0.01%) ⬆️
osx 83.12% <91.37%> (+0.02%) ⬆️
win 85.23% <91.37%> (+0.01%) ⬆️
win_other 85.23% <91.37%> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Added non-positive error check and value 1 warning for num_solutions in balas
Added num_solution error if num_solutions is non-positive, warning if num_solutions =1
Copy link
Contributor

@emma58 emma58 left a comment

Choose a reason for hiding this comment

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

Sorry for the huuuuge number of comments, but there's a lot here! Many are minor, but there are a few design questions too: The biggest one is that I'm wondering if the VariableInfo and ObjectiveInfo classes could store a reference to the original pyomo component rather than copying a lot but not all of the same data. It seems like that would make it much easier to map solutions in the pool to the original model, which I imagine will be a common user need.

@whart222
Copy link
Member Author

whart222 commented Nov 6, 2025

@emma58 Thanks for all the feedback! I've gone through some of your comments, but I'll get back to this sometime next week.

@blnicho blnicho moved this from Todo to Review In Progress in Pyomo 6.10 Nov 11, 2025
@@ -0,0 +1,130 @@
# ___________________________________________________________________________
Copy link
Member

Choose a reason for hiding this comment

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

This module name is misleading: I expected this to define a derived SolutionPool class that was specific to the gurobipy interface.

Copy link
Member Author

Choose a reason for hiding this comment

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

What would you suggest? Something like gurobi_with_native_solution_pool.py seems verbose.

Comment on lines +104 to +107
objective : None or :py:class:`ObjectiveInfo`
A :py:class:`ObjectiveInfo` object. (default is None)
objectives : None or list
A list of :py:class:`ObjectiveInfo` objects. (default is None)
Copy link
Member

Choose a reason for hiding this comment

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

I find the duplicative API confusing. It would be simpler and less error-prone for the __init__ to take a single argument, and then in the constructor map a single ObjectiveData to a list of length 1 (you can also then accept an IndexedObjective and map it to a list of Objectives!)

(To the point about "error prone": note that the current implementation for processing the "2 argument solution" has logic errors and will silently drop objectives if both objective= and objectives= are specified.)

Comment on lines +36 to +53
@dataclasses.dataclass(**dataclass_kwargs)
class VariableInfo:
"""
A class to store solutions from a Pyomo model.
Represents a variable in a solution.
Attributes
----------
variables : ComponentMap
A map between Pyomo variables and their values for a solution.
fixed_vars : ComponentSet
The set of Pyomo variables that are fixed in a solution.
objective : ComponentMap
A map between Pyomo objectives and their values for a solution.
Methods
-------
pprint():
Prints a solution.
get_variable_name_values(self, ignore_fixed_vars=False):
Get a dictionary of variable name-variable value pairs.
get_fixed_variable_names(self):
Get a list of fixed-variable names.
get_objective_name_values(self):
Get a dictionary of objective name-objective value pairs.
value : float
The value of the variable.
fixed : bool
If True, then the variable was fixed during optimization.
name : str
The name of the variable.
index : int
The unique identifier for this variable.
discrete : bool
If True, then this is a discrete variable
suffix : dict
Copy link
Member

Choose a reason for hiding this comment

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

Are there any applications using a Solution (Pool) from Pyomo that are making use of the feature that the pool is disconnected from Pyomo? This seems like a design decision that is adding a lot of complexity and potential for logic errors for a use case that doesn't exist?

For example, you are re-inventing solutions for caching Pyomo objects that have already been well-defined and worked out / debugged elsewhere (e.g., pyomo.common.collections; pyomo.contrib.solver).

Further, the dev team has been spending a significant amount of effort to remove the use of string names from other parts of Pyomo (e.g., parmest), so going and adding a new API that is string-based seems to run counter to that effort.

@whart222 whart222 requested a review from mrmundt November 20, 2025 16:15
@whart222
Copy link
Member Author

It's not obvious to me why the linux slim tests are failing. Do we need to conditionally import numpy?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Review In Progress

Development

Successfully merging this pull request may close these issues.

6 participants