Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions doc/src/extensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ CoeffRho
^^^^^^^^

Set per variable rho values proportional to the cost coefficient on each non-anticipative variable,
with an optional multiplier (default = 1.0). If the coefficient is 0, the default rho value is used instead.
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.

primal_dual_rho
^^^^^^^^^^^^^^^
Expand Down Expand Up @@ -228,7 +228,7 @@ There are options in ``cfg`` to control dynamic updates.
mult_rho_updater
^^^^^^^^^^^^^^^^

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

cross-scenario cuts
^^^^^^^^^^^^^^^^^^^
Expand Down
19 changes: 16 additions & 3 deletions examples/run_all.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ def do_one_mmw(dirname, modname, runefstring, npyfile, mmwargstring):

os.remove(npyfile)
os.chdir("..")
os.chdir("..") # moved to CI directory

do_one("farmer/CI", "farmer_ef.py", 1,
"1 3 {}".format(solver_name))
Expand Down Expand Up @@ -190,13 +191,25 @@ def do_one_mmw(dirname, modname, runefstring, npyfile, mmwargstring):
"--solver-name={}".format(solver_name))
do_one("farmer/archive", "farmer_cylinders.py", 4,
"--num-scens 6 --bundles-per-rank=2 --max-iterations=50 "
"--fwph-stop-check-tol 0.1 "
"--fwph-stop-check-tol 0.1 --rel-gap 0.001 "
"--default-rho=1 --solver-name={} --lagrangian --xhatshuffle --fwph".format(solver_name))
do_one("farmer", "../../mpisppy/generic_cylinders.py", 3,
"--module-name farmer --num-scens 6 "
"--rel-gap 0.001 --max-iterations=50 "
"--grad-rho --grad-order-stat 0.5 "
"--default-rho=2 --solver-name={} --lagrangian --xhatshuffle".format(solver_name))
do_one("farmer", "../../mpisppy/generic_cylinders.py", 4,
"--module-name farmer "
"--num-scens 6 --bundles-per-rank=2 --max-iterations=50 "
"--module-name farmer --num-scens 6 "
"--rel-gap 0.001 --max-iterations=50 "
"--ph-primal-hub --ph-dual --ph-dual-rescale-rho-factor=0.1 "
"--default-rho=2 --solver-name={} --lagrangian --xhatshuffle".format(solver_name))
do_one("farmer", "../../mpisppy/generic_cylinders.py", 4,
"--module-name farmer "
"--num-scens 6 --max-iterations=50 --grad-rho --grad-order-stat 0.5 "
"--ph-dual-grad-order-stat 0.3 "
"--ph-primal-hub --ph-dual --ph-dual-rescale-rho-factor=0.1 --ph-dual-rho-multiplier 0.2 "
"--default-rho=1 --solver-name={} --lagrangian --xhatshuffle".format(solver_name))

do_one("farmer/archive", "farmer_cylinders.py", 2,
"--num-scens 6 --bundles-per-rank=2 --max-iterations=50 "
"--default-rho=1 "
Expand Down
2 changes: 1 addition & 1 deletion mpisppy/extensions/grad_rho.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,4 +312,4 @@ def register_receive_fields(self):
self.best_xhat_spoke_index,
)

return
return
18 changes: 13 additions & 5 deletions mpisppy/generic_cylinders.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import sys
import os
import json
import copy
import shutil
import numpy as np
import pyomo.environ as pyo
Expand Down Expand Up @@ -340,14 +341,21 @@ def _do_decomp(module, cfg, scenario_creator, scenario_creator_kwargs, scenario_
rho_setter = rho_setter,
all_nodenames = all_nodenames,
)
if cfg.sep_rho or cfg.coeff_rho or cfg.sensi_rho or cfg.grad_rho:
# Note that this deepcopy might be expensive if certain wrappers were used.
# (Could we do the modification to cfg in ph_dual to obviate the need?)
modified_cfg = copy.deepcopy(cfg)
modified_cfg["grad_rho_multiplier"] = cfg.ph_dual_rho_multiplier
if cfg.sep_rho:
vanilla.add_sep_rho(ph_dual_spoke, cfg)
vanilla.add_sep_rho(ph_dual_spoke, modified_cfg)
if cfg.coeff_rho:
vanilla.add_coeff_rho(ph_dual_spoke, cfg)
vanilla.add_coeff_rho(ph_dual_spoke, modified_cfg)
if cfg.sensi_rho:
vanilla.add_sensi_rho(ph_dual_spoke, cfg)
# TBD xxxxx add grad rho asap after PR#559 is merged
# Note: according to DLW, it is non-trivial to deal with the cfg args
vanilla.add_sensi_rho(ph_dual_spoke, modified_cfg)
if cfg.grad_rho:
modified_cfg["grad_order_stat"] = cfg.ph_dual_grad_order_stat
vanilla.add_grad_rho(ph_dual_spoke, modified_cfg)


# relaxed ph spoke
if cfg.relaxed_ph:
Expand Down
42 changes: 31 additions & 11 deletions mpisppy/utils/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,21 +143,30 @@ def get(self, name, ifmissing=None):
def checker(self):
"""Verify that options *selected* make sense with respect to each other
"""
def _bad_rho_setters(msg):
raise ValueError("Rho setter options do not make sense together:\n"
def _bad_options(msg):
raise ValueError("Options do not make sense together:\n"
f"{msg}")

if self.get("grad_rho") and self.get("sensi_rho"):
_bad_rho_setters("Only one rho setter can be active.")

# remember that True is 1 and False is 0
if (self.get("grad_rho") + self.get("sensi_rho") + self.get("coeff_rho") + self.get("reduced_costs_rho") + self.get("sep_rho")) > 1:
_bad_options("Only one rho setter can be active.")
if not (self.get("grad_rho")
or self.get("sensi_rho")
or self.get("sep_rho")
or self.get("reduced_costs_rho")):
if self.get("dynamic_rho_primal_crit") or self.get("dynamic_rho_dual_crit"):
_bad_rho_setters("dynamic rho only works with grad-, sensi-, and sep-rho")
_bad_options("dynamic rho only works with an automated rho setter")
if self.get("grad_rho") and self.get("bundles_per_rank") != 0:
_bad_options("Grad rho does not work with loose bundling (--bundles-per-rank).\n "
"Also note that loose bundling is being deprecated in favor of proper bundles.")

if self.get("ph_primal_hub")\
and not (self.get("ph_dual") or self.get("relaxed_ph")):
_bad_options("--ph-primal-hub is used only when there is a cylinder that provideds Ws "
"such as --ph-dual or --relaxed-ph")

if self.get("rc_fixer") and not self.get("reduced_costs"):
_bad_rho_setters("--rc-fixer requires --reduced-costs")

_bad_options("--rc-fixer requires --reduced-costs")

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


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

def relaxed_ph_args(self):

Expand All @@ -753,9 +763,19 @@ def ph_dual_args(self):
domain=bool,
default=False)
self.add_to_config("ph_dual_rescale_rho_factor",
description="Used to rescale rho initially (default=1.0)",
description="Used to rescale rho initially (default=0.1)",
domain=float,
default=1.0)
default=0.1)
self.add_to_config("ph_dual_rho_multiplier",
description="Rescale factor for dynamic updates in ph_dual if ph_dual and a rho setter are chosen;"
" note that it is not cummulative (default=0.1)",
domain=float,
default=0.1)
self.add_to_config("ph_dual_grad_order_stat",
description="Order stat for selection rho if ph_dual and ph_dual_grad_rho are chosen;"
" note that this is impacted by the multiplier (default=0.5)",
domain=float,
default=0.5)


def xhatlooper_args(self):
Expand Down
Loading