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.

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