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.

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