Source code for cann.optim.differential_evolution
from typing import Callable, Tuple, Dict, Any, List
import numpy as np
from scipy.optimize import differential_evolution
from cann.engine.calibration_config import CalibrationConfig
[docs]
def minimize_global(
objective_fn: Callable[[np.ndarray], np.ndarray],
bounds: List[Tuple[float, float]],
config: CalibrationConfig,
verbose: bool = False,
) -> Dict[str, Any]:
"""
Global minimization using Differential Evolution.
Args:
objective_fn: Vectorized objective function.
SciPy will call this with an array of shape ``(n_params, n_candidates)`` and expects
a 1D array of shape ``(n_candidates,)``.
bounds: List of ``(min, max)`` pairs for each parameter.
config: Calibration configuration.
verbose: Whether to print optimization progress.
Returns:
Dict[str, Any]: Optimization results compatible with :class:`~cann.engine.calibration_config.CalibrationResult`.
Raises:
ValueError: If SciPy rejects the provided configuration.
"""
vectorized_objective_evaluations = 0
candidate_function_evaluations = 0
def _counted_objective_fn(x: np.ndarray) -> np.ndarray:
nonlocal vectorized_objective_evaluations, candidate_function_evaluations
x_arr = np.asarray(x)
vectorized_objective_evaluations += 1
candidate_function_evaluations += 1 if x_arr.ndim <= 1 else int(x_arr.shape[1])
return objective_fn(x)
def _de_callback_func(xk, convergence):
nonlocal iteration_counter
iteration_counter += 1
if iteration_counter % 10 == 0:
print(
f"Iteration {iteration_counter}, best value: {objective_fn(xk.reshape(-1,1)).min()}, Current parameters: {xk}, Convergence: {convergence}"
)
iteration_counter = 0
result = differential_evolution(
func=_counted_objective_fn,
bounds=bounds,
strategy=config.strategy,
maxiter=config.max_iter,
popsize=config.pop_size,
tol=config.tol,
mutation=config.mutation,
recombination=config.recombination,
rng=np.random.default_rng(config.seed if config.seed is not None else 42),
polish=config.polish,
vectorized=True,
updating="deferred",
init=config.init,
disp=False,
callback=_de_callback_func if verbose else None,
)
return {
"x": result.x,
"fun": result.fun,
"nfev": result.nfev,
"vectorized_nfev": vectorized_objective_evaluations,
"candidate_nfev": candidate_function_evaluations,
"nit": result.nit,
"success": result.success,
"message": result.message,
}