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
@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?

@blnicho
Copy link
Member

blnicho commented Nov 25, 2025

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

@whart222 yes, numpy needs to be treated as an optional dependency so tests relying on numpy need to be skipped when it's not available.

skipif needs to come *after* the parameterize decorator
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