Basic local search

This commit is contained in:
Peter Vacho 2025-03-11 01:04:14 +01:00
parent bcf22269e8
commit 6cb016d5e9
Signed by: school
GPG key ID: 8CFC3837052871B4
2 changed files with 104 additions and 1 deletions

View file

@ -1,6 +1,65 @@
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
def main() -> None:
"""Program entrypoint."""
print("Hello from task1!")
dims = 3
iters = 100
neighbors = 10
stddev = 1
for func_name, function in available_functions.items():
print(func_name)
for x in search(function, function.definition_interval.random_point(dims), iters, neighbors, stddev):
y = function(x)
print(f"{x} -> {y}")
if __name__ == "__main__":

44
src/utils.py Normal file
View file

@ -0,0 +1,44 @@
import numpy as np
from src.function import Interval
from src.types import INPUT_VECTOR
def generate_bounded_points(
center: INPUT_VECTOR,
num_points: int,
std_dev: float,
interval: Interval,
rng: np.random.Generator | None = None,
) -> list[INPUT_VECTOR]:
"""Generate `num_points` random points around `center`, following a normal distribution.
Args:
center: The N-dimensional center point.
num_points: Number of points to generate.
std_dev: Standard deviation for the normal distribution.
interval: Interval, determining the min/max values for each dimension.
rng: Random generator instance (None for a new rng)
Returns:
A list of generated points.
"""
dimensions = center.shape[0]
valid_points: list[INPUT_VECTOR] = []
if rng is None:
rng = np.random.default_rng()
while len(valid_points) < num_points:
# Generate a batch of points (larger batch to reduce iterations)
batch_size = (num_points - len(valid_points)) * 2 # Oversampling to reduce loop iterations
candidates = rng.normal(loc=center, scale=std_dev, size=(batch_size, dimensions))
# Keep only those within bounds
mask = (candidates >= interval.min) & (candidates <= interval.max)
valid_candidates = candidates[np.all(mask, axis=1)]
# Add valid points to the list
valid_points.extend(valid_candidates[: num_points - len(valid_points)])
return valid_points