Skip to content

Commit 06f7286

Browse files
authored
Merge pull request #564 from DLWoodruff/ph_dual_grad_rho
Ph dual grad rho
2 parents f0134c1 + 542c31a commit 06f7286

File tree

5 files changed

+62
-21
lines changed

5 files changed

+62
-21
lines changed

doc/src/extensions.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ CoeffRho
194194
^^^^^^^^
195195

196196
Set per variable rho values proportional to the cost coefficient on each non-anticipative variable,
197-
with an optional multiplier (default = 1.0). If the coefficient is 0, the default rho value is used instead.
197+
with an optional multiplier (default = 1.0) that is applied to the computed value. If the coefficient is 0, the default rho value is used instead.
198198

199199
primal_dual_rho
200200
^^^^^^^^^^^^^^^
@@ -228,7 +228,7 @@ There are options in ``cfg`` to control dynamic updates.
228228
mult_rho_updater
229229
^^^^^^^^^^^^^^^^
230230

231-
This extension does a simple multiplicative update of rho.
231+
This extension does a simple multiplicative update of rho; consequently, the update is cumulative.
232232

233233
cross-scenario cuts
234234
^^^^^^^^^^^^^^^^^^^

examples/run_all.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ def do_one_mmw(dirname, modname, runefstring, npyfile, mmwargstring):
158158

159159
os.remove(npyfile)
160160
os.chdir("..")
161+
os.chdir("..") # moved to CI directory
161162

162163
do_one("farmer/CI", "farmer_ef.py", 1,
163164
"1 3 {}".format(solver_name))
@@ -190,13 +191,25 @@ def do_one_mmw(dirname, modname, runefstring, npyfile, mmwargstring):
190191
"--solver-name={}".format(solver_name))
191192
do_one("farmer/archive", "farmer_cylinders.py", 4,
192193
"--num-scens 6 --bundles-per-rank=2 --max-iterations=50 "
193-
"--fwph-stop-check-tol 0.1 "
194+
"--fwph-stop-check-tol 0.1 --rel-gap 0.001 "
194195
"--default-rho=1 --solver-name={} --lagrangian --xhatshuffle --fwph".format(solver_name))
196+
do_one("farmer", "../../mpisppy/generic_cylinders.py", 3,
197+
"--module-name farmer --num-scens 6 "
198+
"--rel-gap 0.001 --max-iterations=50 "
199+
"--grad-rho --grad-order-stat 0.5 "
200+
"--default-rho=2 --solver-name={} --lagrangian --xhatshuffle".format(solver_name))
195201
do_one("farmer", "../../mpisppy/generic_cylinders.py", 4,
196-
"--module-name farmer "
197-
"--num-scens 6 --bundles-per-rank=2 --max-iterations=50 "
202+
"--module-name farmer --num-scens 6 "
203+
"--rel-gap 0.001 --max-iterations=50 "
198204
"--ph-primal-hub --ph-dual --ph-dual-rescale-rho-factor=0.1 "
205+
"--default-rho=2 --solver-name={} --lagrangian --xhatshuffle".format(solver_name))
206+
do_one("farmer", "../../mpisppy/generic_cylinders.py", 4,
207+
"--module-name farmer "
208+
"--num-scens 6 --max-iterations=50 --grad-rho --grad-order-stat 0.5 "
209+
"--ph-dual-grad-order-stat 0.3 "
210+
"--ph-primal-hub --ph-dual --ph-dual-rescale-rho-factor=0.1 --ph-dual-rho-multiplier 0.2 "
199211
"--default-rho=1 --solver-name={} --lagrangian --xhatshuffle".format(solver_name))
212+
200213
do_one("farmer/archive", "farmer_cylinders.py", 2,
201214
"--num-scens 6 --bundles-per-rank=2 --max-iterations=50 "
202215
"--default-rho=1 "

mpisppy/extensions/grad_rho.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,4 +312,4 @@ def register_receive_fields(self):
312312
self.best_xhat_spoke_index,
313313
)
314314

315-
return
315+
return

mpisppy/generic_cylinders.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import sys
1212
import os
1313
import json
14+
import copy
1415
import shutil
1516
import numpy as np
1617
import pyomo.environ as pyo
@@ -340,14 +341,21 @@ def _do_decomp(module, cfg, scenario_creator, scenario_creator_kwargs, scenario_
340341
rho_setter = rho_setter,
341342
all_nodenames = all_nodenames,
342343
)
344+
if cfg.sep_rho or cfg.coeff_rho or cfg.sensi_rho or cfg.grad_rho:
345+
# Note that this deepcopy might be expensive if certain wrappers were used.
346+
# (Could we do the modification to cfg in ph_dual to obviate the need?)
347+
modified_cfg = copy.deepcopy(cfg)
348+
modified_cfg["grad_rho_multiplier"] = cfg.ph_dual_rho_multiplier
343349
if cfg.sep_rho:
344-
vanilla.add_sep_rho(ph_dual_spoke, cfg)
350+
vanilla.add_sep_rho(ph_dual_spoke, modified_cfg)
345351
if cfg.coeff_rho:
346-
vanilla.add_coeff_rho(ph_dual_spoke, cfg)
352+
vanilla.add_coeff_rho(ph_dual_spoke, modified_cfg)
347353
if cfg.sensi_rho:
348-
vanilla.add_sensi_rho(ph_dual_spoke, cfg)
349-
# TBD xxxxx add grad rho asap after PR#559 is merged
350-
# Note: according to DLW, it is non-trivial to deal with the cfg args
354+
vanilla.add_sensi_rho(ph_dual_spoke, modified_cfg)
355+
if cfg.grad_rho:
356+
modified_cfg["grad_order_stat"] = cfg.ph_dual_grad_order_stat
357+
vanilla.add_grad_rho(ph_dual_spoke, modified_cfg)
358+
351359

352360
# relaxed ph spoke
353361
if cfg.relaxed_ph:

mpisppy/utils/config.py

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -143,21 +143,30 @@ def get(self, name, ifmissing=None):
143143
def checker(self):
144144
"""Verify that options *selected* make sense with respect to each other
145145
"""
146-
def _bad_rho_setters(msg):
147-
raise ValueError("Rho setter options do not make sense together:\n"
146+
def _bad_options(msg):
147+
raise ValueError("Options do not make sense together:\n"
148148
f"{msg}")
149-
150-
if self.get("grad_rho") and self.get("sensi_rho"):
151-
_bad_rho_setters("Only one rho setter can be active.")
149+
150+
# remember that True is 1 and False is 0
151+
if (self.get("grad_rho") + self.get("sensi_rho") + self.get("coeff_rho") + self.get("reduced_costs_rho") + self.get("sep_rho")) > 1:
152+
_bad_options("Only one rho setter can be active.")
152153
if not (self.get("grad_rho")
153154
or self.get("sensi_rho")
154155
or self.get("sep_rho")
155156
or self.get("reduced_costs_rho")):
156157
if self.get("dynamic_rho_primal_crit") or self.get("dynamic_rho_dual_crit"):
157-
_bad_rho_setters("dynamic rho only works with grad-, sensi-, and sep-rho")
158+
_bad_options("dynamic rho only works with an automated rho setter")
159+
if self.get("grad_rho") and self.get("bundles_per_rank") != 0:
160+
_bad_options("Grad rho does not work with loose bundling (--bundles-per-rank).\n "
161+
"Also note that loose bundling is being deprecated in favor of proper bundles.")
162+
163+
if self.get("ph_primal_hub")\
164+
and not (self.get("ph_dual") or self.get("relaxed_ph")):
165+
_bad_options("--ph-primal-hub is used only when there is a cylinder that provideds Ws "
166+
"such as --ph-dual or --relaxed-ph")
167+
158168
if self.get("rc_fixer") and not self.get("reduced_costs"):
159-
_bad_rho_setters("--rc-fixer requires --reduced-costs")
160-
169+
_bad_options("--rc-fixer requires --reduced-costs")
161170

162171
def add_solver_specs(self, prefix=""):
163172
sstr = f"{prefix}_solver" if prefix != "" else "solver"
@@ -732,7 +741,8 @@ def subgradient_bounder_args(self):
732741

733742

734743
def ph_ob_args(self):
735-
raise RuntimeError("ph_ob (the --ph-ob option) and ph_ob_args were deprecated and replaced with ph_dual August 2025")
744+
raise RuntimeError("ph_ob (the --ph-ob option) and ph_ob_args were deprecated and replaced with ph_dual August 2025\n"
745+
"To get the same effect as ph_ob, use --ph-dual with --ph-dual-grad-rho")
736746

737747
def relaxed_ph_args(self):
738748

@@ -753,9 +763,19 @@ def ph_dual_args(self):
753763
domain=bool,
754764
default=False)
755765
self.add_to_config("ph_dual_rescale_rho_factor",
756-
description="Used to rescale rho initially (default=1.0)",
766+
description="Used to rescale rho initially (default=0.1)",
767+
domain=float,
768+
default=0.1)
769+
self.add_to_config("ph_dual_rho_multiplier",
770+
description="Rescale factor for dynamic updates in ph_dual if ph_dual and a rho setter are chosen;"
771+
" note that it is not cummulative (default=1.0)",
757772
domain=float,
758773
default=1.0)
774+
self.add_to_config("ph_dual_grad_order_stat",
775+
description="Order stat for selecting rho if ph_dual and ph_dual_grad_rho are chosen;"
776+
" note that this is impacted by the multiplier (default=0.0)",
777+
domain=float,
778+
default=0.0)
759779

760780

761781
def xhatlooper_args(self):

0 commit comments

Comments
 (0)