@@ -423,8 +423,13 @@ def get_worst_discrete_separation_solution(
423
423
# violation of specified second-stage inequality
424
424
# constraint by separation
425
425
# problem solutions for all scenarios
426
+ # scenarios with subsolver errors are replaced with nan
426
427
violations_of_ss_ineq_con = [
427
- solve_call_res .scaled_violations [ss_ineq_con ]
428
+ (
429
+ solve_call_res .scaled_violations [ss_ineq_con ]
430
+ if not solve_call_res .subsolver_error
431
+ else np .nan
432
+ )
428
433
for solve_call_res in discrete_solve_results .solver_call_results .values ()
429
434
]
430
435
@@ -433,9 +438,9 @@ def get_worst_discrete_separation_solution(
433
438
# determine separation solution for which scaled violation of this
434
439
# second-stage inequality constraint is the worst
435
440
worst_case_res = discrete_solve_results .solver_call_results [
436
- list_of_scenario_idxs [np .argmax (violations_of_ss_ineq_con )]
441
+ list_of_scenario_idxs [np .nanargmax (violations_of_ss_ineq_con )]
437
442
]
438
- worst_case_violation = np .max (violations_of_ss_ineq_con )
443
+ worst_case_violation = np .nanmax (violations_of_ss_ineq_con )
439
444
assert worst_case_violation in worst_case_res .scaled_violations .values ()
440
445
441
446
# evaluate violations for specified second-stage inequality constraints
@@ -463,6 +468,13 @@ def get_worst_discrete_separation_solution(
463
468
else :
464
469
results_list = []
465
470
471
+ # check if there were any failed scenarios for subsolver_error
472
+ # if there are failed scenarios, subsolver error triggers for all ineq
473
+ if any (np .isnan (violations_of_ss_ineq_con )):
474
+ subsolver_error_flag = True
475
+ else :
476
+ subsolver_error_flag = False
477
+
466
478
return SeparationSolveCallResults (
467
479
solved_globally = worst_case_res .solved_globally ,
468
480
results_list = results_list ,
@@ -471,7 +483,7 @@ def get_worst_discrete_separation_solution(
471
483
variable_values = worst_case_res .variable_values ,
472
484
found_violation = (worst_case_violation > config .robust_feasibility_tolerance ),
473
485
time_out = False ,
474
- subsolver_error = False ,
486
+ subsolver_error = subsolver_error_flag ,
475
487
discrete_set_scenario_index = worst_case_res .discrete_set_scenario_index ,
476
488
)
477
489
@@ -642,9 +654,7 @@ def perform_separation_loop(separation_data, master_data, solve_globally):
642
654
643
655
priority_group_solve_call_results [ss_ineq_con ] = solve_call_results
644
656
645
- termination_not_ok = (
646
- solve_call_results .time_out or solve_call_results .subsolver_error
647
- )
657
+ termination_not_ok = solve_call_results .time_out
648
658
if termination_not_ok :
649
659
all_solve_call_results .update (priority_group_solve_call_results )
650
660
return SeparationLoopResults (
@@ -653,6 +663,14 @@ def perform_separation_loop(separation_data, master_data, solve_globally):
653
663
worst_case_ss_ineq_con = None ,
654
664
)
655
665
666
+ # provide message that PyROS will attempt to find a violation and move
667
+ # to the next iteration even after subsolver error
668
+ if solve_call_results .subsolver_error :
669
+ config .progress_logger .warning (
670
+ "PyROS is attempting to recover and will continue to "
671
+ "the next iteration if a constraint violation is found."
672
+ )
673
+
656
674
all_solve_call_results .update (priority_group_solve_call_results )
657
675
658
676
# there may be multiple separation problem solutions
@@ -1139,13 +1157,19 @@ def discrete_solve(
1139
1157
]
1140
1158
1141
1159
solve_call_results_dict = {}
1142
- for scenario_idx in scenario_idxs_to_separate :
1160
+ for idx , scenario_idx in enumerate ( scenario_idxs_to_separate ) :
1143
1161
# fix uncertain parameters to scenario value
1144
1162
# hence, no need to activate uncertainty set constraints
1145
1163
scenario = config .uncertainty_set .scenarios [scenario_idx ]
1146
1164
for param , coord_val in zip (uncertain_param_vars , scenario ):
1147
1165
param .fix (coord_val )
1148
1166
1167
+ # debug statement for solving square problem for each scenario
1168
+ config .progress_logger .debug (
1169
+ f"Attempting to solve square problem for discrete scenario { scenario } "
1170
+ f", { idx + 1 } of { len (scenario_idxs_to_separate )} total"
1171
+ )
1172
+
1149
1173
# obtain separation problem solution
1150
1174
solve_call_results = solver_call_separation (
1151
1175
separation_data = separation_data ,
@@ -1158,12 +1182,17 @@ def discrete_solve(
1158
1182
solve_call_results_dict [scenario_idx ] = solve_call_results
1159
1183
1160
1184
# halt at first encounter of unacceptable termination
1161
- termination_not_ok = (
1162
- solve_call_results .subsolver_error or solve_call_results .time_out
1163
- )
1185
+ termination_not_ok = solve_call_results .time_out
1164
1186
if termination_not_ok :
1165
1187
break
1166
1188
1189
+ # report any subsolver errors, but continue
1190
+ if solve_call_results .subsolver_error :
1191
+ config .progress_logger .warning (
1192
+ f"All solvers failed to solve discrete scenario { scenario_idx } : "
1193
+ f"{ scenario } "
1194
+ )
1195
+
1167
1196
return DiscreteSeparationSolveCallResults (
1168
1197
solved_globally = solve_globally ,
1169
1198
solver_call_results = solve_call_results_dict ,
0 commit comments