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 <DirectANN.hpp>
10 #include <BoundedVariables.hpp>
11 #include <AffineVariableTransformation.hpp>
12 #include <RegressionBuilder.hpp>
13 #include "linear_solvers.hpp"
14 #include "OptionsList.hpp"
15
16 namespace Pecos {
17 namespace surrogates {
18
DirectANNBasisSet(const RealMatrix & weights_in)19 DirectANNBasisSet::DirectANNBasisSet(const RealMatrix &weights_in)
20 : weights(weights_in) {
21 }
22
nodeSum(int index,const RealVector & x) const23 Real DirectANNBasisSet::nodeSum(int index, const RealVector &x) const
24 {
25 if (index >= weights.numRows())
26 throw(std::runtime_error("basis set index is out of bounds"));
27 if (x.length() + 1 != weights.numCols())
28 throw(std::runtime_error("size of input to hidden layer weights matrix is incorrect"));
29 Real sum = 0.0;
30 for (int i = 0; i < x.length(); i++) {
31 sum += weights(index,i)*x[i];
32 }
33 sum += weights(index, x.length()); // bias term
34 return sum;
35 }
36
eval(int index,const RealVector & x) const37 Real DirectANNBasisSet::eval(int index, const RealVector &x) const
38 {
39 return tanh(nodeSum(index, x));
40 }
41
42 /*
43 * no longer valid because output node has no tanh(.) ...
44 Real DirectANNBasisSet::deriv(int index, const RealVector &x, const IntVector &vars) const
45 {
46 if (vars.length() != 1)
47 throw(std::runtime_error("vars vector too long"));
48 if (vars[0] >= x.length())
49 throw(std::runtime_error("vars[0] entry too large"));
50 Real sum = nodeSum(index, x);
51 Real tanhsum = tanh(sum);
52 return (1.0 - tanhsum*tanhsum)*weights(index, vars[0]);
53 }
54 */
55
56 // default constructor
DirectANN()57 DirectANN::DirectANN(){}
58 // default destructor
~DirectANN()59 DirectANN::~DirectANN(){}
60
61 // main constructor
DirectANN(const int num_nodes,const Real range_param,const RealMatrix & samples,const RealMatrix & response,const std::string scaler_type,const int seed)62 DirectANN::DirectANN(const int num_nodes, const Real range_param,
63 const RealMatrix &samples, const RealMatrix &response,
64 const std::string scaler_type, const int seed) {
65
66 const int num_vars = samples.numRows();
67 const int num_samples = samples.numCols();
68 nodes = std::min(num_nodes, maxNodes); // max of 100 allowed
69 if (num_samples < num_nodes + 1) {
70 throw(std::runtime_error("DirectANN needs at least"
71 " as many samples are the number of nodes + 1"));
72 }
73
74 // Scale the data
75 if (scaler_type == "mean normalization")
76 ds = std::make_shared<NormalizationScaler>(samples, true);
77 else if (scaler_type == "min-max normalization")
78 ds = std::make_shared<NormalizationScaler>(samples, false);
79 else if (scaler_type == "standardization")
80 ds = std::make_shared<StandardizationScaler>(samples);
81 else
82 throw(std::string("Invalid scaler type"));
83
84 RealMatrix scaled_samples = ds->getScaledFeatures();
85
86 // Randomly generate weights for the first layer
87 RealVector ranges;
88 define_homogeneous_ranges(nodes, -range_param/2.0, range_param/2.0, ranges);
89 std::shared_ptr<Variables> variables(new BoundedVariables);
90 std::dynamic_pointer_cast<BoundedVariables>(variables)->set_ranges(ranges);
91 std::shared_ptr<VariableTransformation>
92 var_transform(new AffineVariableTransformation);
93 var_transform->set_variables(variables);
94
95 RealMatrix random_weights;
96 generate_uniform_samples(nodes, num_vars + 1, seed, *var_transform,
97 random_weights);
98 bs = std::make_shared<DirectANNBasisSet>(random_weights);
99
100 // construct the linear system
101 RealMatrix A(num_samples, nodes + 1);
102 RealVector b(num_samples);
103
104 for (int s = 0; s < num_samples; s++) {
105 for (int n = 0; n < nodes; n++) {
106 RealVector sample = Teuchos::getCol<int,Real> (Teuchos::View,
107 scaled_samples, s);
108 A(s,n) = bs->eval(n, sample);
109 }
110 A(s, nodes) = 1.0; // bias term
111 b[s] = response(s,0);
112 }
113
114 // set solver options and solve
115 util::OptionsList params;
116 params.set("regression_type",util::ORTHOG_MATCH_PURSUIT);
117 std::shared_ptr<util::LinearSystemSolver> solver = regression_solver_factory(params);
118
119 solver->solve(A, b, params);
120 solver->get_final_solutions(coeffs);
121 }
122
123 // constructor for existing (i.e. calibrated) ANN -- stills needs
124 // scaler info
125 /*
126 DirectANN::DirectANN(const RealMatrix& bs_matrix, const RealVector& coeffs_in)
127 {
128 bs = std::make_shared<DirectANNBasisSet>(bs_matrix);
129 setCoefficients(coeffs_in);
130 }
131 */
132
133 // constructor for known 1st layer weights, unknown output layer weights
134 // initially used to compare output from pecos ANN ... no longer suitable
135 // for this in current form.
136 //
137 /*
138 DirectANN::DirectANN(int num_nodes, const RealMatrix& bs_matrix,
139 RealMatrix &samples, RealMatrix& response)
140 {
141 bs = std::make_shared<DirectANNBasisSet>(bs_matrix);
142
143 const int num_samples = samples.numCols();
144 nodes = std::min(num_nodes, maxNodes); // max of 100 allowed
145 if (num_samples < num_nodes + 1) {
146 throw(std::runtime_error("DirectANN needs at least"
147 " as many samples are the number of nodes + 1"));
148 }
149
150 // Scale the data
151 const Real norm_factor = 0.8;
152 const int num_vars = samples.numRows();
153 ds = std::make_shared<NormalizationScaler>(samples, false);
154 //ds = std::make_shared<StandardizationScaler>(samples);
155 RealMatrix scaled_samples = ds->getScaledFeatures();
156
157 // construct the linear system
158 RealMatrix A(num_samples, nodes + 1);
159 RealVector b(num_samples);
160
161 for (int s = 0; s < num_samples; s++) {
162 for (int n = 0; n < nodes; n++) {
163 RealVector sample = Teuchos::getCol<int,Real> (Teuchos::View,
164 scaled_samples, s);
165
166 A(s,n) = bs->eval(n, sample);
167 }
168 A(s, nodes) = 1.0; // bias term
169 b(s) = response(s,0);
170 }
171
172 // set solver options and solve
173 util::OptionsList params;
174 params.set("regression_type",util::ORTHOG_MATCH_PURSUIT);
175 //params.set("regression_type",util::QR_LEAST_SQ_REGRESSION);
176 std::shared_ptr<util::LinearSystemSolver> solver = regression_solver_factory(params);
177
178 solver->solve(A, b, params);
179 solver->get_final_solutions(coeffs);
180 }
181 */
182
183
evaluate(const RealVector & x) const184 Real DirectANN::evaluate(const RealVector &x) const
185 {
186 RealVector u;
187 if (ds->has_scaling) {
188 u = ds->scaleFeatures(x);
189 }
190 else {
191 u = x;
192 }
193 const int num_features = u.length();
194 const int num_coeffs = coeffs.length();
195 if (num_features + 1 != bs->weights.numCols()) {
196 throw(std::runtime_error("DirectANN input to hidden layer weights do"
197 " not match the number of features"));
198 }
199 if (nodes != bs->weights.numRows()) {
200 throw(std::runtime_error("DirectANN input to hidden layer weights do"
201 " not match the number of nodes"));
202 }
203 Real sum = 0.0;
204 for (int i = 0; i < nodes; i++) {
205 sum += coeffs[i]*bs->eval(i, u);
206 }
207 sum += coeffs[num_coeffs - 1]; // bias term
208 return sum;
209 }
210
value(const RealMatrix & samples,RealMatrix & approx_values)211 void DirectANN::value(const RealMatrix &samples, RealMatrix &approx_values) {
212 const int num_features = samples.numRows();
213 const int num_samples = samples.numCols();
214 if (num_samples != approx_values.numRows()) {
215 throw(std::runtime_error("Direct ANN value inputs are not consistent."
216 " Number of samples and approximation sizes do not match"));
217 }
218 RealVector x(num_features);
219 // can't use Teuchos::getCol because it doesn't compile with const samples
220 for (int i = 0; i < num_samples; i++) {
221 for (int j = 0; j < num_features; j++) {
222 x(j) = samples(j,i);
223 }
224 approx_values(i,0) = this->evaluate(x);
225 }
226 }
227
gradient(const RealMatrix & samples,int qoi,RealMatrix & gradients)228 void DirectANN::gradient(const RealMatrix &samples, int qoi, RealMatrix &gradients) {
229 throw(std::string("This Function type does not provide gradients"));
230 }
231
jacobian(const RealVector & sample,RealMatrix & jacobian)232 void DirectANN::jacobian(const RealVector &sample, RealMatrix &jacobian) {
233 throw(std::string("This Function type does not provide a jacobian"));
234 }
235
hessian(const RealMatrix & samples,int qoi,RealMatrixList & hessians)236 void DirectANN::hessian(const RealMatrix &samples, int qoi, RealMatrixList &hessians) {
237 throw(std::string("This Function type does not provide hessians"));
238 }
239
240 } // namespace surrogates
241 } // namespace Pecos
242