LCOV - code coverage report
Current view: top level - include/reporters - GenericActiveLearner.h (source / functions) Hit Total Coverage
Test: idaholab/moose stochastic_tools: #32971 (54bef8) with base c6cf66 Lines: 107 110 97.3 %
Date: 2026-05-29 20:40:35 Functions: 19 23 82.6 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //* This file is part of the MOOSE framework
       2             : //* https://www.mooseframework.org
       3             : //*
       4             : //* All rights reserved, see COPYRIGHT for full restrictions
       5             : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
       6             : //*
       7             : //* Licensed under LGPL 2.1, please see LICENSE for details
       8             : //* https://www.gnu.org/licenses/lgpl-2.1.html
       9             : 
      10             : #pragma once
      11             : 
      12             : #include "GeneralReporter.h"
      13             : #include "GenericActiveLearningSampler.h"
      14             : #include "ActiveLearningGaussianProcess.h"
      15             : #include "GaussianProcess.h"
      16             : #include "SurrogateModel.h"
      17             : #include "SurrogateModelInterface.h"
      18             : #include "GaussianProcessSurrogate.h"
      19             : #include "ParallelAcquisitionFunctionBase.h"
      20             : #include "ParallelAcquisitionInterface.h"
      21             : 
      22             : // forward declarations
      23             : template <typename SamplerType>
      24             : class GenericActiveLearnerTempl;
      25             : 
      26             : typedef GenericActiveLearnerTempl<GenericActiveLearningSampler> GenericActiveLearner;
      27             : 
      28             : /**
      29             :  * A generic reporter to support parallel active learning: re-trains GP and picks the next best
      30             :  * batch
      31             :  */
      32             : template <typename SamplerType>
      33             : class GenericActiveLearnerTempl : public GeneralReporter,
      34             :                                   public ParallelAcquisitionInterface,
      35             :                                   public SurrogateModelInterface
      36             : 
      37             : {
      38             : public:
      39             :   static InputParameters validParams();
      40             :   GenericActiveLearnerTempl(const InputParameters & parameters);
      41         156 :   virtual void initialize() override {}
      42         156 :   virtual void finalize() override {}
      43             :   virtual void execute() override;
      44             : 
      45             : protected:
      46             :   /**
      47             :    * Sets up the training data for the GP model
      48             :    * @param data_out The data vector containing the outputs to train the GP
      49             :    * @param data_in The data matrix containing the inputs to train the GP
      50             :    */
      51             :   virtual void setupGPData(const std::vector<Real> & data_out, const DenseMatrix<Real> & data_in);
      52             : 
      53             :   /**
      54             :    * Computes the outputs of the trained GP model
      55             :    * @param eval_outputs The outputs predicted by the GP model
      56             :    */
      57             :   virtual void computeGPOutput(std::vector<Real> & eval_outputs);
      58             : 
      59             :   /**
      60             :    * Computes the convergence value during active learning
      61             :    */
      62             :   virtual Real computeConvergenceValue();
      63             : 
      64             :   /**
      65             :    * Evaluate the GP on all the test samples sent by the Sampler
      66             :    */
      67             :   virtual void evaluateGPTest();
      68             : 
      69             :   /**
      70             :     Setup the generic variable for acquisition computation (depends on the objective:
      71             :     optimization, UQ, etc.)
      72             :   */
      73             :   virtual void setupGeneric();
      74             : 
      75             :   /**
      76             :    * Include additional inputs before evaluating the acquisition function.
      77             :    * Has trivial function in base, but can be modified in derived if necessary depending
      78             :    * upon the objective of active learning (i.e., forward UQ, inverse UQ, optimization, etc.)
      79             :    */
      80             :   virtual void includeAdditionalInputs();
      81             : 
      82             :   /**
      83             :    * Output the acquisition function values and ordering of the indices
      84             :    * @param acq_new The computed values of the acquisition function
      85             :    * @param indices The indices ordered according to the acqusition values to be sent to Sampler
      86             :    */
      87             :   virtual void getAcquisition(std::vector<Real> & acq_new, std::vector<unsigned int> & indices);
      88             : 
      89             :   /// The base sampler
      90             :   SamplerType & _al_sampler;
      91             : 
      92             :   /// The input dimension for GP, equal to Sampler columns
      93             :   unsigned int _n_dim;
      94             : 
      95             :   /// Storage for the number of parallel proposals
      96             :   dof_id_type _props;
      97             : 
      98             :   /// Storage for all the proposed samples to test the GP model
      99             :   const std::vector<std::vector<Real>> & _inputs_test;
     100             : 
     101             :   /// Model output value from SubApp
     102             :   const std::vector<Real> & _output_value;
     103             : 
     104             :   /// Modified value of model output by this reporter class
     105             :   std::vector<Real> & _output_comm;
     106             : 
     107             :   /// The selected sample indices to evaluate the subApp
     108             :   std::vector<unsigned int> & _sorted_indices;
     109             : 
     110             :   /// The active learning GP trainer that permits re-training
     111             :   const ActiveLearningGaussianProcess & _al_gp;
     112             : 
     113             :   /// The GP evaluator object that permits re-evaluations
     114             :   const SurrogateModel & _gp_eval;
     115             : 
     116             :   /// Storage for the parallel acquisition object to be utilized
     117             :   ParallelAcquisitionFunctionBase & _acquisition_obj;
     118             : 
     119             :   /// The acquistion function values in the current iteration
     120             :   std::vector<Real> & _acquisition_value;
     121             : 
     122             :   /// For monitoring convergence of active learning
     123             :   Real & _convergence_value;
     124             : 
     125             :   /// Storage for all the modified proposed samples to test the GP model
     126             :   std::vector<std::vector<Real>> _inputs_test_modified;
     127             : 
     128             :   /// Transmit the required inputs to the json file
     129             :   std::vector<std::vector<Real>> & _inputs_required;
     130             : 
     131             :   /// Penalize acquisition to prevent clustering when operating in parallel
     132             :   const bool & _penalize_acquisition;
     133             : 
     134             :   /// Ensure that the MCMC algorithm proceeds in a sequential fashion
     135             :   int _check_step;
     136             : 
     137             :   /// Storage for the GP re-training inputs
     138             :   std::vector<std::vector<Real>> _gp_inputs;
     139             : 
     140             :   /// Storage for the GP re-training outputs
     141             :   std::vector<Real> _gp_outputs;
     142             : 
     143             :   /// Outputs of GP model for the test samples
     144             :   std::vector<Real> _gp_outputs_test;
     145             : 
     146             :   /// Outputs of GP model standard deviation for the test samples
     147             :   std::vector<Real> _gp_std_test;
     148             : 
     149             :   /// Storage for the length scales after the GP training
     150             :   std::vector<Real> _length_scales;
     151             : 
     152             :   /// A generic parameter to be passed to the acquisition function
     153             :   std::vector<Real> _generic;
     154             : 
     155             :   /// The GP outputs from the current iteration before re-training (to evaluate convergence)
     156             :   std::vector<Real> _eval_outputs_current;
     157             : };
     158             : 
     159             : template <typename SamplerType>
     160             : InputParameters
     161          96 : GenericActiveLearnerTempl<SamplerType>::validParams()
     162             : {
     163          96 :   InputParameters params = GeneralReporter::validParams();
     164          96 :   params += ParallelAcquisitionInterface::validParams();
     165          96 :   params.addClassDescription("A generic reporter to support parallel active learning: re-trains GP "
     166             :                              "and picks the next best batch.");
     167         192 :   params.addRequiredParam<ReporterName>("output_value",
     168             :                                         "Value of the model output from the SubApp.");
     169         192 :   params.addParam<ReporterValueName>(
     170             :       "outputs_required",
     171             :       "outputs_required",
     172             :       "Modified value of the model output from this reporter class.");
     173         192 :   params.addRequiredParam<SamplerName>("sampler", "The sampler object.");
     174         192 :   params.addRequiredParam<UserObjectName>("al_gp", "Active learning GP trainer.");
     175         192 :   params.addRequiredParam<UserObjectName>("gp_evaluator", "Evaluator for the trained GP.");
     176         192 :   params.addParam<ReporterValueName>(
     177             :       "sorted_indices",
     178             :       "sorted_indices",
     179             :       "The sorted sample indices in order of importance to evaluate the subApp.");
     180         192 :   params.addParam<ReporterValueName>(
     181             :       "acquisition_function",
     182             :       "acquisition_function",
     183             :       "The values of the acquistion function in the current iteration.");
     184         192 :   params.addParam<ReporterValueName>(
     185             :       "convergence_value", "convergence_value", "Value to measure convergence of active learning.");
     186         192 :   params.addParam<ReporterValueName>(
     187             :       "inputs", "inputs", "Modified value of the model inputs from this reporter class.");
     188         192 :   params.addRequiredParam<UserObjectName>("acquisition", "Name of the acquisition function.");
     189         192 :   params.addParam<bool>(
     190             :       "penalize_acquisition",
     191         192 :       true,
     192             :       "Set true to prevent clustering of the best batch inputs when operating in parallel.");
     193          96 :   return params;
     194           0 : }
     195             : 
     196             : template <typename SamplerType>
     197          48 : GenericActiveLearnerTempl<SamplerType>::GenericActiveLearnerTempl(
     198             :     const InputParameters & parameters)
     199             :   : GeneralReporter(parameters),
     200             :     ParallelAcquisitionInterface(parameters),
     201             :     SurrogateModelInterface(this),
     202          48 :     _al_sampler(getSampler<SamplerType>("sampler")),
     203          48 :     _n_dim(_al_sampler.getNumberOfCols()),
     204          48 :     _props(_al_sampler.getNumParallelProposals()),
     205          48 :     _inputs_test(_al_sampler.getSampleTries()),
     206          48 :     _output_value(getReporterValue<std::vector<Real>>("output_value", REPORTER_MODE_DISTRIBUTED)),
     207          48 :     _output_comm(declareValue<std::vector<Real>>("outputs_required")),
     208          48 :     _sorted_indices(declareValue<std::vector<unsigned int>>("sorted_indices")),
     209          48 :     _al_gp(getUserObject<ActiveLearningGaussianProcess>("al_gp")),
     210          48 :     _gp_eval(getSurrogateModel<GaussianProcessSurrogate>("gp_evaluator")),
     211          96 :     _acquisition_obj(getParallelAcquisitionFunctionByName(getParam<UserObjectName>("acquisition"))),
     212          48 :     _acquisition_value(declareValue<std::vector<Real>>("acquisition_function")),
     213          48 :     _convergence_value(declareValue<Real>("convergence_value")),
     214          48 :     _inputs_required(declareValue<std::vector<std::vector<Real>>>("inputs")),
     215          96 :     _penalize_acquisition(getParam<bool>("penalize_acquisition")),
     216          96 :     _check_step(std::numeric_limits<int>::max())
     217             : {
     218             :   // Setting up the variable sizes to facilitate active learning.
     219          48 :   _gp_outputs_test.resize(_inputs_test.size());
     220          48 :   _gp_std_test.resize(_inputs_test.size());
     221          48 :   _acquisition_value.resize(_props);
     222          48 :   _length_scales.resize(_n_dim);
     223          48 :   _eval_outputs_current.resize(_props);
     224          48 :   _generic.resize(1);
     225          48 :   _inputs_required.resize(_props, std::vector<Real>(_n_dim, 0.0));
     226          48 :   _sorted_indices.resize(_props, 1u);
     227          48 : }
     228             : 
     229             : template <typename SamplerType>
     230             : void
     231          92 : GenericActiveLearnerTempl<SamplerType>::setupGPData(const std::vector<Real> & data_out,
     232             :                                                     const DenseMatrix<Real> & data_in)
     233             : {
     234         552 :   for (unsigned int i = 0; i < data_out.size(); ++i)
     235             :   {
     236        1380 :     for (unsigned int j = 0; j < _n_dim; ++j)
     237         920 :       _inputs_required[i][j] = data_in(i, j);
     238         460 :     _gp_inputs.push_back(_inputs_required[i]);
     239         460 :     _gp_outputs.push_back(data_out[i]);
     240             :   }
     241          92 : }
     242             : 
     243             : template <typename SamplerType>
     244             : void
     245          60 : GenericActiveLearnerTempl<SamplerType>::computeGPOutput(std::vector<Real> & eval_outputs)
     246             : {
     247         360 :   for (unsigned int i = 0; i < eval_outputs.size(); ++i)
     248         300 :     eval_outputs[i] = _gp_eval.evaluate(_gp_inputs[i]);
     249          60 : }
     250             : 
     251             : template <typename SamplerType>
     252             : void
     253         108 : GenericActiveLearnerTempl<SamplerType>::setupGeneric()
     254             : {
     255         108 :   _generic = _gp_outputs;
     256         108 : }
     257             : 
     258             : template <typename SamplerType>
     259             : void
     260          92 : GenericActiveLearnerTempl<SamplerType>::includeAdditionalInputs()
     261             : {
     262          92 :   _inputs_test_modified = _inputs_test;
     263          92 : }
     264             : 
     265             : template <typename SamplerType>
     266             : void
     267         108 : GenericActiveLearnerTempl<SamplerType>::getAcquisition(std::vector<Real> & acq_new,
     268             :                                                        std::vector<unsigned int> & indices)
     269             : {
     270             :   std::vector<Real> acq;
     271         108 :   acq.resize(_inputs_test.size());
     272         108 :   includeAdditionalInputs();
     273         108 :   _acquisition_obj.computeAcquisition(
     274         108 :       acq, _gp_outputs_test, _gp_std_test, _inputs_test_modified, _gp_inputs, _generic);
     275         108 :   acq_new = acq;
     276         108 :   if (_penalize_acquisition)
     277         108 :     _acquisition_obj.penalizeAcquisition(
     278         108 :         acq_new, indices, acq, _length_scales, _inputs_test_modified);
     279         108 : }
     280             : 
     281             : template <typename SamplerType>
     282             : Real
     283          48 : GenericActiveLearnerTempl<SamplerType>::computeConvergenceValue()
     284             : {
     285             :   Real convergence_value = 0.0;
     286         288 :   for (unsigned int ii = 0; ii < _output_comm.size(); ++ii)
     287         240 :     convergence_value += Utility::pow<2>(_output_comm[ii] - _eval_outputs_current[ii]);
     288          48 :   convergence_value = std::sqrt(convergence_value) / _output_comm.size();
     289          48 :   return convergence_value;
     290             : }
     291             : 
     292             : template <typename SamplerType>
     293             : void
     294          92 : GenericActiveLearnerTempl<SamplerType>::evaluateGPTest()
     295             : {
     296       92092 :   for (unsigned int i = 0; i < _gp_outputs_test.size(); ++i)
     297       92000 :     _gp_outputs_test[i] = _gp_eval.evaluate(_inputs_test[i], _gp_std_test[i]);
     298          92 : }
     299             : 
     300             : template <typename SamplerType>
     301             : void
     302         156 : GenericActiveLearnerTempl<SamplerType>::execute()
     303             : {
     304         156 :   if (_al_sampler.getNumberOfLocalRows() == 0 || _check_step == _t_step)
     305             :   {
     306           0 :     _check_step = _t_step;
     307           0 :     return;
     308             :   }
     309             : 
     310         156 :   DenseMatrix<Real> data_in(_al_sampler.getNumberOfRows(), _al_sampler.getNumberOfCols());
     311         886 :   for (dof_id_type ss = _al_sampler.getLocalRowBegin(); ss < _al_sampler.getLocalRowEnd(); ++ss)
     312             :   {
     313         730 :     const auto data = _al_sampler.getNextLocalRow();
     314        2390 :     for (unsigned int j = 0; j < _al_sampler.getNumberOfCols(); ++j)
     315        1660 :       data_in(ss, j) = data[j];
     316             :   }
     317         156 :   _communicator.sum(data_in.get_values());
     318         156 :   _output_comm = _output_value;
     319         156 :   _communicator.allgather(_output_comm);
     320             : 
     321         156 :   if (_t_step > 1)
     322             :   {
     323             :     // Setup the GP training data
     324         108 :     setupGPData(_output_comm, data_in);
     325             : 
     326             :     // Compute the convergence value before re-training the GP
     327         108 :     if (_t_step > 2)
     328             :     {
     329          60 :       computeGPOutput(_eval_outputs_current);
     330          60 :       _convergence_value = computeConvergenceValue();
     331             :     }
     332             : 
     333             :     // Retrain the GP and get the length scales
     334         108 :     _al_gp.reTrain(_gp_inputs, _gp_outputs);
     335         108 :     _length_scales = _al_gp.getLengthScales();
     336             : 
     337             :     // Evaluate the GP on all the test samples sent by the Sampler
     338         108 :     evaluateGPTest();
     339             : 
     340             :     // Setup the generic variable for acquisition computation (depends on the objective:
     341             :     // optimization, UQ, etc.)
     342         108 :     setupGeneric();
     343             : 
     344             :     // Get the acquisition function values and ordering of indices as per the acquisition
     345             :     std::vector<Real> acq_new;
     346             :     std::vector<unsigned int> indices;
     347         108 :     indices.resize(_inputs_test.size());
     348         108 :     getAcquisition(acq_new, indices);
     349             : 
     350             :     // Output the acquisition function values and the best ordering of the indices
     351         108 :     std::copy_n(indices.begin(), _props, _sorted_indices.begin());
     352         108 :     std::copy_n(acq_new.begin(), _props, _acquisition_value.begin());
     353         108 :   }
     354             :   else
     355          48 :     std::iota(_sorted_indices.begin(), _sorted_indices.end(), 0);
     356             : 
     357             :   // Track the current step
     358         156 :   _check_step = _t_step;
     359         156 : }

Generated by: LCOV version 1.14