File: example_dream_02.cpp

package info (click to toggle)
tasmanian 8.2-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 4,852 kB
  • sloc: cpp: 34,523; python: 7,039; f90: 5,080; makefile: 224; sh: 64; ansic: 8
file content (172 lines) | stat: -rw-r--r-- 7,815 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
#include "Tasmanian.hpp"

using namespace std;

/*!
 * \internal
 * \file example_dream_02.cpp
 * \brief Examples for the Tasmanian DREAM module.
 * \author Miroslav Stoyanov
 * \ingroup TasmanianDREAMExamples
 *
 * Tasmanian DREAM Example 2
 * \endinternal
 */

/*!
 * \ingroup TasmanianDREAMExamples
 * \addtogroup TasmanianDREAMExamples2 Tasmanian DREAM module, example 2
 *
 * Example 2:
 * Demonstrates how to solve a Bayesian inference problem for parameter calibration
 * using a sparse grid approximation to the Bayesian likelihood function.
 */

//! \brief DREAM Example 2: perform parameter calibration using likelihood that is approximated with a sparse grid.
//! \ingroup TasmanianDREAMExamples2

//! \snippet DREAM/Examples/example_dream_02.cpp DREAM_Example_02 example
void dream_example_02(){
#ifndef __TASMANIAN_DOXYGEN_SKIP
//! [DREAM_Example_02 example]
#endif
    // using the default random engine, but must reset the random number generator
    srand((int) time(nullptr));

    // Example 2
    cout << "\n" << "---------------------------------------------------------------------------------------------------\n";
    cout << std::scientific; cout.precision(5);
    cout << "EXAMPLE 2: set the inference problem: identify model parameters x_0 and x_1\n"
         << "           from data (noise free example)\n"
         << "           model: f(x) = sin(x_0*M_PI*t + x_1),  data: d = sin(M_PI*t + 0.3*M_PI)\n"
         << "           t in [0,1], t is discretized with 32 equidistant nodes\n"
         << "           the likelihood is exp(- 16 * (f(x) - d)^2)\n"
         << "           using a sparse grid to interpolate the likelihood\n"
         << "     NOTE: 16 = 32/2 corresponds to the discretization error in t\n\n";

    constexpr double pi = 3.14159265358979323846;

    int num_dimensions = 2;
    int num_chains = 100;
    int num_burnup_iterations = 3000;
    int num_sample_iterations = 100;
    // the total number of samples is num_chains * num_iterations = 100 * 100 = 10,000
    int num_discrete_nodes = 32;

    // create a lambda function that represents the model,
    // normally this would be a call to an external code
    auto model = [&](double x0, double x1, std::vector<double> &data)->
        void{
            double dt = 1.0 / ((double) data.size());
            double t = 0.5 * dt;
            for(auto &d : data){
                d = std::sin(x0 * pi * t + x1);
                t += dt;
            }
        };

    // create a lambda function that represents the likelihood
    // the Tasmanian LikelihoodGaussIsotropic class can be used here,
    // but the objective of the example is to use sparse grid interpolant for the likelihood
    // thus we assume that the likelihood formula is potentially much more complex
    auto likelihood = [&](double scale,
                          std::vector<double> &model_values,
                          std::vector<double> &data)->
        double{
            double likelihood_value = 0.0;
            for(size_t j=0; j<model_values.size(); j++){
                likelihood_value += (model_values[j] - data[j]) * (model_values[j] - data[j]);
            }
            return - 0.5 * scale * likelihood_value;
        };

    // generate the data, normally this will come from an external source
    std::vector<double> data(num_discrete_nodes);
    model(1.0, 0.3 * pi, data); // "true" values: x0 = 1.0, x1 = 0.3 * pi

    // construct a sparse grid approximation for the log of the likelihood
    // the sampling will be done in logform
    // for details consult the documentation of the sparse grids module

    // define the domain over which the sparse grid will be constructed
    std::vector<double> domain_a = {0.5, -0.1}, domain_b = {8.0,  1.7};

    auto grid = TasGrid::makeGlobalGrid(num_dimensions, 1, 10, // using 10-th order polynomial
                                        TasGrid::type_iptotal, TasGrid::rule_clenshawcurtis);
    grid.setDomainTransform(domain_a, domain_b);

    TasGrid::loadNeededPoints<TasGrid::mode_sequential>(
        [&](std::vector<double> const &x, std::vector<double> &y, size_t)->void{
            std::vector<double> model_at_point(num_discrete_nodes);
            model(x[0], x[1], model_at_point);
            y[0] = likelihood((double) num_discrete_nodes, model_at_point, data);
        },
        grid, 1);

    TasDREAM::TasmanianDREAM state(num_chains, grid); // get the dimensions from the sparse grid
    std::vector<double> init_chains = TasDREAM::genUniformSamples(domain_a, domain_b, num_chains);
    state.setState(init_chains); // use chains distributed uniformly over the domain

    // Call to Tasmanian DREAM Sampling algorithm
    TasDREAM::SampleDREAM<TasDREAM::logform>
                          (num_burnup_iterations, num_sample_iterations,
                           TasDREAM::posterior(grid, TasDREAM::uniform_prior),
                           grid.getDomainInside(),
                           state,
                           TasDREAM::dist_uniform, 0.5, // independent update of magnitude 0.5
                           TasDREAM::const_percent<90> // use 90% differential update
                           );

    std::vector<double> expectation, variance;
    state.getHistoryMeanVariance(expectation, variance);

    cout << "Inferred values (using 10th order polynomial sparse grid):\n";
    cout << "   frequency:" << setw(12) << std::fixed << expectation[0]
         << "   error:" << setw(12) << std::scientific << std::abs(expectation[0] - 1.0) << "\n";
    cout << "  correction:" << setw(12) << std::fixed << expectation[1]
         << "   error:" << setw(12) << std::scientific << std::abs(expectation[1] - 0.3 * pi)
         << "\n\n";

    // Solve the same example, but switch to 30-th order polynomial with a Sequence grid

    // rebuild the sparse grid interpolant
    // use 30-th order polynomial and the faster Sequence grid
    grid.makeSequenceGrid(2, 1, 30, TasGrid::type_iptotal, TasGrid::rule_leja);
    grid.setDomainTransform(domain_a, domain_b);

    TasGrid::loadNeededPoints<TasGrid::mode_sequential>(
        [&](std::vector<double> const &x, std::vector<double> &y, size_t)->
        void{
            std::vector<double> model_at_point(num_discrete_nodes);
            model(x[0], x[1], model_at_point);
            y[0] = likelihood((double) num_discrete_nodes, model_at_point, data);
        },
        grid, 1);

    state.clearPDFvalues(); // forget the previous history
    state.setState(init_chains); // reset the chains

    // Call to Tasmanian DREAM Sampling algorithm
    TasDREAM::SampleDREAM<TasDREAM::logform>
                         (num_burnup_iterations, num_sample_iterations,
                          TasDREAM::posterior<TasDREAM::logform>(grid, TasDREAM::uniform_prior),
                          grid.getDomainInside(),
                          state,
                          TasDREAM::dist_uniform, 0.5, // independent update of magnitude 0.5
                          TasDREAM::const_percent<90> // use 90% differential update
                          );

    state.getHistoryMeanVariance(expectation, variance); // recompute the statistics

    cout << "Inferred values (using 30th order polynomial sparse grid):\n";
    cout << "   frequency:" << setw(12) << std::fixed << expectation[0]
         << "   error:" << setw(12) << std::scientific << std::abs(expectation[0] - 1.0) << "\n";
    cout << "  correction:" << setw(12) << std::fixed << expectation[1]
         << "   error:" << setw(12) << std::scientific << std::abs(expectation[1] - 0.3 * pi)
         << "\n\n";

    cout << "\n" << "---------------------------------------------------------------------------------------------------\n";
#ifndef __TASMANIAN_DOXYGEN_SKIP
//! [DREAM_Example_02 example]
#endif
}