Optional include center search (LS/SHC switch)

This also moves the search function into a separate file.
This commit is contained in:
Peter Vacho 2025-03-16 11:18:58 +01:00
parent 6cb016d5e9
commit 9258805802
Signed by: school
GPG key ID: 8CFC3837052871B4
2 changed files with 68 additions and 48 deletions

View file

@ -1,51 +1,5 @@
from collections.abc import Iterator
import numpy as np
from src.function import Function
from src.functions import available_functions
from src.types import INPUT_VECTOR
from src.utils import generate_bounded_points
def search(
function: Function,
x0: INPUT_VECTOR,
iterations: int,
neighbors_count: int,
std_dev: float,
rng: np.random.Generator | None = None,
) -> Iterator[INPUT_VECTOR]:
"""Search for the minimum value of the function using the local search algorithm.
On each iteration, N neighboring points will be generated around the starting point. These points
will then be evaluated on the function, finding the smallest one. This smallest point will become
the new starting point, repeating until we run out of iterations.
Params:
x0: Starting point (N-dimensional).
iterations: Maximum number of iterations.
include_origin: When searching for the next minimum, should the origin point be checked too?
neighbors_count: The amount of neighbor points.
std_dev: Standard deviation for the normal distribution for neighbor generating.
rng: Random generator instance (None for a new rng).
Yields:
Minimum input vector (x) found so far, yielded from each iteration.
"""
if rng is None:
rng = np.random.default_rng()
x_center = x0
for _ in range(iterations):
y_min = function(x_center)
for point in generate_bounded_points(x_center, neighbors_count, std_dev, function.definition_interval, rng):
y_point = function(point)
if y_point < y_min:
y_min = y_point
x_center = point
yield x_center
from src.search import min_search
def main() -> None:
@ -56,8 +10,15 @@ def main() -> None:
stddev = 1
for func_name, function in available_functions.items():
print("-" * 80)
print(func_name)
for x in search(function, function.definition_interval.random_point(dims), iters, neighbors, stddev):
for x in min_search(
function,
function.definition_interval.random_point(dims),
iterations=iters,
neighbors_count=neighbors,
std_dev=stddev,
):
y = function(x)
print(f"{x} -> {y}")

59
src/search.py Normal file
View file

@ -0,0 +1,59 @@
from collections.abc import Iterator
import numpy as np
from src.function import Function
from src.types import INPUT_VECTOR
from src.utils import generate_bounded_points
def min_search(
function: Function,
x0: INPUT_VECTOR,
*,
iterations: int,
neighbors_count: int,
std_dev: float,
include_center: bool = True,
rng: np.random.Generator | None = None,
) -> Iterator[INPUT_VECTOR]:
"""Search for the minimum value of the function using the Local Search / Stochastic Hill Climber algorithm.
On each iteration, N neighboring points will be generated around the starting point. These points
will then be evaluated on the function, finding the smallest one. This smallest point will become
the new starting point, repeating until we run out of iterations.
Params:
x0: Starting point (N-dimensional).
iterations: Maximum number of iterations.
include_origin: When searching for the next minimum, should the origin point be checked too?
neighbors_count: The amount of neighbor points.
std_dev: Standard deviation for the normal distribution for neighbor generating.
include_center:
- When False, this algorithm works as Stochastic Hill Climber, always picking the minimum
from the newly generated points only, ignoring the current point.
- When True, this algorithm works as Local Search, picking the minimum from all of the
newly generated points and also from the center point.
rng: Random generator instance (None for a new rng).
Yields:
Minimum input vector (x) found so far, yielded from each iteration.
"""
if rng is None:
rng = np.random.default_rng()
x_center = x0
y_min = function(x_center) if include_center else float("inf")
for _ in range(iterations):
for point in generate_bounded_points(x_center, neighbors_count, std_dev, function.definition_interval, rng):
y_point = function(point)
if y_point < y_min:
y_min = y_point
x_center = point
# If we're using Stochastic Hill Climber, reset the minimum value, to only
# pick it from the newly generated points, not the current center
if not include_center:
y_min = float("inf")
yield x_center