gemseo_box_subdivision.convexity_sweep#

Asking the master to sweep the convexity, rather than calibrating a value.

The convexity margin min_dfk and the convexification constant are absolute quantities in the units of the objective, and annex C says neither transfers between problems: a margin of \(100\) reaches the optimum from every starting point on Rastrigin, which spans about eighty, and is the worst of those tried on Ackley, which spans about twenty-two. Asking the user for a number calibrated against a quantity they do not know is the standing criticism of the method.

The answer is to stop choosing. The master already probes one trust-region radius per parallel point, over geomspace(step / 2, step): the parallel points are a sweep and not a batch. The same probes can carry a ladder of convexity values, the low rungs proposing the box next door and the high rungs the box across the design space, with every probe that proposes nothing new redeployed a rung higher. The user then supplies an upper bound and a number of points, or nothing at all.

That loop belongs to the master, and it is implemented there, in gemseo_bilevel_outer_approximation.algos.opt.core.convexity_sweep and the probe loop beside it, under the settings convexity_sweep_points and convexity_sweep_max. What this module holds is what is this package’s own:

  • the translation of what a run asks for into those two settings of the master, which SweptBoxSubdivisionSettings.to_master_settings() does: the upper bound as it is given, and the rungs from the parallel points;

  • the ladder itself, re-exported from the master, so that the rest of the package has one name for it wherever it lives.

MASTER_SWEEPS_CONVEXITY says which master is installed. Against one that predates the sweep, the settings above are not accepted and the ladder is not there to import: the package then falls back to its own _convexity_sweep_fallback copy of the ladder, and to _convexity_sweep_driver, which sweeps that ladder around the solves of the released master. Both are temporary, and both go when the master ships the sweep.

Module Attributes

MASTER_SWEEPS_CONVEXITY

Whether the installed master sweeps the convexity on its own.

gemseo_box_subdivision.convexity_sweep.MASTER_SWEEPS_CONVEXITY = False#

Whether the installed master sweeps the convexity on its own.

class gemseo_box_subdivision.convexity_sweep.ConvexitySweep(ladder)[source]#

Bases: object

A ladder of convexity values, and the rungs each probe deploys.

This is a policy rather than an optimizer: it says which value the master should be given next, and knows nothing of the master itself.

Parameters:

ladder (tuple[float, ...])

ladder: tuple[float, ...]#

The rungs of the ladder, in increasing order.

classmethod from_bounds(max_value, n_points, decades=2.0)[source]#

Return the sweep of an upper bound and a number of points.

Parameters:
  • max_value (float) – The upper bound of the sweep, in the units of the objective.

  • n_points (int) – The number of rungs.

  • decades (float) – The number of decades the ladder spans below max_value.

Returns:

The sweep.

Return type:

ConvexitySweep

property n_points: int#

The number of rungs, hence of probes.

property max_value: float#

The upper bound of the sweep, the top rung.

rungs(index)[source]#

Return the rungs a probe deploys, in the order it deploys them.

A probe starts at the rung of its own index, which is what makes an iteration span the ladder, and climbs from there. The probe at the top rung has nowhere to climb, so a box it does not propose is a box no probe will propose.

Parameters:

index (int) – The index of the probe, from zero. An index past the ladder is clamped to its top rung, the conservative end, which is where a probe with no rung of its own belongs.

Returns:

The rungs, the probe’s own first.

Return type:

tuple[float, …]

probe_index(n_probes, probe)[source]#

Return the rung a probe starts from.

The probes and the rungs need not be equal in number: the probes spread over the ladder, so that the tight trust region and the raw cuts explore together and the wide region and the dominated cuts do too.

A master with nowhere to spread its probes gets the top rung. A lone probe at the bottom is a run with its cuts unguarded, which converges after two or three sub-problems and reports success far from the optimum, and a single value belongs at the conservative end of the ladder.

Parameters:
  • n_probes (int) – The number of parallel points of the master.

  • probe (int) – The index of the probe, from zero.

Returns:

The index of the rung the probe starts from.

Return type:

int

gemseo_box_subdivision.convexity_sweep.convexity_ladder(max_value, n_points, decades=2.0)[source]#

Return the rungs of a convexity ladder.

The rungs are geometric from $kappa_{max} / 10^{text{decades}}$ up to $kappa_{max}$, the upper bound being the last one, so that a ladder of a single point is the single value it would replace.

Parameters:
  • max_value (float) – The upper bound of the sweep, in the units of the objective.

  • n_points (int) – The number of rungs.

  • decades (float) – The number of decades the ladder spans below max_value.

Returns:

The rungs, in increasing order.

Raises:

ValueError – When the upper bound is not positive, when the number of rungs is not positive, or when the span is not positive while more than one rung is asked for.

Return type:

tuple[float, …]

gemseo_box_subdivision.convexity_sweep.objective_scale(values)[source]#

Return the variation of the objective over the values observed so far.

Both mechanisms are calibrated against the same quantity, the non-convexity they have to dominate, whose order is the variation of the objective over the design space. The master observes that variation as it solves sub-problems, so the upper bound of a sweep needs no user at all, once corrected by the headroom that a partial observation calls for.

Parameters:

values (Iterable[float]) – The objective values of the sub-problems already solved.

Returns:

The spread of the finite values among them, or 0.0 when fewer than two of them are finite, which is not yet a scale.

Return type:

float