Optimization of resonant system based on Gaussian fit

Driver:

ActiveLearning

Download script: harmonic_oscillator_fit.m

The target of the study is to tune a resonant system. For simplicity, the resonant system is a harmonic oscillator with eigenfrequency \(\omega_0\) and damping \(\gamma\) driven with force \(F\) at frequency \(\omega\). The amplitude of the oscillator is

\[a_{\rm harm}(\omega) = \frac{F}{\sqrt{(2\omega\omega_0\gamma)^2 + (\omega_0^2 - \omega)^2}} .\]

We assume the system as a black box with unknown resonant behavior. To capture the resonant feature, it is fitted against a Gaussian plus a linear function

\[a_{\rm fit}(\omega) = A \exp\left(-\frac{1}{2}\frac{(\omega-\tau)^2}{\sigma^2}\right) + B\omega + C .\]

The target is to tune \(F, \omega_0, \gamma\) such that the fitted amplitude is \(A = 10\), the resonance frequency is \(\tau = 2\) and the resonance width \(\sigma\) is as small as possible.

 1client = jcmoptimizer.Client(); 
 2
 3%Amplitude of a driven harmonic oscillator. 
 4function a = amplitudes(omegas, F, omega0, gamma)
 5    a = F./sqrt((2*omegas*omega0*gamma).^2 + (omega0^2 - omegas.^2).^2);
 6end
 7
 8% Definition of the search domain to tune the oscillator
 9design_space = { ...
10    struct('name', 'F', 'type', 'continuous', 'domain', [0.1, 40.0]), ... 
11    struct('name', 'omega0', 'type', 'continuous', 'domain', [1.0, 4.0]), ...
12    struct('name', 'gamma', 'type', 'continuous', 'domain', [0.03, 0.3]) ...
13};
14
15%The amplitude is scaned for 10 different driving frequencies
16omegas = linspace(1,3,10);
17
18 % Creation of the study object with study_id 'harmonic_oscillator_fit'
19study = client.create_study( ...
20    'design_space', design_space, ...
21    'driver','ActiveLearning',...
22    'study_name','Optimization of resonant system based on Gaussian fit',...
23    'study_id', 'harmonic_oscillator_fit');
24
25%definition of variables
26
27%The amplitudes are fitted to a Gaussian + linear expression with
28%amplitude A, resonance frequency tau, linear gradient B, and constant offset C.
29%The output are the fitted values and the mean-squared error (MSE).
30fit = struct();
31fit.type = "Fit";
32fit.name = "fit";
33fit.input = "amplitudes";
34fit.expression = "A*exp(-0.5*(omega-tau)^2/sigma^2) + B*omega + C";
35fit.output_names = {"A", "B", "C", "tau", "sigma", "MSE"};
36fit.output_dim = 6;
37fit.model_variables = {"omega"};
38fit.variable_values = omegas.';
39fit.initial_parameters = [1.0, 0.0, 0.0, 1.0, 0.5];
40fit.prior_uncertainties = [10.0, 10.0, 10.0, 10.0, 10.0];
41
42%The expression that defines the loss function:
43expression = struct();
44expression.type = "Expression";
45expression.name = "loss";
46expression.expression = "(A-20)^2 + (tau - 2)^2 + sigma^2 + 0.001*MSE";
47 
48%configuration of study
49study.configure( ...
50    'max_iter', 30, ...
51    'surrogates', { ...
52        ...A single-output Gaussian process learns the dependence of the amplitudes
53        ...for all wavelengths on the design parameters.
54        struct('type', "GP", 'name', "amplitudes", 'output_dim', length(omegas)) ...
55    }, ...
56    'variables', {fit, expression}, ...
57    'objectives', { ...
58        ...The only objective of the study is to minimize the loss.
59        struct("type", "Minimizer", "variable", "loss"), ...
60    }, ...
61    'acquisition_optimizer', struct( ...
62        ...Sample computation based on fits is more expensive. In this case
63        ...advanced sample computation is usually too laborious.
64        'compute_suggestion_in_advance',  false ...
65    ) ...
66);
67
68% Evaluation of the black-box function for specified design parameters
69function observation = evaluate(study, sample)
70
71    pause(2); % make objective expensive
72    observation = study.new_observation();
73    %The harmonic-oscillator amplitude is evaluated for all omega values and the
74    %observed values are used as training input for the Gaussian process "amplitudes"
75    observation.add(amplitudes(omegas, sample.F, sample.omega0, sample.gamma));
76    
77end  
78
79% Run the minimization
80study.set_evaluator(@evaluate);
81study.run(); 
82
83
84% Print result
85best = study.get_state("driver.best_sample");
86fprintf("Best design parameters: F=%f, omega0=%f, gamma=%f\n", ...
87        best.F, best.omega0, best.gamma);
88fprintf(['Objective: (A-20)^2 + (tau - 2)^2 + sigma^2 + 0.001*MSE = ', ...
89         '%.3f\n'], study.get_state('driver.min_objective'));
90client.shutdown_server();