Global minimization of a non-expensive scalar function with discrete parameters

Driver:

CMAES

Download script:

cma_es.py

The target of the study is to minimize a scalar function. The scalar function is assumed to be inexpensive to evaluate (i.e. evaluation time shorter than a second) and to have no known derivatives. In this case a heuristic global optimization is advisable. CMA-ES is an evolutionary optimization algorithm that draws population members from a multivariate Gaussian distribution.

As an example, a 3D Rastrigin-like function on a mixed circular and discrete domain is minimized,

\[ \begin{align}\begin{aligned}&\text{min.}\,& f(x_1,x_2,x_3) = 3\cdot10 + \sum_{i=1}^3 \left(x_i^2 - 10\cos(2\pi x_i)\right)\\&\text{s.t.}\,& \sqrt{x_1^2 + x_2^2} \leq 1.5 \wedge x_3 \in \{-1,0,1\}.\end{aligned}\end{align} \]
 1import sys,os
 2import numpy as np
 3import time
 4
 5jcm_optimizer_path = r"<JCM_OPTIMIZER_PATH>"
 6sys.path.insert(0, os.path.join(jcm_optimizer_path, "interface", "python"))
 7from jcmoptimizer import Server, Client, Study, Observation
 8server = Server()
 9client = Client(server.host)
10
11# Definition of the search domain
12design_space = [
13    {'name': 'x1', 'type': 'continuous', 'domain': (-1.5,1.5)}, 
14    {'name': 'x2', 'type': 'continuous', 'domain': (-1.5,1.5)},
15    {'name': 'x3', 'type': 'discrete', 'domain': (1.0,0.0,1.0)},
16]
17
18# Definition of fixed environment parameter
19environment = [
20    {'name': 'radius', 'type': 'fixed', 'domain': 1.5},
21]
22
23# Definition of a constraint on the search domain
24constraints = [
25    {'name': 'circle', 'expression': 'sqrt(x1^2 + x2^2) <= radius'}
26]
27
28# Creation of the study object with study_id 'cma_es'
29study = client.create_study(
30    design_space=design_space,
31    environment=environment,
32    constraints=constraints,
33    driver="CMAES",
34    name="Global minimization of a non-expensive scalar function with discrete parameters",
35    study_id="cma_es"
36)
37# Configure study parameters
38study.configure(max_iter=80, num_parallel=2)
39
40# Evaluation of the black-box function for specified design parameters
41def evaluate(study: Study, x1: float, x2: float, x3: float, radius: float) -> Observation:
42
43    observation = study.new_observation()
44    observation.add(10*3
45                + (x1**2-10*np.cos(2*np.pi*x1)) 
46                + (x2**2-10*np.cos(2*np.pi*x2))
47                + (x3**2-10*np.cos(2*np.pi*x3))
48            )return observation
49
50# Run the minimization
51study.set_evaluator(evaluate)
52study.run()
53best = study.driver.best_sample
54print(f"Best sample at: x1={best['x1']:.3f}, x2={best['x2']:.3f}, x3={best['x3']:.1f}")