Optimization of resonant system based on Gaussian fit
- Driver:
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'));