moptipy.operators package

In this package, we provide implementations for basic search operators.

Subpackages

Submodules

moptipy.operators.op0_forward module

A nullary operator forwarding to another function.

This is a nullary operator (an instance of Op0) whose method op0() forwards to another Callable. This other Callable can then return a solution that is created in some special way, or maybe even the current best solution of a search process.

This operator has been designed to be used in conjunction with from_starting_point(), which is an optimization Process where a starting point has been defined, i.e., where the methods get_copy_of_best_x() and get_best_f() return pre-defined values. By setting forward_to() to get_copy_of_best_x(), this nullary operator will return the current-best solution of the optimization process, which, in this case, will be the pre-defined starting point. Any optimization algorithm (e.g., an instance of Algorithm0) using this nullary operator to get its initial solution will then begin the search at this pre-defined starting point. This allows using one algorithm as a sub-algorithm of another one. Wrapping from_starting_point() around the result of a call to for_fes() would allow to limit the number of objective function evaluations consumed by the sub-algorithm.

class moptipy.operators.op0_forward.Op0Forward[source]

Bases: Op0

A nullary operator that forwards all calls to op0 to a Callable.

forward_to(call)[source]

Set the Callable to forward all calls from op0() to.

Parameters:

call (Callable[[Any], None]) – the Callable to which all calls to op0() should be delegated to.

Return type:

None

initialize()[source]

Initialize this operator by stopping to forward.

Return type:

None

op0(random, dest)[source]

Forward the call.

Parameters:
  • random (Generator) – ignored

  • dest (ndarray) – the destination data structure to be filled with the data of the point in the search space by the internal Callable set by forward_to().

Return type:

None

stop_forwarding()[source]

Stop forwarding the call.

Return type:

None

moptipy.operators.tools module

Some tools for optimization.

moptipy.operators.tools.exponential_step_size(step_size, min_steps, max_steps)[source]

Translate a step size in [0,1] to an integer in min_steps..max_steps.

The idea is that we would like to have step-size dependent operators of type Op1WithStepSize. These operators allow algorithms to tune the amount of change to be applied to a solution between 0 and 1 (inclusively). As described in the documentation of op1(), 0 means that the smallest possible change should be applied and 1 means that the largest possible change should be applied.

Now for many search spaces, we need to translate this step size from [0,1] to an integer. For instance, if we have n-dimensional bitstrings as search space, then we can flip anything between 1 and n bits. Straightforwardly, one would linearly scale the step size from [0,1] to 1..n. Unfortunately, if we do that, then different values of step_size will have very different meaning depending on n. For example, step_size=0.05 would translate to round(1 + step_size * (n-1)) = 1 bits to be flipped if n=10, to 6 bit flips for n=100, and to 501 bit flips for n=10_000. While flipping one bit is a very small move and flipping six bits may be a medium-size move in discrete optimization, flipping over 500 bits is actually always quite a lot.

What we would like is to have scale-independent small moves but still be able to make large moves. We can get this by exponentially transforming the step_width. Most step_size values will result in small integer steps and only step_size values close to 1 will yield really big results.

And for a step_size=0.05, we get for different n:

>>> exponential_step_size(0.05, 1, 10)
1
>>> exponential_step_size(0.05, 1, 100)
1
>>> exponential_step_size(0.05, 1, 10_000)
2
>>> exponential_step_size(0.05, 1, 1_000_000)
2
>>> exponential_step_size(0.05, 1, 1_000_000_000)
3
>>> exponential_step_size(0.05, 1, 1_000_000_000_000)
4

For different values of step_size and a fixed n, we can still obtain the whole spectrum of possible changes. For n=10, for example, we get:

>>> exponential_step_size(0.0, 1, 10)
1
>>> exponential_step_size(0.1, 1, 10)
1
>>> exponential_step_size(0.2, 1, 10)
2
>>> exponential_step_size(0.3, 1, 10)
2
>>> exponential_step_size(0.4, 1, 10)
3
>>> exponential_step_size(0.5, 1, 10)
3
>>> exponential_step_size(0.6, 1, 10)
4
>>> exponential_step_size(0.7, 1, 10)
5
>>> exponential_step_size(0.8, 1, 10)
6
>>> exponential_step_size(0.85, 1, 10)
7
>>> exponential_step_size(0.9, 1, 10)
8
>>> exponential_step_size(0.95, 1, 10)
9
>>> exponential_step_size(1.0, 1, 10)
10

So we can still reach the whole range of possible steps from 1 to n.

>>> isinstance(exponential_step_size(0.5, 1, 9), int)
True
>>> exponential_step_size(0.0, 1, 100)
1
>>> exponential_step_size(1.0, 1, 100)
100
>>> exponential_step_size(1.0 / 3.0, 1, 10)
2
>>> exponential_step_size(1.0 / 3.0, 1, 100)
5
>>> exponential_step_size(1.0 / 3.0, 1, 10_000)
22
>>> exponential_step_size(0.0, 2, 10)
2
>>> exponential_step_size(0.0, 9, 10)
9
>>> exponential_step_size(0.0, 10, 10)
10
>>> exponential_step_size(1.0, 10, 10)
10
Parameters:
  • step_size (float) – the step size from [0,1] to be transformed to an integer

  • min_steps (int) – the minimum (inclusive) value for the returned integer

  • max_steps (int) – the maximum (inclusive) value for the returned integer

Return type:

int

moptipy.operators.tools.inv_exponential_step_size(int_val, min_steps, max_steps)[source]

Compute the inverse of exponential_step_size().

This routine exists mainly to make testing easier.

Parameters:
  • int_val (int) – the return value of exponential_step_size().

  • min_steps (int) – the minimum (inclusive) value any int_val

  • max_steps (int) – the maximum (inclusive) value any int_val

Return type:

float

>>> exponential_step_size(0.47712125471966244, 1, 10)
3
>>> inv_exponential_step_size(3, 1, 10)
0.47712125471966244
>>> inv_exponential_step_size(1, 1, 10)
0.0
>>> inv_exponential_step_size(10, 1, 10)
1.0
>>> inv_exponential_step_size(33, 6, 673)
0.5123088678224029
>>> exponential_step_size(0.5123088678224029, 6, 673)
33
>>> inv_exponential_step_size(3, 3, 3)
1.0
>>> inv_exponential_step_size(3, 3, 10)
0.0
>>> inv_exponential_step_size(10, 3, 10)
1.0