Simple Optimization

This example demonstrates the basic usage of Gradient-Free-Optimizers on a simple 2D function.

Minimizing a Quadratic Function

Let’s find the minimum of the sphere function: f(x, y) = x^2 + y^2

import numpy as np
from gradient_free_optimizers import HillClimbingOptimizer

# Define the objective function
# GFO maximizes by default, so we negate for minimization
def sphere(para):
    x = para["x"]
    y = para["y"]
    return -(x**2 + y**2)

# Define a simple two-dimensional search space
search_space = {
    "x": np.linspace(-10, 10, 100),  # 100 values from -10 to 10
    "y": np.linspace(-10, 10, 100),
}

# Create optimizer and run search
opt = HillClimbingOptimizer(search_space)
opt.search(sphere, n_iter=500)

# Results
print(f"Best parameters: {opt.best_para}")
print(f"Best score: {opt.best_score}")
print(f"Evaluations: {len(opt.search_data)}")

Expected output:

Best parameters: {'x': 0.0, 'y': 0.0}
Best score: -0.0
Evaluations: 500

Multi-Modal Function (Rastrigin)

The Rastrigin function has many local optima, making it harder to optimize:

import numpy as np
from gradient_free_optimizers import SimulatedAnnealingOptimizer

def rastrigin(para):
    x = para["x"]
    y = para["y"]
    A = 10
    return -(A * 2 + (x**2 - A * np.cos(2 * np.pi * x))
                  + (y**2 - A * np.cos(2 * np.pi * y)))

search_space = {
    "x": np.linspace(-5.12, 5.12, 200),
    "y": np.linspace(-5.12, 5.12, 200),
}

# Simulated Annealing can escape local optima
opt = SimulatedAnnealingOptimizer(
    search_space,
    annealing_rate=0.98,
    start_temp=1.0,
)
opt.search(rastrigin, n_iter=2000)

print(f"Best: {opt.best_para}")
print(f"Score: {opt.best_score}")
# Global optimum is at (0, 0) with score 0

Higher Dimensions

GFO handles higher-dimensional spaces:

import numpy as np
from gradient_free_optimizers import ParticleSwarmOptimizer

def sphere_nd(para):
    return -sum(para[f"x{i}"]**2 for i in range(5))

# 5-dimensional search space
search_space = {
    f"x{i}": np.linspace(-10, 10, 50)
    for i in range(5)
}

opt = ParticleSwarmOptimizer(search_space, population=20)
opt.search(sphere_nd, n_iter=500)

print(f"Best: {opt.best_para}")
print(f"Score: {opt.best_score}")

Accessing All Results

The search_data attribute contains all evaluations:

import numpy as np
import pandas as pd
from gradient_free_optimizers import RandomSearchOptimizer

def objective(para):
    return -(para["x"]**2 + para["y"]**2)

search_space = {
    "x": np.linspace(-10, 10, 100),
    "y": np.linspace(-10, 10, 100),
}

opt = RandomSearchOptimizer(search_space)
opt.search(objective, n_iter=100)

# Get all evaluations as DataFrame
df = opt.search_data
print(df.head())
print(f"\nBest 5 results:")
print(df.nlargest(5, "score"))