|
36 | 36 | from pyomo.contrib.pyros.uncertainty_sets import Geometry
|
37 | 37 | from pyomo.contrib.pyros.util import (
|
38 | 38 | ABS_CON_CHECK_FEAS_TOL,
|
| 39 | + BYPASSING_SEPARATION_PRIORITY, |
39 | 40 | call_solver,
|
40 | 41 | check_time_limit_reached,
|
41 | 42 | )
|
@@ -386,21 +387,50 @@ def group_ss_ineq_constraints_by_priority(separation_data):
|
386 | 387 | Keys are sorted in descending order
|
387 | 388 | (i.e. highest priority first).
|
388 | 389 | """
|
| 390 | + separation_data.config.progress_logger.debug( |
| 391 | + "Grouping second-stage inequality constraints by separation priority..." |
| 392 | + ) |
| 393 | + |
389 | 394 | ss_ineq_cons = separation_data.separation_model.second_stage.inequality_cons
|
390 | 395 | separation_priority_groups = dict()
|
391 | 396 | for name, ss_ineq_con in ss_ineq_cons.items():
|
392 |
| - # by default, priority set to 0 |
393 | 397 | priority = separation_data.separation_priority_order[name]
|
394 | 398 | cons_with_same_priority = separation_priority_groups.setdefault(priority, [])
|
395 | 399 | cons_with_same_priority.append(ss_ineq_con)
|
396 | 400 |
|
397 | 401 | # sort separation priority groups
|
398 |
| - return { |
| 402 | + numeric_priority_grp_items = [ |
| 403 | + (priority, cons) |
| 404 | + for priority, cons in separation_priority_groups.items() |
| 405 | + if priority is not BYPASSING_SEPARATION_PRIORITY |
| 406 | + ] |
| 407 | + sorted_priority_groups = { |
399 | 408 | priority: ss_ineq_cons
|
400 |
| - for priority, ss_ineq_cons in sorted( |
401 |
| - separation_priority_groups.items(), reverse=True |
402 |
| - ) |
| 409 | + for priority, ss_ineq_cons in sorted(numeric_priority_grp_items, reverse=True) |
403 | 410 | }
|
| 411 | + if BYPASSING_SEPARATION_PRIORITY in separation_priority_groups: |
| 412 | + sorted_priority_groups[BYPASSING_SEPARATION_PRIORITY] = ( |
| 413 | + separation_priority_groups[BYPASSING_SEPARATION_PRIORITY] |
| 414 | + ) |
| 415 | + |
| 416 | + num_priority_groups = len(sorted_priority_groups) |
| 417 | + separation_data.config.progress_logger.debug( |
| 418 | + f"Found {num_priority_groups} separation " |
| 419 | + f"priority group{'s' if num_priority_groups != 1 else ''}." |
| 420 | + ) |
| 421 | + separation_data.config.progress_logger.debug( |
| 422 | + "Separation priority grouping statistics:" |
| 423 | + ) |
| 424 | + separation_data.config.progress_logger.debug( |
| 425 | + f" {'Priority':20s}{'# Ineq Cons':15s}" |
| 426 | + ) |
| 427 | + for priority, cons in sorted_priority_groups.items(): |
| 428 | + priority_str = str(priority) + (" (bypass)" if priority is None else "") |
| 429 | + separation_data.config.progress_logger.debug( |
| 430 | + f" {priority_str:20s}{len(cons):<15d}" |
| 431 | + ) |
| 432 | + |
| 433 | + return sorted_priority_groups |
404 | 434 |
|
405 | 435 |
|
406 | 436 | def get_worst_discrete_separation_solution(
|
@@ -566,7 +596,7 @@ def perform_separation_loop(separation_data, master_data, solve_globally):
|
566 | 596 | master_data=master_data,
|
567 | 597 | ss_ineq_cons=all_ss_ineq_constraints,
|
568 | 598 | )
|
569 |
| - sorted_priority_groups = group_ss_ineq_constraints_by_priority(separation_data) |
| 599 | + sorted_priority_groups = separation_data.separation_priority_groups |
570 | 600 | uncertainty_set_is_discrete = (
|
571 | 601 | config.uncertainty_set.geometry == Geometry.DISCRETE_SCENARIOS
|
572 | 602 | )
|
@@ -628,11 +658,24 @@ def perform_separation_loop(separation_data, master_data, solve_globally):
|
628 | 658 |
|
629 | 659 | all_solve_call_results = ComponentMap()
|
630 | 660 | priority_groups_enum = enumerate(sorted_priority_groups.items())
|
| 661 | + solve_desc = "global" if solve_globally else "local" |
| 662 | + solve_adverb = "Globally" if solve_globally else "Locally" |
631 | 663 | for group_idx, (priority, ss_ineq_constraints) in priority_groups_enum:
|
632 | 664 | priority_group_solve_call_results = ComponentMap()
|
| 665 | + |
| 666 | + if priority is BYPASSING_SEPARATION_PRIORITY: |
| 667 | + config.progress_logger.debug( |
| 668 | + f"Bypassing {solve_desc} separation of all " |
| 669 | + f"{len(ss_ineq_constraints)} second-stage " |
| 670 | + f"inequality constraints in the group with priority {priority} " |
| 671 | + f"(group {group_idx + 1} of {len(sorted_priority_groups)}) " |
| 672 | + f"as the priority value is {priority}." |
| 673 | + ) |
| 674 | + worst_case_ss_ineq_con = None |
| 675 | + continue |
| 676 | + |
633 | 677 | for idx, ss_ineq_con in enumerate(ss_ineq_constraints):
|
634 | 678 | # log progress of separation loop
|
635 |
| - solve_adverb = "Globally" if solve_globally else "Locally" |
636 | 679 | config.progress_logger.debug(
|
637 | 680 | f"{solve_adverb} separating second-stage inequality constraint "
|
638 | 681 | f"{get_con_name_repr(separation_data.separation_model, ss_ineq_con)} "
|
@@ -1293,6 +1336,12 @@ def __init__(self, model_data):
|
1293 | 1336 | else:
|
1294 | 1337 | self.idxs_of_master_scenarios = None
|
1295 | 1338 |
|
| 1339 | + self.separation_priority_groups = group_ss_ineq_constraints_by_priority(self) |
| 1340 | + |
| 1341 | + @property |
| 1342 | + def ss_ineq_cons_to_bypass(self): |
| 1343 | + return self.separation_priority_groups.get(BYPASSING_SEPARATION_PRIORITY, []) |
| 1344 | + |
1296 | 1345 | def solve_separation(self, master_data):
|
1297 | 1346 | """
|
1298 | 1347 | Solve the separation problem.
|
|
0 commit comments