Memory and Warm Starts

GFO supports caching evaluations and continuing from previous results.

Memory Caching

Enable caching to avoid re-evaluating the same positions:

import numpy as np
from gradient_free_optimizers import HillClimbingOptimizer

evaluation_count = 0

def expensive_objective(para):
    global evaluation_count
    evaluation_count += 1
    return -(para["x"]**2 + para["y"]**2)

search_space = {
    "x": np.linspace(-10, 10, 20),  # Coarse grid
    "y": np.linspace(-10, 10, 20),
}

# With memory=True, duplicate positions aren't re-evaluated
opt = HillClimbingOptimizer(search_space)
opt.search(expensive_objective, n_iter=500, memory=True)

print(f"Iterations: 500")
print(f"Actual evaluations: {evaluation_count}")
print(f"Cache hits: {500 - evaluation_count}")

Warm Start from Previous Run

Continue optimization from a previous run’s results:

import numpy as np
from gradient_free_optimizers import BayesianOptimizer

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

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

# First optimization run
opt1 = BayesianOptimizer(search_space, random_state=42)
opt1.search(objective, n_iter=25)
print(f"Run 1: best = {opt1.best_score:.4f}")

# Get the search data from first run
previous_data = opt1.search_data

# Second run, starting from previous results
opt2 = BayesianOptimizer(search_space, random_state=42)
opt2.search(
    objective,
    n_iter=25,
    memory_warm_start=previous_data,  # Pass previous results
)
print(f"Run 2: best = {opt2.best_score:.4f}")

# The second run benefits from knowledge of the first run

Warm Start with Specific Points

Start from known good configurations:

import numpy as np
from gradient_free_optimizers import HillClimbingOptimizer

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

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

# Initialize with specific starting points
opt = HillClimbingOptimizer(
    search_space,
    initialize={
        "warm_start": [
            {"x": 1.0, "y": 1.0},   # Known good region
            {"x": -0.5, "y": 0.5},  # Another starting point
        ],
        "random": 2,  # Plus some random exploration
    }
)

opt.search(objective, n_iter=100)
print(f"Best: {opt.best_para}, Score: {opt.best_score}")

Resuming After Interruption

Save and restore optimization state:

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

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

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

# First session
opt1 = BayesianOptimizer(search_space)
opt1.search(objective, n_iter=20)

# Save results
opt1.search_data.to_csv("optimization_checkpoint.csv", index=False)
print(f"Session 1 complete: {opt1.best_score:.4f}")

# Later session - resume from saved results
saved_data = pd.read_csv("optimization_checkpoint.csv")

opt2 = BayesianOptimizer(search_space)
opt2.search(
    objective,
    n_iter=30,
    memory_warm_start=saved_data,
)
print(f"Session 2 complete: {opt2.best_score:.4f}")

Combining Multiple Search Histories

import numpy as np
import pandas as pd
from gradient_free_optimizers import (
    RandomSearchOptimizer,
    BayesianOptimizer,
)

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

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

# Run random search for exploration
opt_random = RandomSearchOptimizer(search_space)
opt_random.search(objective, n_iter=50)

# Continue with Bayesian optimization
opt_bayes = BayesianOptimizer(search_space)
opt_bayes.search(
    objective,
    n_iter=30,
    memory_warm_start=opt_random.search_data,
)

print(f"Random search best: {opt_random.best_score:.4f}")
print(f"Bayesian (with warm start) best: {opt_bayes.best_score:.4f}")