Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
45 changes: 30 additions & 15 deletions mpisppy/utils/cfg_vanilla.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,13 @@
import mpisppy.utils.sputils as sputils

def _hasit(cfg, argname):
# aside: Config objects act like a dict or an object TBD: so why the and?
return cfg.get(argname) is not None and cfg[argname] is not None and \
cfg[argname]
# aside: Config objects act like a dict or an object
val = cfg.get(argname)
# For boolean flags, require True. For non-bool values, treat any non-None
# value as "set".
if isinstance(val, bool):
return val
return val is not None

def shared_options(cfg):
shoptions = {
Expand Down Expand Up @@ -69,6 +73,24 @@ def shared_options(cfg):

return shoptions

def apply_solver_specs(name, spoke, cfg):
options = spoke["opt_kwargs"]["options"]
if _hasit(cfg, name+"_solver_name"):
options["solver_name"] = cfg.get(name+"_solver_name")
if _hasit(cfg, name+"_solver_options"):
odict = sputils.option_string_to_dict(cfg.get(name+"_solver_options"))
options["iter0_solver_options"] = odict
options["iterk_solver_options"] = copy.deepcopy(odict)
if _hasit(cfg, name+"_iter0_mipgap"):
options["iter0_solver_options"]["mipgap"] = cfg.get(name+"_iter0_mipgap")
if _hasit(cfg, name+"_iterk_mipgap"):
options["iterk_solver_options"]["mipgap"] = cfg.get(name+"_iterk_mipgap")
# re-apply max_solver_threads since we may have over-written the
# iter*_solver_options above.
if _hasit(cfg, "max_solver_threads"):
options["iter0_solver_options"]["threads"] = cfg.max_solver_threads
options["iterk_solver_options"]["threads"] = cfg.max_solver_threads

def add_multistage_options(cylinder_dict,all_nodenames,branching_factors):
cylinder_dict = copy.deepcopy(cylinder_dict)
if branching_factors is not None:
Expand Down Expand Up @@ -653,12 +675,7 @@ def lagrangian_spoke(
ph_extensions=ph_extensions,
extension_kwargs=extension_kwargs,
)
if cfg.lagrangian_iter0_mipgap is not None:
lagrangian_spoke["opt_kwargs"]["options"]["iter0_solver_options"]\
["mipgap"] = cfg.lagrangian_iter0_mipgap
if cfg.lagrangian_iterk_mipgap is not None:
lagrangian_spoke["opt_kwargs"]["options"]["iterk_solver_options"]\
["mipgap"] = cfg.lagrangian_iterk_mipgap
apply_solver_specs("lagrangian", lagrangian_spoke, cfg)
add_ph_tracking(lagrangian_spoke, cfg, spoke=True)

return lagrangian_spoke
Expand Down Expand Up @@ -689,6 +706,7 @@ def reduced_costs_spoke(
extension_kwargs=extension_kwargs,
)

apply_solver_specs("reduced_costs", rc_spoke, cfg)
add_ph_tracking(rc_spoke, cfg, spoke=True)

return rc_spoke
Expand Down Expand Up @@ -760,12 +778,7 @@ def subgradient_spoke(
extension_kwargs=extension_kwargs,
)
subgradient_spoke["opt_class"] = Subgradient
if cfg.subgradient_iter0_mipgap is not None:
subgradient_spoke["opt_kwargs"]["options"]["iter0_solver_options"]\
["mipgap"] = cfg.subgradient_iter0_mipgap
if cfg.subgradient_iterk_mipgap is not None:
subgradient_spoke["opt_kwargs"]["options"]["iterk_solver_options"]\
["mipgap"] = cfg.subgradient_iterk_mipgap
apply_solver_specs("subgradient", subgradient_spoke, cfg)
if cfg.subgradient_rho_multiplier is not None:
subgradient_spoke["opt_kwargs"]["options"]["subgradient_rho_multiplier"]\
= cfg.subgradient_rho_multiplier
Expand Down Expand Up @@ -809,6 +822,7 @@ def ph_dual_spoke(
options = ph_dual_spoke["opt_kwargs"]["options"]
if cfg.ph_dual_rescale_rho_factor is not None:
options["rho_factor"] = cfg.ph_dual_rescale_rho_factor
apply_solver_specs("ph_dual", ph_dual_spoke, cfg)

# make sure this spoke doesn't hit the time or iteration limit
options["time_limit"] = None
Expand Down Expand Up @@ -848,6 +862,7 @@ def relaxed_ph_spoke(
options = relaxed_ph_spoke["opt_kwargs"]["options"]
if cfg.relaxed_ph_rescale_rho_factor is not None:
options["rho_factor"] = cfg.relaxed_ph_rescale_rho_factor
apply_solver_specs("relaxed_ph", relaxed_ph_spoke, cfg)

# make sure this spoke doesn't hit the time or iteration limit
options["time_limit"] = None
Expand Down
74 changes: 32 additions & 42 deletions mpisppy/utils/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,17 +169,33 @@ def _bad_options(msg):
_bad_options("--rc-fixer requires --reduced-costs")

def add_solver_specs(self, prefix=""):
sstr = f"{prefix}_solver" if prefix != "" else "solver"
sstr = f"{prefix}_solver" if prefix else "solver"
if prefix:
prefix += " "
self.add_to_config(f"{sstr}_name",
description= "solver name (default None)",
description= f"{prefix}solver name (default None)",
domain = str,
default=None)

self.add_to_config(f"{sstr}_options",
description= "solver options; space delimited with = for values (default None)",
description= f"{prefix}solver options; space delimited with = for values (default None)",
domain = str,
default=None)

def add_mipgap_specs(self, prefix=""):
sstr = f"{prefix}_" if prefix else ""
if prefix:
prefix += " "
self.add_to_config(f"{sstr}iter0_mipgap",
description=f"{prefix}mip gap option for iteration 0 (default None)",
domain=float,
default=None)

self.add_to_config(f"{sstr}iterk_mipgap",
description=f"{prefix}mip gap option non-zero iterations (default None)",
domain=float,
default=None)

def _common_args(self):
raise RuntimeError("_common_args is no longer used. See comments at top of config.py")

Expand Down Expand Up @@ -211,7 +227,7 @@ def popular_args(self):
domain=bool,
default=False)

self.add_solver_specs(prefix="")
self.add_solver_specs()

self.add_to_config("seed",
description="Seed for random numbers (default is 1134)",
Expand Down Expand Up @@ -456,16 +472,7 @@ def timed_mipgap_args(self):
default="0.05:600")

def mip_options(self):

self.add_to_config("iter0_mipgap",
description="mip gap option for iteration 0 (default None)",
domain=float,
default=None)

self.add_to_config("iterk_mipgap",
description="mip gap option non-zero iterations (default None)",
domain=float,
default=None)
self.add_mipgap_specs()

def aph_args(self):

Expand Down Expand Up @@ -660,15 +667,8 @@ def lagrangian_args(self):
domain=bool,
default=False)

self.add_to_config("lagrangian_iter0_mipgap",
description="lgr. iter0 solver option mipgap (default None)",
domain=float,
default=None)

self.add_to_config("lagrangian_iterk_mipgap",
description="lgr. iterk solver option mipgap (default None)",
domain=float,
default=None)
self.add_solver_specs("lagrangian")
self.add_mipgap_specs("lagrangian")


def reduced_costs_args(self):
Expand All @@ -677,6 +677,8 @@ def reduced_costs_args(self):
description="have a reduced costs spoke",
domain=bool,
default=False)

self.add_solver_specs("reduced_costs")

self.add_to_config('rc_verbose',
description="verbose output for reduced costs",
Expand Down Expand Up @@ -731,16 +733,7 @@ def lagranger_args(self):
description="have a special lagranger spoke",
domain=bool,
default=False)

self.add_to_config("lagranger_iter0_mipgap",
description="lagranger iter0 mipgap (default None)",
domain=float,
default=None)

self.add_to_config("lagranger_iterk_mipgap",
description="lagranger iterk mipgap (default None)",
domain=float,
default=None)
self.add_mipgap_specs("lagranger")

self.add_to_config("lagranger_rho_rescale_factors_json",
description="json file: rho rescale factors (default None)",
Expand All @@ -755,15 +748,8 @@ def subgradient_bounder_args(self):
domain=bool,
default=False)

self.add_to_config("subgradient_iter0_mipgap",
description="lgr. iter0 solver option mipgap (default None)",
domain=float,
default=None)

self.add_to_config("subgradient_iterk_mipgap",
description="lgr. iterk solver option mipgap (default None)",
domain=float,
default=None)
self.add_solver_specs("subgradient")
self.add_mipgap_specs("subgradient")

self.add_to_config("subgradient_rho_multiplier",
description="rescale rho (update step size) by this factor",
Expand All @@ -785,6 +771,7 @@ def relaxed_ph_args(self):
description="Used to rescale rho initially (default=1.0)",
domain=float,
default=1.0)
self.add_solver_specs("relaxed_ph")


def ph_dual_args(self):
Expand All @@ -793,6 +780,9 @@ def ph_dual_args(self):
description="have a dual PH spoke",
domain=bool,
default=False)

self.add_solver_specs("ph_dual")

self.add_to_config("ph_dual_rescale_rho_factor",
description="Used to rescale rho initially (default=0.1)",
domain=float,
Expand Down
2 changes: 1 addition & 1 deletion mpisppy/utils/solver_spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def solver_specification(cfg, prefix="", name_required=True):
solver_name = cfg[name_idx]
options_idx = "solver_options" if sroot == "" else f"{sroot}_solver_options"
ostr = cfg.get(options_idx)
solver_options = sputils.option_string_to_dict(ostr) # will return None for None
solver_options = sputils.option_string_to_dict(ostr)
break
else:
if name_required:
Expand Down
2 changes: 1 addition & 1 deletion mpisppy/utils/sputils.py
Original file line number Diff line number Diff line change
Expand Up @@ -653,7 +653,7 @@ def convert_value_string_to_number(s):

solver_options = dict()
if ostr is None or ostr == "":
return None
return solver_options
for this_option_string in ostr.split():
this_option_pieces = this_option_string.strip().split("=")
if len(this_option_pieces) == 2:
Expand Down
Loading