File: example_sparse_grids_06.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 (137 lines) | stat: -rw-r--r-- 5,651 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

#include "Tasmanian.hpp"
#include <random>

using namespace std;

/*!
 * \internal
 * \file example_sparse_grids_06.cpp
 * \brief Examples for the Tasmanian Sparse Grid module.
 * \author Miroslav Stoyanov
 * \ingroup TasmanianSGExamples
 *
 * Tasmanian Sparse Grids Example 6.
 * \endinternal
 */

/*!
 * \ingroup TasmanianSGExamples
 * \addtogroup TasmanianSGExamples6 Tasmanian Sparse Grids module, example 6
 *
 * \par Example 6
 * Build a surrogate model using automatic construction.
 */

/*!
 * \ingroup TasmanianSGExamples6
 * \brief Sparse Grids Example 6: adaptive surrogate modeling
 *
 * Example 5 demonstrates how to construct a surrogate by processing
 * the samples in batches. The batch construction can facilitate
 * parallel sampling, but the exact number of samples is hard to control
 * since the points from the entire batch are needed at once.
 * Furthermore, if a fixed number of computing nodes is available
 * (e.g., processor cores or MPI ranks) it may be hard to keep all
 * nodes loaded if the batch size does not divide evenly.
 *
 * The construction procedure offers a more flexible refinement alternative.
 * The batch size can be controlled at a much finer scale to accommodate
 * a fixed budget and occupancy across all parallel resources can be guaranteed
 * (unless the level limits or tolerance are about to be reached,
 * or the initial grid is too coarse).
 * See the documentation for TasGrid::mpiConstructSurrogate() for
 * the MPI variant of this method that differs only in some MPI related
 * parameters.
 *
 * \snippet SparseGrids/Examples/example_sparse_grids_06.cpp SG_Example_06 example
 */
void sparse_grids_example_06(){
#ifndef __TASMANIAN_DOXYGEN_SKIP
//! [SG_Example_06 example]
#endif

    cout << "\n---------------------------------------------------------------------------------------------------\n";
    cout << std::scientific; cout.precision(4);
    cout << "Example 6: interpolate f(x,y) = exp(-x^2) * exp(y) * cos(z), using the rleja rule\n"
         << "           employs adaptive construction\n";

    // define the model as a C++ lambda expression, the advantage of lambdas
    // is that they can wrap around very complex models
    // the construction signature uses std::vector<double>
    int const num_inputs  = 3;
    int const num_outputs = 1;
    auto model = [&](std::vector<double> const &x, std::vector<double> &y, size_t)->
        void{
            // if no initial guess is used, then y will be guaranteed to have
            // the correct size, i.e., num_outputs
            // otherwise, y must be correctly resized
            y.resize(num_outputs);
            y[0] = std::exp(-x[0] * x[0]) * std::exp(x[1]) * std::cos(x[2]);
        };

    // the accuracy of the surrogate models is measure from 1000 random reference points
    int const num_test_points = 1000;
    std::vector<double> test_points(num_test_points * num_inputs);
    std::minstd_rand park_miller(42);
    std::uniform_real_distribution<double> domain(-1.0, 1.0);
    for(auto &t : test_points) t = domain(park_miller);

    std::vector<double> reference_values(num_test_points * num_outputs);
    for(int i=0; i<num_test_points; i++){
        std::vector<double> y(1);
        model(std::vector<double>(test_points.begin() + i * num_inputs,
                                  test_points.begin() + (i+1) * num_inputs),
              y, 0);
        std::copy(y.begin(), y.end(), reference_values.begin() + i * num_outputs);
    }

    // computes the maximum error between the reference values and the grid interpolant
    auto test_grid = [&](TasGrid::TasmanianSparseGrid const &grid)->
        double{
            std::vector<double> result;
            grid.evaluateBatch(test_points, result);
            double diff = 0.0;
            for(int i=0; i<num_test_points; i++)
                diff = std::max(diff, std::abs(result[i] - reference_values[i]));
            return diff;
        };

    // when the model is cheap to compute, running multiple-threads results in a large
    // difference in the time-per-sample due to the dominating cost of thread
    // synchronization
    // unfortunately, this can have adverse effect when important samples are delayed
    // and less important samples are computed thus wasting the budget
    // for that reason, the example runs sequentially
    constexpr auto mode = TasGrid::mode_sequential;

    auto grid = TasGrid::makeSequenceGrid(num_inputs, num_outputs, 2,
                                          TasGrid::type_level, TasGrid::rule_rleja);

    cout << setw(10) << "points" << setw(20) << "error\n";

    int budget = 25; // try budget 50, 100, 200, 400, etc.

    // running examples with ever increasing budget
    // the budget is defined as the sum of loaded + new points
    for(int itr=0; itr<6; itr++){ // run 6 example iterations

        budget *= 2; // try budget 100, 200, 400, ...

        TasGrid::constructSurrogate<mode>(model, budget, 12, 1, grid,
                                          TasGrid::type_iptotal, 0);

        cout << setw(10) << grid.getNumPoints() << setw(20) << test_grid(grid) << "\n";
    }

    // Note: when using a sequence rule, the actual number of points always matches the budget
    // but this cannot be guaranteed for rules that grow by more than one node per level
    // because a tensor can be added only when all points are present and each tensor requires
    // more than one point
    // for fast growing rules, sometime it is simply not possible to build a grid
    // with exactly the given number of points

#ifndef __TASMANIAN_DOXYGEN_SKIP
//! [SG_Example_06 example]
#endif
}