gemseo_box_subdivision.settings#

The settings of a box-subdivision run, in the units the method measures.

The master of the outer approximation takes a dozen settings, several of which are coupled: two mechanisms that must not be combined, a trust-region radius whose unit is not obvious, and a convexity margin in the units of the objective. This module names them in the terms the methodology uses and rejects the combinations that measure nothing.

The convexity margin is the one with no value that transfers between problems, and SweptBoxSubdivisionSettings is the entry point that avoids choosing it, sweeping a ladder of values instead; see convexity_sweep.

The two levels are solved by two algorithms, and both are settings like the rest: BoxSubdivisionSettings.master_algo_name and BoxSubdivisionSettings.sub_problem_algo_name name them, and BoxSubdivisionSettings.master_algo_settings and BoxSubdivisionSettings.sub_problem_algo_settings carry whatever else each one takes. The defaults are the pair the benchmark measures, the outer-approximation master over SLSQP; naming another algorithm is checked against what that algorithm declares, so a setting the chosen algorithm does not have is refused when the settings are built rather than when the run starts.

Module Attributes

TRUST_REGION_RADIUS

The default radius of the trust region, in components changed.

CONVEXITY_MARGIN

The default convexity margin of the adaptive repair.

MASTER_ALGO_NAME

The default algorithm solving the master problem.

N_PARALLEL_POINTS

The default number of points the master probes per iteration.

SUB_PROBLEM_ALGO_NAME

The default algorithm solving each sub-problem inside its box.

Classes

BaseBoxSubdivisionSettings(*[, mechanism, ...])

What every box-subdivision run needs, whichever master drives it.

BoxSubdivisionSettings(*[, mechanism, ...])

The settings of a box-subdivision run, with the master you name.

SweptBoxSubdivisionSettings(*[, mechanism, ...])

The settings of a run that sweeps the convexity instead of choosing it.

gemseo_box_subdivision.settings.TRUST_REGION_RADIUS: int = 2#

The default radius of the trust region, in components changed.

A radius of two is what the measurements support, and it is far from the radius at which the region stops constraining. Letting the master change every component at once is markedly worse, and removing the region is worse still.

gemseo_box_subdivision.settings.CONVEXITY_MARGIN: float = 100.0#

The default convexity margin of the adaptive repair.

This is an absolute quantity in the units of the objective, so the default only suits an objective of the scale of the benchmark. It has to be set to the order of the variation of the objective over the design space.

gemseo_box_subdivision.settings.MASTER_ALGO_NAME: str = 'BILEVEL_MASTER_OUTER_APPROXIMATION'#

The default algorithm solving the master problem.

The master decides which box to look into, so it is the outer approximation itself rather than an ordinary optimizer, and this is the one the plugin gemseo-bilevel-outer-approximation provides. Another master is allowed, any that handles integer variables, but only this one takes the settings BoxSubdivisionSettings.to_master_settings() translates; the rest are driven by BoxSubdivisionSettings.master_algo_settings.

gemseo_box_subdivision.settings.N_PARALLEL_POINTS: int = 4#

The default number of points the master probes per iteration.

One trust-region radius per point, and under SweptBoxSubdivisionSettings one rung of the convexity ladder per point as well: a probe per rung is what makes an iteration span the ladder, so the two counts are this one number rather than two that can disagree.

gemseo_box_subdivision.settings.SUB_PROBLEM_ALGO_NAME: str = 'SLSQP'#

The default algorithm solving each sub-problem inside its box.

A box is a continuous problem with bounds, and every result reported was measured with SLSQP over it. A derivative-free solver is a legitimate choice for an objective whose gradient is unavailable or noisy, but nothing here measures one.

class gemseo_box_subdivision.settings.BaseBoxSubdivisionSettings(*, mechanism='adaptive', trust_region_radius=2, n_parallel_points=4, max_iter=80, sub_problem_max_iter=40, tolerance=0.0001, sub_problem_algo_name='SLSQP', sub_problem_algo_settings=<factory>)[source]#

Bases: ABC

What every box-subdivision run needs, whichever master drives it.

A run is two algorithms: a master deciding which box to look into, and a solver running inside the box it chose. This class holds what belongs to the run rather than to either of them, and what the sub-problem level takes; BoxSubdivisionSettings and SweptBoxSubdivisionSettings add the master.

Every setting is given by name. They are a vocabulary rather than an order, and which class holds which is a matter of what applies to what, so an order would change under the reader; naming one refuses what it cannot take rather than binding it to whatever sits in that position.

Parameters:
  • mechanism (str)

  • trust_region_radius (int)

  • n_parallel_points (int)

  • max_iter (int)

  • sub_problem_max_iter (int)

  • tolerance (float)

  • sub_problem_algo_name (str)

  • sub_problem_algo_settings (Mapping[str, Any])

MECHANISMS: ClassVar[tuple[str, ...]] = ('adaptive', 'convexification')#

The two mechanisms keeping the cuts usable on a non-convex problem.

MASTER_TERMS: ClassVar[dict[str, str]] = {'convexification_constant': 'convexification_constant', 'convexity_margin': 'min_dfk', 'max_iter': 'max_iter', 'mechanism': 'adapt', 'n_parallel_points': 'number_of_parallel_points', 'tolerance': 'ub_tol', 'trust_region_radius': 'max_step'}#

What each setting of the method is called by the master it is meant for.

The method names a quantity; the master names a setting. A master declaring the name on the right takes the setting on the left, and one that does not takes nothing of it, which is what makes a setting of the outer approximation inapplicable to another master.

mechanism: str = 'adaptive'#

How the master keeps its cuts usable, "adaptive" or "convexification".

They rest on different arguments and are not combined: the adaptive repair fixes the slope of each cut against the boxes already solved, while the convexification adds a convex term whose constant, once it dominates the concavity of the relaxation, makes the outer approximation convergent. Measuring the two together measures neither.

This describes an outer approximation and nothing else, so it reaches a master only where that master declares the settings it drives; see BoxSubdivisionSettings.to_master_settings().

trust_region_radius: int = 2#

The radius of the trust region of the master, in components changed.

n_parallel_points: int = 4#

The number of trust-region radii the master probes per iteration.

The pure convexification probes a single point whatever this says, which is the configuration its constant was measured at; only the adaptive repair and a sweep spread the probes, a sweep because a probe per rung is what makes an iteration span its ladder.

max_iter: int = 80#

The number of iterations of the master, not of the sub-problems.

sub_problem_max_iter: int = 40#

The number of iterations of each sub-problem.

tolerance: float = 0.0001#

The tolerance on the upper bound of the master.

sub_problem_algo_name: str = 'SLSQP'#

The name of the algorithm solving each sub-problem inside its box.

Any GEMSEO optimizer, its name as the library factory knows it. The sub-problem is continuous and bounded, which is what SUB_PROBLEM_ALGO_NAME suits; a solver returning a point far from a local optimum of its box leaves the cut of that box wrong, rather than merely loose.

sub_problem_algo_settings: Mapping[str, Any]#

Any other setting of the sub-problem solver, passed through unchanged.

These win over sub_problem_max_iter, which is the max_iter of the solver under its own name.

property convexity_setting_name: str#

The name of the master setting the mechanism calibrates.

The adaptive repair reads the convexity margin under "min_dfk", the pure convexification its constant under "convexification_constant". This is what a sweep of the convexity varies, and naming it here keeps the sweep from having to know which mechanism is active.

abstract property master_algo_name: str#

The name of the algorithm solving the master problem.

Every run needs one, and the scenario executes it by this name, so it is part of the contract rather than of either construction: the general one takes it as a setting, the swept one pins it to the master implementing the sweep.

abstract property convexity_value: float | None#

The value the mechanism calibrates, or None when it is not known.

A sweep reading its bound off the objective has none to give before the run has solved anything: only the master, which observes the objective, can compute one. None then leaves the master its own value rather than overwriting it, an unknown guard being no reason to impose a guard of zero.

abstractmethod to_master_settings(radius=None)[source]#

Return the settings of the master problem.

Parameters:

radius (int | None) – The radius of the trust region, overriding trust_region_radius.

Returns:

The settings of the master problem.

Return type:

dict[str, Any]

drive_the_master()[source]#

Run the master under whatever this construction has to supply it.

A master configured entirely by its settings needs nothing, which is the general case and what this yields. The swept construction overrides it where the installed master does not sweep on its own.

Yields:

Nothing.

Return type:

Iterator[None]

master_settings_the_run_needs()[source]#

Return what a run keeps of its master settings whatever it overrides.

Nothing, in the general construction: its master is named and configured by the caller, so a caller giving that master settings of its own is choosing what it is given, the convexity included.

Returns:

The settings a caller configuring the master does not override.

Return type:

dict[str, Any]

to_sub_problem_settings()[source]#

Return the settings of a sub-problem.

Returns:

The settings of the algorithm solving a sub-problem inside its box.

Return type:

dict[str, Any]

create_sub_problem_settings_model()[source]#

Return the settings of a sub-problem, as a Pydantic model.

This is what the Benders formulation takes as its sub_problem_algo_settings, the model carrying the name of the algorithm it selects.

Returns:

The settings of a sub-problem, for sub_problem_algo_name.

Return type:

BaseAlgorithmSettings

class gemseo_box_subdivision.settings.BoxSubdivisionSettings(*, mechanism='adaptive', trust_region_radius=2, n_parallel_points=4, max_iter=80, sub_problem_max_iter=40, tolerance=0.0001, sub_problem_algo_name='SLSQP', sub_problem_algo_settings=<factory>, convexity_margin=100.0, convexification_constant=100.0, options=<factory>, master_algo_name='BILEVEL_MASTER_OUTER_APPROXIMATION', master_algo_settings=<factory>)[source]#

Bases: BaseBoxSubdivisionSettings

The settings of a box-subdivision run, with the master you name.

The defaults are the configuration the benchmark supports: the adaptive repair of the cut slopes, four probe points, a trust region of two components, and the outer-approximation master over SLSQP.

Two of these settings decide whether a run works at all, and neither has a default that transfers between problems:

convexity_margin

subtracted from an objective difference, so it is absolute and in the units of your objective. Start from the variation of the objective over the design space. It crosses a threshold and then saturates, so erring high costs sub-problems rather than quality.

trust_region_radius

the number of components a candidate box may change. Keep it small.

Not choosing either is what SweptBoxSubdivisionSettings is for.

The mechanism belongs to the outer approximation, not to every master. mechanism, the two convexity values, the trust region and the parallel points are settings of an outer-approximation master, and a master that does not declare them is driven by master_algo_settings alone, under its own names. Naming such a master and setting one of those is refused where it is written rather than silently ignored at execution.

Parameters:
  • mechanism (str)

  • trust_region_radius (int)

  • n_parallel_points (int)

  • max_iter (int)

  • sub_problem_max_iter (int)

  • tolerance (float)

  • sub_problem_algo_name (str)

  • sub_problem_algo_settings (Mapping[str, Any])

  • convexity_margin (float)

  • convexification_constant (float)

  • options (Mapping[str, Any])

  • master_algo_name (str)

  • master_algo_settings (Mapping[str, Any])

convexity_margin: float = 100.0#

The convexity margin of the adaptive repair, in the units of the objective.

convexification_constant: float = 100.0#

The constant of the convexification, in the units of the objective.

options: Mapping[str, Any]#

Any other setting of the master, passed through unchanged.

Deprecated since version 0.2.0: Use master_algo_settings, which says which of the two levels it configures. Both may be given, in which case master_algo_settings wins.

master_algo_name: str = 'BILEVEL_MASTER_OUTER_APPROXIMATION'#

The name of the algorithm solving the master problem.

Any GEMSEO algorithm deciding the next box. The default is the outer approximation the method is built on, whose settings this class translates; another master is configured through master_algo_settings, under the names it declares. Changing it changes the method, not merely its tuning.

master_algo_settings: Mapping[str, Any]#

Any other setting of the master, passed through unchanged.

These win over the settings this class translates, so a setting named both here and by the class takes the value given here. For a master that is not an outer approximation, this is the whole of its configuration.

property convexity_value: float#

The value of the convexity setting the mechanism calibrates.

to_master_settings(radius=None)[source]#

Return the settings of the master problem.

For an outer approximation, the settings this class names are translated into the master’s own terms, the mechanism deciding which of the two constants is passed and which is switched off, so that the two can never be active at once. For any other master, none of them is: it is driven by master_algo_settings alone. max_iter and ub_tol are sent under those names or not at all, so a master calling its iteration count or its tolerance anything else takes them from master_algo_settings like the rest.

Parameters:

radius (int | None) – The radius of the trust region, overriding trust_region_radius. Used by the encodings whose distance counts something other than design variables.

Returns:

The settings of the master problem.

Return type:

dict[str, Any]

class gemseo_box_subdivision.settings.SweptBoxSubdivisionSettings(*, mechanism='adaptive', trust_region_radius=2, n_parallel_points=4, max_iter=80, sub_problem_max_iter=40, tolerance=0.0001, sub_problem_algo_name='SLSQP', sub_problem_algo_settings=<factory>, max_value=0.0)[source]#

Bases: BaseBoxSubdivisionSettings

The settings of a run that sweeps the convexity instead of choosing it.

The convexity is the one quantity of this method with no value that transfers between problems, and this is the entry point that does not ask for one. The master probes a ladder of convexity values across the parallel points it already spends on trust-region radii, the low rungs proposing the box next door and the high rungs the box across the design space, and redeploys a rung higher every probe that proposes nothing new.

The sweep is the master’s, so this names no master: it drives the one that implements it, under its own settings convexity_sweep_points and convexity_sweep_max. There is nothing to pass through either, since the parameters of the master are chosen here rather than supplied:

n_parallel_points

the probes and the rungs. A probe per rung is the whole construction, so the two are one number rather than two that can disagree; a single probe is given the top rung, the conservative end, since there is no ladder to span.

max_value

the top of the ladder, or zero to read it off the objective as the run observes it, lifted by a decade of headroom.

What is not here is as much of the point as what is: no convexity margin, no convexification constant, and no master to name. Those live on BoxSubdivisionSettings, which is the general construction, and supplying one alongside a sweep is the contradiction this split removes.

Parameters:
  • mechanism (str)

  • trust_region_radius (int)

  • n_parallel_points (int)

  • max_iter (int)

  • sub_problem_max_iter (int)

  • tolerance (float)

  • sub_problem_algo_name (str)

  • sub_problem_algo_settings (Mapping[str, Any])

  • max_value (float)

max_value: float = 0.0#

The upper bound of the ladder, or zero to read it off the objective.

Zero asks the master for the bound the run observes, the spread of the objective over the boxes already solved, lifted by HEADROOM. That spread is a lower estimate of the spread over the design space, and reading it literally is circular, which is what the headroom answers.

property master_algo_name: str#

The name of the algorithm solving the master problem.

The sweep is implemented in the master, so this entry point drives that master rather than one of the caller’s choosing. A master to name, and settings to pass it, are what BoxSubdivisionSettings is for.

property convexity_value: float | None#

The value the mechanism takes before the ladder is there.

The top rung of a bounded sweep, which is what the master uses in the first iterations, before it has solved enough boxes to have a ladder at all, and what a master predating the sweep uses throughout. The tuning says an over-large margin costs sub-problems rather than quality, so the conservative end is where a lone value belongs.

An unbounded sweep has no such value: its bound is the spread of the objective, which the run has not measured yet, so the master keeps whatever it uses by default until create_sweep() can compute one.

drive_the_master()[source]#

Sweep the convexity from outside a master that does not sweep.

Where MASTER_SWEEPS_CONVEXITY is True the master varies the setting itself and this yields as the base does. Where it is False, leaving the master to its own value would leave it at zero, which is a run with its cuts unguarded rather than a run without a sweep, so the ladder is driven around its mixed-integer solve instead; see _convexity_sweep_driver.

Yields:

Nothing.

Return type:

Iterator[None]

master_settings_the_run_needs()[source]#

Return the sweep, which a run configuring its own master still needs.

The sweep is what this entry point is, so a swept run reaching the master without it solves at the master’s own convexity, which is zero: the sweep is what the run needs of its master rather than one of the settings a caller overrides. Where the master does not sweep there is nothing to pass, drive_the_master() applying the ladder around its solves instead; where it does, the two settings carrying the sweep are what must reach it whichever way the run is executed.

Returns:

The settings a caller configuring the master does not override.

Return type:

dict[str, Any]

create_sweep(observed_scale=0.0, n_points=0)[source]#

Return the ladder this run asks for.

This is the only place a ladder is built, so that the rungs are never counted twice: the driver passes the probes the master actually runs, which is what a probe per rung means, and a caller with no master to ask gets the probes this run would request of one.

Parameters:
  • observed_scale (float) – The variation of the objective over the boxes already solved, which is the upper bound of a sweep given none. See objective_scale().

  • n_points (int) – The rungs, which are the probes of the master. Zero takes n_parallel_points, the number this run asks a master for, which is what a master left to these settings runs.

Returns:

The ladder, or None when its upper bound is neither given nor observed yet.

Return type:

ConvexitySweep | None

to_master_settings(radius=None)[source]#

Return the settings of the master problem.

The sweep is asked for under the master’s own two settings, the rungs being the parallel points. Against a master predating the sweep, see MASTER_SWEEPS_CONVEXITY, those two are not declared and are not passed. A bounded sweep is then left with its top rung, the conservative end, so it loses the cheap rungs rather than the result; an unbounded one has no rung to be left with and gives the master nothing rather than a guard of zero, as convexity_value describes.

Parameters:

radius (int | None) – The radius of the trust region, overriding trust_region_radius. Used by the encodings whose distance counts something other than design variables.

Returns:

The settings of the master problem.

Return type:

dict[str, Any]