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

Modify PyROS Subproblem Initialization and Solver Call Routines #2515

Merged
merged 26 commits into from
Sep 13, 2022

Conversation

shermanjasonaf
Copy link
Contributor

Summary/Motivation:

Modify subproblem initialization and solver call routines such that:

  • subproblem models are initialized to a feasible point, where possible
  • non-optimal or exceptional solver termination statuses are handled more gracefully
  • solutions returned by the appropriate solver(s) are properly loaded to the appropriate model(s) where appropriate.

Changes proposed in this PR:

General

  • More comprehensive documentation for all subproblem solver call routines
  • alter exception handling for subordinate optimizer solve() calls

Master Feasibility Problem

  • ensure initialized to feasible point
  • if solver does not succesfully converge to at least a feasible solution, load the initial point to the master model

Master Problem

  • ensure initial point provided to each backup solver (if used) is the same, and not the final iterate returned by the previous backup solver

DR Polishing Problem

  • use local subsolver if solve_master_globally=False, and global subsolver otherwise.
  • load second-stage variable and state variable values from polished DR problem (to ensure polished master solution is feasible for the master model, and separation problems may therefore be initialized to a feasible point).
  • ensure polished DR solution is loaded in event of globallyOptimal solver termination (resolve bug)

Separation Problem

  • initialize to feasible point from either the nominal or most recently added master block (depending on the objective focus)
  • ensure initial point provided to each backup solver (if used) is the same, and not final iterate returned by previous backup solver
  • check for PyROS timeout before checking for optimality

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.

@codecov
Copy link

codecov bot commented Aug 29, 2022

Codecov Report

Merging #2515 (ff666c4) into main (4873819) will increase coverage by 0.06%.
The diff coverage is 85.93%.

@@            Coverage Diff             @@
##             main    #2515      +/-   ##
==========================================
+ Coverage   86.54%   86.60%   +0.06%     
==========================================
  Files         715      715              
  Lines       80165    80190      +25     
==========================================
+ Hits        69375    69449      +74     
+ Misses      10790    10741      -49     
Flag Coverage Δ
linux 83.42% <21.87%> (-0.02%) ⬇️
osx 73.75% <21.87%> (-0.01%) ⬇️
other 83.60% <21.87%> (-0.02%) ⬇️
win 80.59% <21.87%> (-0.02%) ⬇️

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

Impacted Files Coverage Δ
pyomo/contrib/pyros/separation_problem_methods.py 86.78% <84.74%> (+2.82%) ⬆️
pyomo/contrib/pyros/master_problem_methods.py 89.79% <86.56%> (+6.28%) ⬆️
pyomo/contrib/pyros/pyros.py 94.24% <100.00%> (+0.52%) ⬆️
pyomo/contrib/pyros/util.py 85.04% <100.00%> (+1.71%) ⬆️
pyomo/opt/plugins/sol.py 84.18% <0.00%> (+3.38%) ⬆️
pyomo/contrib/pyros/pyros_algorithm_methods.py 93.90% <0.00%> (+5.48%) ⬆️

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

Copy link
Member

@jsiirola jsiirola left a comment

Choose a reason for hiding this comment

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

Some minor comments, but nothing that would prevent merging.

pyomo/contrib/pyros/master_problem_methods.py Outdated Show resolved Hide resolved
pyomo/contrib/pyros/master_problem_methods.py Outdated Show resolved Hide resolved
pyomo/contrib/pyros/tests/test_grcs.py Outdated Show resolved Hide resolved
@jsiirola
Copy link
Member

One note: patch coverage is a bit low. Most of the uncovered lines relate to handling solver errors. We should consider adding a test case that exercises (some of) the solver failure cases.

@shermanjasonaf
Copy link
Contributor Author

Can you add @natalieisenberg as a reviewer? I'm still unable to request reviewers.

Copy link
Member

@jsiirola jsiirola left a comment

Choose a reason for hiding this comment

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

Looks good.

Copy link
Contributor

@natalieisenberg natalieisenberg left a comment

Choose a reason for hiding this comment

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

Overall it looks good, I just had one comment/question.

# check: initial point feasible?
for con in sep_model.component_data_objects(Constraint, active=True):
lb, val, ub = value(con.lb), value(con.body), value(con.ub)
lb_viol = val < lb - 1e-5 if lb is not None else False
Copy link
Contributor

Choose a reason for hiding this comment

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

There's a hard-coded tolerance here of 1e-5...I assume that was agreed upon at some point, but should it be made into a global variable somewhere so we don't loose track of it? Like initial_point_feas_tol or something? Also out of curiosity, why 1e-5?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My choice of 1E-5 is somewhat arbitrary; I chose this initially as this is the default absolute constraint violation tolerance for BARON, a solver I use frequently with PyROS. I could make this a global variable (perhaps defined in util.py), or opt for config.robust_feasibility_tolerance instead.

Copy link
Contributor

@natalieisenberg natalieisenberg left a comment

Choose a reason for hiding this comment

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

Looks good!

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.

One question (that might be silly), but otherwise I think this looks good!

'pyros_var_map',
)
setattr(model_data.master_model, varmap_name,
list(model_data.master_model.component_data_objects(Var)))
Copy link
Contributor

Choose a reason for hiding this comment

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

No need to hold up this PR, but in the future, you maybe want to use the get_vars_from_components utility (defined here) in case there are variables used in Constraints or Objectives that are no in the active tree rooted at master_model.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Noted. Should I make this change here? If I understand correctly, master_model has no parent, such that all variables are in the active tree rooted here.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, oops, I may have mislead you. The fact that it has no parent doesn't necessarily mean that all the variables are in the active tree (they could be on deactivated Blocks, for example), but pyros makes master_model, right? So this is actually in your control at this point, I think. I just brought it up because when you initially collect the variables from the user-defined model, you likely want to use get_vars_from_components.

)
for master_dr, polish_dr in dr_var_zip:
for mvar, pvar in zip(master_dr.values(), polish_dr.values()):
mvar.set_value(value(pvar))
Copy link
Contributor

Choose a reason for hiding this comment

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

Should skip_validation=True here? Just noticing since it looks like this is change from before.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Restored skip_validation=True in subsequent commits.

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.

6 participants