Skip to content

Commit 7c8423e

Browse files
committed
being more careful about reporting iteration 0 bounds
1 parent 98c2a07 commit 7c8423e

File tree

7 files changed

+41
-38
lines changed

7 files changed

+41
-38
lines changed

Diff for: mpisppy/cylinders/hub.py

+2-9
Original file line numberDiff line numberDiff line change
@@ -470,11 +470,6 @@ def setup_hub(self):
470470
"Cannot call setup_hub before memory windows are constructed"
471471
)
472472

473-
# attribute to set False if some extension
474-
# modified the iteration 0 subproblems such
475-
# that the trivial bound is no longer valid
476-
self.use_trivial_bound = True
477-
478473
self.initialize_spoke_indices()
479474
self.initialize_bound_values()
480475

@@ -536,10 +531,8 @@ def sync_with_spokes(self):
536531
self.sync()
537532

538533
def is_converged(self):
539-
## might as well get a bound, in this case
540-
if self.opt._PHIter == 1 and self.use_trivial_bound:
541-
self.BestOuterBound = self.OuterBoundUpdate(self.opt.trivial_bound)
542-
self.BestOuterBound = self.OuterBoundUpdate(self.opt.best_bound_obj_val)
534+
if self.opt.best_bound_obj_val is not None:
535+
self.BestOuterBound = self.OuterBoundUpdate(self.opt.best_bound_obj_val)
543536

544537
if not self.has_innerbound_spokes:
545538
if self.opt._PHIter == 1:

Diff for: mpisppy/extensions/reduced_costs_fixer.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ def iter0_post_solver_creation(self):
9090
while not self.opt.spcomm.hub_from_spoke(self.opt.spcomm.outerbound_receive_buffers[self.reduced_costs_spoke_index], self.reduced_costs_spoke_index):
9191
continue
9292
self.sync_with_spokes(pre_iter0 = True)
93-
self.opt.spcomm.use_trivial_bound = False
9493
self.fix_fraction_target = self._fix_fraction_target_iter0
9594

9695
def post_iter0_after_sync(self):
@@ -109,11 +108,14 @@ def sync_with_spokes(self, pre_iter0 = False):
109108
self._last_serial_number = serial_number
110109
reduced_costs = spcomm.outerbound_receive_buffers[idx][1:1+self.nonant_length]
111110
this_outer_bound = spcomm.outerbound_receive_buffers[idx][0]
112-
new_outer_bound = self._update_best_outer_bound(this_outer_bound)
111+
is_new_outer_bound = self._update_best_outer_bound(this_outer_bound)
112+
if pre_iter0:
113+
# make sure we set the bound we compute prior to iteration 0
114+
self.opt.spcomm.BestOuterBound = self.opt.spcomm.OuterBoundUpdate(self._best_outer_bound, idx=idx)
113115
if not pre_iter0 and self._use_rc_bt:
114116
self.reduced_costs_bounds_tightening(reduced_costs, this_outer_bound)
115117
if self._use_rc_fixer and self.fix_fraction_target > 0.0:
116-
if new_outer_bound or not self._rc_fixer_require_improving_lagrangian:
118+
if is_new_outer_bound or not self._rc_fixer_require_improving_lagrangian:
117119
self.reduced_costs_fixing(reduced_costs)
118120
else:
119121
if self.opt.cylinder_rank == 0 and self.verbose:

Diff for: mpisppy/opt/aph.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -1067,7 +1067,8 @@ def APH_main(self, spcomm=None, finalize=True):
10671067
# End APH-specific Prep
10681068

10691069
trivial_bound = self.Iter0()
1070-
self.best_bound_obj_val = trivial_bound
1070+
if self._can_update_best_bound():
1071+
self.best_bound_obj_val = trivial_bound
10711072

10721073
self.setup_Lens()
10731074
self.setup_dispatchrecord()

Diff for: mpisppy/opt/ph.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ def ph_main(self, finalize=True):
6161
if (verbose):
6262
print('Calling PH Iter0 on global rank {}'.format(global_rank))
6363
trivial_bound = self.Iter0()
64-
self.best_bound_obj_val = trivial_bound
64+
if self._can_update_best_bound():
65+
self.best_bound_obj_val = trivial_bound
6566
if (verbose):
6667
print ('Completed PH Iter0 on global rank {}'.format(global_rank))
6768
if ('asynchronousPH' in self.options) and (self.options['asynchronousPH']):

Diff for: mpisppy/opt/subgradient.py

+6-21
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,9 @@
88
###############################################################################
99

1010
import mpisppy.phbase
11-
import mpisppy.MPI as mpi
11+
import mpisppy.MPI as _mpi
1212

13-
from pyomo.common.collections import ComponentSet
14-
15-
_global_rank = mpi.COMM_WORLD.Get_rank()
13+
_global_rank = _mpi.COMM_WORLD.Get_rank()
1614

1715
class Subgradient(mpisppy.phbase.PHBase):
1816
""" Subgradient Algorithm """
@@ -43,13 +41,14 @@ def subgradient_main(self, finalize=True):
4341
smoothed = self.options['smoothed']
4442
if smoothed != 0:
4543
raise RuntimeError("Cannnot use smoothing with Subgradient algorithm")
46-
self.create_fixed_nonant_cache()
4744
self.PH_Prep(attach_prox=False, attach_smooth=smoothed)
4845

4946
if (verbose):
5047
print('Calling Subgradient Iter0 on global rank {}'.format(_global_rank))
5148
trivial_bound = self.Iter0()
52-
self.best_bound_obj_val = trivial_bound
49+
# set self.best_bound_obj_val if we don't have any additional fixed variables
50+
if self._can_update_best_bound():
51+
self.best_bound_obj_val = trivial_bound
5352
if (verbose):
5453
print('Completed Subgradient Iter0 on global rank {}'.format(_global_rank))
5554

@@ -126,21 +125,7 @@ def solve_loop(self,
126125
)
127126

128127
# set self.best_bound_obj_val if we don't have any additional fixed variables
129-
if self.can_update_best_bound():
128+
if self._can_update_best_bound():
130129
self.best_bound_obj_val = self.Ebound(verbose)
131130

132131

133-
def create_fixed_nonant_cache(self):
134-
self._initial_fixed_varibles = ComponentSet()
135-
for s in self.local_scenarios.values():
136-
for v in s._mpisppy_data.nonant_indices.values():
137-
if v.fixed:
138-
self._initial_fixed_varibles.add(v)
139-
140-
def can_update_best_bound(self):
141-
for s in self.local_scenarios.values():
142-
for v in s._mpisppy_data.nonant_indices.values():
143-
if v.fixed:
144-
if v not in self._initial_fixed_varibles:
145-
return False
146-
return True

Diff for: mpisppy/phbase.py

+8-3
Original file line numberDiff line numberDiff line change
@@ -931,6 +931,10 @@ def _vb(msg):
931931
if have_extensions:
932932
self.extobject.post_iter0()
933933

934+
self.trivial_bound = self.Ebound(verbose)
935+
if self._can_update_best_bound():
936+
self.best_bound_obj_val = self.trivial_bound
937+
934938
if self.spcomm is not None:
935939
self.spcomm.sync()
936940

@@ -955,8 +959,6 @@ def _vb(msg):
955959
#global_toc('Rank: {} - Before iter loop'.format(self.cylinder_rank), True)
956960
self.conv = None
957961

958-
self.trivial_bound = self.Ebound(verbose)
959-
960962
if dprogress and self.cylinder_rank == 0:
961963
print("")
962964
print("After PH Iteration",self._PHIter)
@@ -1000,6 +1002,10 @@ def iterk_loop(self):
10001002
self.conv = None
10011003

10021004
max_iterations = int(self.options["PHIterLimit"])
1005+
if self.spcomm is not None:
1006+
# print a screen trace for iteration 0
1007+
if self.spcomm.is_converged():
1008+
global_toc("Cylinder convergence", self.cylinder_rank == 0)
10031009

10041010
for self._PHIter in range(1, max_iterations+1):
10051011
iteration_start_time = time.time()
@@ -1159,6 +1165,5 @@ def attach_xbars(self):
11591165
scenario._mpisppy_data.nonant_indices.keys(), initialize=0.0, mutable=True
11601166
)
11611167

1162-
11631168
if __name__ == "__main__":
11641169
print ("No main for PHBase")

Diff for: mpisppy/spopt.py

+16
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import pyomo.environ as pyo
2121
from pyomo.opt import SolverFactory
22+
from pyomo.common.collections import ComponentSet
2223

2324
from mpisppy.spbase import SPBase
2425
import mpisppy.utils.sputils as sputils
@@ -65,6 +66,7 @@ def __init__(
6566
# object to get garbage collected to
6667
# free the memory the C++ model uses.
6768
SPPresolve(self).presolve()
69+
self._create_fixed_nonant_cache()
6870
self.current_solver_options = None
6971
self.extensions = extensions
7072
self.extension_kwargs = extension_kwargs
@@ -940,6 +942,20 @@ def _create_solvers(self, presolve=True):
940942
print("Set instance times: \tmin=%4.2f mean=%4.2f max=%4.2f" %
941943
(np.min(asit), np.mean(asit), np.max(asit)))
942944

945+
def _create_fixed_nonant_cache(self):
946+
self._initial_fixed_varibles = ComponentSet()
947+
for s in self.local_scenarios.values():
948+
for v in s._mpisppy_data.nonant_indices.values():
949+
if v.fixed:
950+
self._initial_fixed_varibles.add(v)
951+
952+
def _can_update_best_bound(self):
953+
for s in self.local_scenarios.values():
954+
for v in s._mpisppy_data.nonant_indices.values():
955+
if v.fixed:
956+
if v not in self._initial_fixed_varibles:
957+
return False
958+
return True
943959

944960
def subproblem_scenario_generator(self):
945961
"""

0 commit comments

Comments
 (0)