Source code for moptipy.tests.op1_with_step_size

"""Functions for testing unary search operators with step size."""
from math import isfinite
from typing import Any, Callable, Iterable

from numpy.random import Generator, default_rng
from pycommons.types import type_error

from moptipy.api.operators import Op1WithStepSize, check_op1_with_step_size
from moptipy.api.space import Space
from moptipy.tests.op1 import default_min_unique_samples, validate_op1


[docs] def validate_op1_with_step_size( op1: Op1WithStepSize, search_space: Space | None = None, make_search_space_element_valid: Callable[[Generator, Any], Any] | None = lambda _, x: x, number_of_samples: int = 100, min_unique_samples: int | Callable[[int, Space], int] = default_min_unique_samples, step_sizes: Iterable[float] = (), get_step_size: Callable[[ Space, Any, Any], float | None] | None = None) -> None: """ Check whether an object is a valid moptipy unary operator with step size. :param op1: the operator :param search_space: the search space :param make_search_space_element_valid: make a point in the search space valid :param number_of_samples: the number of times to invoke the operator :param min_unique_samples: a lambda for computing the number :param step_sizes: the step sizes to test :param get_step_size: try to get the step size difference from two space elements :raises ValueError: if `op1` is not a valid instance of :class:`~moptipy.api.operators.Op1` :raises TypeError: if incorrect types are encountered """ if not isinstance(op1, Op1WithStepSize): raise type_error(op1, "op1", Op1WithStepSize) if op1.__class__ == Op1WithStepSize: raise ValueError("Cannot use abstract base Op1WithStepSize directly.") check_op1_with_step_size(op1) if not isinstance(step_sizes, Iterable): raise type_error(step_sizes, "step_sizes", Iterable) if (get_step_size is not None) and (not callable(get_step_size)): raise type_error(get_step_size, "get_step_size", None, call=True) validate_op1(op1, search_space, make_search_space_element_valid, number_of_samples, min_unique_samples) random: Generator | None = None x: Any = None x_copy: Any = None dest: Any = None if search_space is not None: if random is None: random = default_rng() x = search_space.create() if x is None: raise ValueError( f"search_space.create()=None for {search_space}") x_copy = search_space.create() if x_copy is None: raise ValueError( f"search_space.create()=None for {search_space}") dest = search_space.create() if dest is None: raise ValueError( f"search_space.create()=None for {search_space}") for i, step_size in enumerate(step_sizes): if not isinstance(step_size, float): raise type_error(step_size, f"step_sizes[{i}]", float) if not (isfinite(step_size) and (0 <= step_size <= 1)): raise ValueError(f"Forbidden step_sizes[{i}]={step_size}.") if (random is not None) and (search_space is not None): if make_search_space_element_valid is not None: x = make_search_space_element_valid(random, x) if x is None: raise ValueError( "make_search_space_element_valid(search_space.create())=" f"None for {search_space}") search_space.validate(x) search_space.copy(x_copy, x) if not search_space.is_equal(x_copy, x): raise ValueError( f"error when copying {x}, got {x_copy} on {search_space}") op1.op1(random, dest, x, step_size) search_space.validate(dest) if search_space.is_equal(dest, x_copy): raise ValueError( f"operator copies source for step_size={step_size} " f"on {search_space} and {dest}") if not search_space.is_equal(x, x_copy): raise ValueError( f"operator modifies source for step_size={step_size} " f"on {search_space}") if get_step_size is not None: found_step_size: float | None = \ get_step_size(search_space, x, dest) if found_step_size is None: continue if not (isfinite(found_step_size) and (0 <= found_step_size <= 1)): raise ValueError( f"invalid detected step size {found_step_size} " f"for {search_space}.") if found_step_size != step_size: raise ValueError( f"step_size={step_size} but {op1} actually " f"performs step of size {found_step_size} for " f"x={x} and returns {dest} for {search_space}.")