Skip to content

Commit 4c54d73

Browse files
committed
trying to add a shared column every iteration
1 parent e388dd5 commit 4c54d73

File tree

1 file changed

+43
-21
lines changed

1 file changed

+43
-21
lines changed

Diff for: mpisppy/opt/fwph.py

+43-21
Original file line numberDiff line numberDiff line change
@@ -102,13 +102,18 @@ def fw_prep(self):
102102
self._attach_MIP_QP_maps()
103103
self._set_QP_objective()
104104
self._initialize_QP_var_values()
105+
self._setup_shared_column_generation()
105106

106107
self._QP_nonants = {}
107108
self._MIP_nonants = {}
108109

109-
if self.FW_options["FW_iter_limit"] == 1:
110-
number_points = self._generate_starting_point()
111-
if number_points == 0:
110+
number_initial_column_tries = self.options.get("FW_initialization_attempts", 20)
111+
if self.FW_options["FW_iter_limit"] == 1 and number_initial_column_tries < 1:
112+
global_toc(f"{self.__class__.__name__}: Warning: FWPH needs an initial shared column if FW_iter_limit == 1. Increasing FW_iter_limit to 2 to ensure convergence")
113+
self.FW_options["FW_iter_limit"] = 2
114+
if self.FW_options["FW_iter_limit"] == 1 or number_initial_column_tries > 0:
115+
number_points = self._generate_shared_column(number_initial_column_tries)
116+
if number_points == 0 and self.FW_options["FW_iter_limit"] == 1:
112117
global_toc(f"{self.__class__.__name__}: Warning: FWPH failed to find an initial feasible solution. Increasing FW_iter_limit to 2 to ensure convergence")
113118
self.FW_options["FW_iter_limit"] = 2
114119

@@ -193,6 +198,19 @@ def fwph_main(self, finalize=True):
193198
secs = time.time() - self.t0
194199
self._output(self._local_bound, best_bound, diff, secs)
195200

201+
# add a shared column
202+
shared_columns = self.options.get("FWPH_shared_columns_per_iteration", 1)
203+
if shared_columns > 0:
204+
self.mpicomm.Barrier()
205+
self._disable_W()
206+
for s in self.local_subproblems.values():
207+
if sputils.is_persistent(s._solver_plugin):
208+
active_objective_datas = list(s.component_data_objects(
209+
pyo.Objective, active=True, descend_into=True))
210+
s._solver_plugin.set_objective(active_objective_datas[0])
211+
self._generate_shared_column(shared_columns)
212+
self._reenable_W()
213+
196214
## Hubs/spokes take precedence over convergers
197215
if self.spcomm:
198216
if isinstance(self.spcomm, FWPHHub):
@@ -214,7 +232,6 @@ def fwph_main(self, finalize=True):
214232
# tphloop = time.time() - tbphloop
215233
# print(f"PH iter {self._PHIter}, total time: {tphloop}")
216234

217-
218235
if finalize:
219236
weight_dict = self._gather_weight_dict() # None if rank != 0
220237
xbars_dict = self._get_xbars() # None if rank != 0
@@ -281,6 +298,8 @@ def SDM(self, model_name):
281298
tee=teeme,
282299
verbose=verbose,
283300
)
301+
# TODO: fixme for maxmimization / larger objectives
302+
self.options["iterk_solver_options"]["MIPABSCUTOFF"] = 1e40
284303
# tmipsolve = time.time() - tbmipsolve
285304

286305
# Algorithm 2 lines 6--8
@@ -681,41 +700,44 @@ def _initialize_QP_var_values(self):
681700
qp.xr[node_name, ix].set_value(
682701
mip._mpisppy_data.nonant_vars[arb_scenario, node_name, ix].value)
683702

684-
def _generate_starting_point(self):
685-
""" Called after iter 0 to satisfy the condition of equation (17)
686-
in Boland et al., if t_max / FW_iter_limit == 1
687-
"""
703+
def _setup_shared_column_generation(self):
704+
""" helper for shared column generation """
688705
#We need to keep track of the way scenario_names were sorted
689706
scen_names = list(enumerate(self.all_scenario_names))
690707

691-
self.random_seed = 42
708+
self._random_seed = 42
692709
# Have a separate stream for shuffling
693-
self.random_stream = random.Random()
694-
self.random_stream.seed(self.random_seed)
710+
random_stream = random.Random()
711+
random_stream.seed(self._random_seed)
695712

696713
# shuffle the scenarios associated (i.e., sample without replacement)
697-
shuffled_scenarios = self.random_stream.sample(scen_names,
698-
len(scen_names))
714+
shuffled_scenarios = random_stream.sample(scen_names, len(scen_names))
699715

700-
scenario_cycler = ScenarioCycler(shuffled_scenarios,
716+
self._scenario_cycler = ScenarioCycler(shuffled_scenarios,
701717
self.nonleaves,
702718
False,
703719
None)
704720

705-
xhatter = XhatBase(self)
706-
xhatter.post_iter0()
721+
self._xhatter = XhatBase(self)
722+
self._xhatter.post_iter0()
723+
724+
def _generate_shared_column(self, tries=1):
725+
""" Called after iter 0 to satisfy the condition of equation (17)
726+
in Boland et al., if t_max / FW_iter_limit == 1
727+
"""
707728

708729
stage2EFsolvern = self.options.get("stage2EFsolvern", None)
709730
branching_factors = self.options.get("branching_factors", None) # for stage2ef
710731

711732
number_points = 0
712-
for _ in range(self.options.get("FW_initialization_attempts", 20)):
733+
for t in range(min(tries, len(self.all_scenario_names))):
713734
# will save in best solution
714-
snamedict = scenario_cycler.get_next()
735+
snamedict = self._scenario_cycler.get_next()
715736
if snamedict is None:
716-
return number_points
717-
obj = xhatter._try_one(snamedict,
718-
solver_options = self.options["mip_solver_options"],
737+
self._scenario_cycler.begin_epoch()
738+
snamedict = self._scenario_cycler.get_next()
739+
obj = self._xhatter._try_one(snamedict,
740+
solver_options = self.options["iterk_solver_options"],
719741
verbose=False,
720742
restore_nonants=False,
721743
stage2EFsolvern=stage2EFsolvern,

0 commit comments

Comments
 (0)