1 /*  _______________________________________________________________________
2 
3     PECOS: Parallel Environment for Creation Of Stochastics
4     Copyright (c) 2011, Sandia National Laboratories.
5     This software is distributed under the GNU Lesser General Public License.
6     For more information, see the README file in the top Pecos directory.
7     _______________________________________________________________________ */
8 
9 #include <ctype.h>
10 #include <string>
11 #include <cmath>
12 #include <iostream>
13 
14 #include <Teuchos_UnitTestHarness.hpp>
15 
16 #include <teuchos_data_types.hpp>
17 #include <CppFunction.hpp>
18 #include <BoundedVariables.hpp>
19 #include <AffineVariableTransformation.hpp>
20 #include <RegressionBuilder.hpp>
21 
22 #include <DirectANN.hpp>
23 
24 using namespace Pecos;
25 using namespace Pecos::util;
26 using namespace Pecos::surrogates;
27 
28 namespace {
29 
30 //--------------------------------------
31 // Helper functions
32 
33 /**
34 \brief Evaluate a quadratic polynomial with no interaction terms, i.e.
35 *  \[q_i = (i+1) + \sum_j (i+1)*(2*x_j-1)^2+(i+2)*(2*x_j-1)\]
36 */
additive_quadratic_function(const Real * sample,Real * func_vals,const OptionsList & opts)37 void additive_quadratic_function(const Real* sample, Real* func_vals, const OptionsList &opts){
38    const int num_vars = opts.get<int>("num_vars");
39    const int num_qoi =  opts.get<int>("num_qoi");
40 
41    for (int i=0; i<num_qoi; i++){
42      func_vals[i] = (Real)(i+1);
43      for (int j=0; j<num_vars; j++){
44        Real x = (2*sample[j]-1);
45        func_vals[i] += (Real)(i+1)*x*x+(Real)(i+2)*x;
46      }
47    }
48 }
49 /**
50 \brief Evaluate the "textbook" function of two real variables, i.e.
51 *  \[q = (x_1 - 1)^4 + (x_2 - 2)^4 \]
52 */
53 
textbook_function(const Real * sample,Real * func_vals,const OptionsList & opts)54 void textbook_function(const Real* sample, Real* func_vals, const OptionsList &opts){
55    Real x1 = sample[0];
56    Real x2 = sample[1];
57    func_vals[0] = pow(x1 - 1., 4.) + pow(x2 - 1., 4.);
58 }
59 
60 // Compute the mean pointwise absolute error
mean_abs_error(RealMatrix & pred,RealMatrix & truth)61 double mean_abs_error(RealMatrix &pred, RealMatrix &truth) {
62   double K = pred.numRows();
63   if (K != truth.numRows())
64     throw(std::runtime_error("Mismatch between predicted and truth vector dimensions"));
65   double eval = 0.0;
66   for (int i = 0; i < K; i++) {
67     eval += std::abs(pred(i,0) - truth(i,0));
68   }
69   eval /= K;
70   return eval;
71 }
72 
73 // Compute the relative mean pointwise absolute error
relative_mean_abs_error(RealMatrix & pred,RealMatrix & truth)74 double relative_mean_abs_error(RealMatrix &pred, RealMatrix &truth) {
75   double K = pred.numRows();
76   if (K != truth.numRows())
77     throw(std::runtime_error("Mismatch between predicted and truth vector dimensions"));
78   double eval = 0.0;
79   double tmp;
80   for (int i = 0; i < K; i++) {
81     eval += std::abs((pred(i,0) - truth(i,0))/truth(i,0));
82   }
83   eval /= K;
84   return eval;
85 }
86 
test_directANN(Real atol)87 int test_directANN(Real atol){
88   // Data generation parameters
89   int num_vars = 2;
90   int num_samples = 100;
91   int training_data_seed = 3105;
92   int test_data_seed = 1000;
93 
94   // direct ANN parameters
95   int num_qoi = 1; // must be 1 for directANN
96   double range_param = 2.0;
97   int directANN_seed = 8105;
98   int nodes = num_samples - 1;
99 
100   // Define the function variables
101   //RealVector ranges;
102   //define_homogeneous_ranges(num_vars, 0., 1., ranges);
103   RealVector ranges(4);
104   ranges[0] = 0.;
105   ranges[1] = 1.;
106   ranges[2] = -1.;
107   ranges[3] = 1.;
108   std::shared_ptr<Variables> variables(new BoundedVariables);
109   std::dynamic_pointer_cast<BoundedVariables>(variables)->set_ranges(ranges);
110 
111   // Define the variable transformation
112   std::shared_ptr<VariableTransformation>
113     var_transform(new AffineVariableTransformation);
114   var_transform->set_variables(variables);
115 
116   // Define the function to approximate
117   CppFunction model;
118   OptionsList model_opts;
119   model_opts.set("num_vars", num_vars);
120   model_opts.set("num_qoi", num_qoi);
121   model.set_options(model_opts);
122   model.set_function(&additive_quadratic_function);
123   //model.set_function(&textbook_function);
124 
125   // Generate training and test data
126   RealMatrix training_samples, training_vals;
127   generate_uniform_samples(num_vars, num_samples, training_data_seed, *var_transform,
128                            training_samples);
129   model.value(training_samples, training_vals);
130 
131   RealMatrix test_samples, test_vals;
132   generate_uniform_samples(num_vars, num_samples, test_data_seed, *var_transform,
133                            test_samples);
134   model.value(test_samples, test_vals);
135 
136   // Build the Direct ANN approximation for 3 different scalers
137   // and compare to "gold" values
138   double mean_normalization_gold = 1.06246e-5;
139   double min_max_normalization_gold = 3.26542e-5;
140   double standardization_gold = 3.04479e-4;
141 
142   RealMatrix pred(num_samples, 1);
143   double error_eval;
144 
145   std::string scaler_type = "mean normalization";
146   DirectANN directANN_mean_nm(nodes, range_param, training_samples, training_vals,
147                          scaler_type, directANN_seed);
148   directANN_mean_nm.value(test_samples, pred);
149   error_eval = relative_mean_abs_error(pred, test_vals);
150   if (!(std::abs(error_eval - mean_normalization_gold < atol))) {
151     std::cout << "1\n";
152     return 1;
153   }
154 
155   scaler_type = "min-max normalization";
156   DirectANN directANN_mm_nm(nodes, range_param, training_samples, training_vals,
157                             scaler_type, directANN_seed);
158   directANN_mm_nm.value(test_samples, pred);
159   error_eval = relative_mean_abs_error(pred, test_vals);
160   if (!(std::abs(error_eval - min_max_normalization_gold < atol))) {
161     std::cout << "2\n";
162     return 2;
163   }
164 
165   scaler_type = "standardization";
166   DirectANN directANN_sd(nodes, range_param, training_samples, training_vals,
167                          scaler_type, directANN_seed);
168   directANN_sd.value(test_samples, pred);
169   error_eval = relative_mean_abs_error(pred, test_vals);
170   if (!(std::abs(error_eval - standardization_gold < atol))) {
171     std::cout << "3\n";
172     return 3;
173   }
174   else {
175     return 0;
176   }
177 }
178 
179 //----------------------------------------------------------------
180 // Unit tests
181 
182 /**
183 \brief Create a direct ANN from sample data and test performance
184        for 3 data scaler types
185 */
TEUCHOS_UNIT_TEST(dry_run,test_directANN)186 TEUCHOS_UNIT_TEST(dry_run, test_directANN)
187 {
188   TEST_ASSERT(!test_directANN(1e-10));
189 }
190 
191 }
192