Skip to content

[Depends on #3991] Adding method for multistart initialization of nonlinear models. - #3994

Draft
stephenscini wants to merge 45 commits into
Pyomo:mainfrom
stephenscini:multistart-init
Draft

[Depends on #3991] Adding method for multistart initialization of nonlinear models.#3994
stephenscini wants to merge 45 commits into
Pyomo:mainfrom
stephenscini:multistart-init

Conversation

@stephenscini

@stephenscini stephenscini commented Jul 13, 2026

Copy link
Copy Markdown
Member

Fixes # .

Summary/Motivation:

Additional method in devel/initialization to use multistart optimization as a way for initializing nonliner models.

Changes proposed in this PR:

  • Updating contrib/multistart solver for reproducibility, and adding support for new solver interface.
  • Adding support for uniform, lhs, and sobol sampling within the multistart solver
  • Adding method to devel/initialization

AI-Use Disclosure

  • AI tools contributed to the development of this PR

    • AI tools generated code (apart from tests)

    Review process (select ONE):

    • Rewritten: All AI-generated content was rewritten by me before being committed.

Notes for reviewers (optional):

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.

@stephenscini

Copy link
Copy Markdown
Member Author

Opening PR, but still in active development. Dependent on #3991 first as it includes those changes.

@stephenscini stephenscini changed the title Adding method for multistart initialization of nonlinear models. [Depends on #3991] Adding method for multistart initialization of nonlinear models. Jul 21, 2026
@stephenscini

stephenscini commented Jul 21, 2026

Copy link
Copy Markdown
Member Author

Note for 7/21 Dev meeting

Method implemented with support for new solver interface. All original tests passing locally.

Tasks before ready for review:

  • It looks like there is an issue with changing the SolverFactory version in a different place, which I will address.
  • Try and simplify interface, check with mentors.
  • Add tests for new functionality

@stephenscini stephenscini left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Went through and added some questions in the code for design meeting.

from pyomo.core.expr.compare import assertExpressionsEqual
from pyomo.core.expr.numeric_expr import LinearExpression

from pyomo.contrib.multistart.multi import MultiStart

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I received an error message when I added the solver to the new SolverFactory that it needed to be in this list. This would mean it is tested with the other solvers from contrib/solver and I am not sure that if that is expected? If so, will all contrib solvers need to eventually be switched over or should the opt/solvers still be the best location.

In this case, I wanted to update it to use these solvers so thought it would also go here. Maybe Legacy?


def rand(val, lb, ub):
return random.uniform(lb, ub) # uniform distribution between lb and ub
def rand(val, lb, ub, rng):

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Previously, there was not a set way I could find to make the solver result reproducible, so I made the default random generator np.random with the current RNG interface.

I wanted to otherwise leave the established strategies alone, but could also make all random strategies support the different random generator options? Kept all newer options under rand_vector to use a vectorized approach.


if config.strategy == "rand_vector":

if len(eligible_vars) == 0:

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Before, if all the variables are unbounded, it would not reset them but just give the warning. I thought it would be better to error out.

logger = logging.getLogger('pyomo.contrib.multistart')


@document_configdict()

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Made changes so it is now loaded as a solver for the new SolverFactory since I wanted to use the new solver interfaces like the other devel/initialization methods, and from discussion with Bethany. Causing testing errors but would like to discuss best way to do this if other method preferred.

def version(self):
"""Get solver version
TODO: This is a solver wrapper, unsure how to define version in this case."""

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Currently no versions for this. If old was v0.0, would this be v0.1?

""",
),
)
self.solver = self.declare(

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Should I make this have different solver acceptances like MindtPy where there are supported categories, or make it so it accepts the solver object? How consistent should the contrib solvers that accept other solvers be with handling internal solver?

visibility=visibility,
)

self.strategy = self.declare(

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Could/should this also support a user supplying a list of initial points? Thinking of obj_at_theta in parmest. Goal in my head is to make as generalized as possible.

raise RuntimeError(
"Multistart solver is unable to handle model with multiple active objectives."
)
if obj is None:

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Previously it was restricted from using square models or constant objectives in the multistart solver. Is there a valid reason for this or can it be removed? Is there a case where a model would have no objective or would that cause a different error? Removing would make more sense in my opinion.

description="Tolerance on HCS objective value equality. Defaults to Python float equality precision.",
),
)
self.break_on_solution = self.declare(

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

For initialization, I thought this made sense, other thought would be implementing a SolutionLimit argument like Gurobi.

)
# If we are looking for the first feasible solution, then return immediately
if config.break_on_solution:
return best_result

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Would it be a good idea to have this return a dictionary or something instead of just the best result if someone wanted to inspect? Or is this better for a user to set up? Should a user be able to access subsolver information too?

@stephenscini

Copy link
Copy Markdown
Member Author

Had discussion with Bethany and Michael. Main takeaway points:

  • Invest to make this work for newer SolverFactory. Do not keep in opt.SolverFactory.
  • Try and resolve all test failures with the new SolverFactory as default. More discussion needed on expectations for contrib solvers as they transition to new SolverFactory.
  • If all results are infeasible/not optimal, have it throw the 'NoOptimalSolution' error.
    -Keep this PR focused on a minimum working version that has key functionality we want. Can always add additional touches later.
  • LHS makes sense to be default for all methods/strategies, and should be looped in to the old strategies too.
  • Availability of multistart should depend on availablility of chosen sub-solver (default is ipopt)
  • Version 0.1.0 makes sense for this new iteration.
  • Subsolver has its own configuration, but still needs to comply with 'meta-solver' configurations. If subsolver timelimit is 1 minute for 50 runs, but metasolver is 10 minutes, when metasolver time runs out the methods stop.

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.

1 participant