n2p2 - A neural network potential package
KalmanFilter.cpp
Go to the documentation of this file.
1// n2p2 - A neural network potential package
2// Copyright (C) 2018 Andreas Singraber (University of Vienna)
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <https://www.gnu.org/licenses/>.
16
17#include "KalmanFilter.h"
18#include "utility.h"
19#include <Eigen/LU>
20#include <iostream>
21#include <stdexcept>
22
23using namespace Eigen;
24using namespace std;
25using namespace nnp;
26
27KalmanFilter::KalmanFilter(size_t const sizeState,
28 KalmanType const type) :
29 Updater(sizeState),
30 sizeObservation(0 ),
31 numUpdates (0 ),
32 epsilon (0.0 ),
33 q (0.0 ),
34 q0 (0.0 ),
35 qtau (0.0 ),
36 qmin (0.0 ),
37 eta (0.0 ),
38 eta0 (0.0 ),
39 etatau (0.0 ),
40 etamax (0.0 ),
41 lambda (0.0 ),
42 nu (0.0 ),
43 gamma (0.0 ),
44 w (NULL),
45 xi (NULL),
46 H (NULL)
47{
48 if (!(type == KT_STANDARD ||
50 {
51 throw runtime_error("ERROR: Unknown Kalman filter type.\n");
52 }
53
54 if (sizeState < 1)
55 {
56 throw runtime_error("ERROR: Wrong Kalman filter dimensions.\n");
57 }
58
59 this->type = type;
61
62 w = new Map<VectorXd >(0, sizeState);
63 xi = new Map<VectorXd const>(0, sizeObservation);
64 H = new Map<MatrixXd const>(0, sizeState, sizeObservation);
65 P.resize(sizeState, sizeState);
66 P.setIdentity();
67 // Prevent problems with unallocated K when log starts.
69 K.setZero();
70}
71
73{
74}
75
76void KalmanFilter::setSizeObservation(size_t const size)
77{
78 sizeObservation = size;
79
80 return;
81}
82
83void KalmanFilter::setState(double* state)
84{
85 new (w) Map<VectorXd>(state, sizeState);
86
87 return;
88}
89
90void KalmanFilter::setError(double const* const error)
91{
93
94 return;
95}
96
97void KalmanFilter::setError(double const* const error, size_t const size)
98{
99 new (xi) Map<VectorXd const>(error, size);
100
101 return;
102}
103
104void KalmanFilter::setJacobian(double const* const jacobian)
105{
106 setJacobian(jacobian, sizeObservation);
107
108 return;
109}
110
111void KalmanFilter::setJacobian(double const* const jacobian,
112 size_t const columns)
113{
114 new (H) Map<MatrixXd const>(jacobian, sizeState, columns);
115
116 return;
117}
118
120{
122
123 return;
124}
125
126void KalmanFilter::update(size_t const sizeObservation)
127{
128 sw[prefix].start(timingReset);
129
130 X.resize(sizeState, sizeObservation);
131
132 // Calculate temporary result.
133 // X = P . H
134 X = P.selfadjointView<Lower>() * (*H);
135
136 // Calculate scaling matrix.
137 // A = H^T . X
138 MatrixXd A = H->transpose() * X;
139
140 // Increase learning rate.
141 // eta(n) = eta(0) * exp(n * tau)
142 if (type == KT_STANDARD && eta < etamax) eta *= exp(etatau);
143
144 // Add measurement noise.
145 // A = A + R
146 if (type == KT_STANDARD)
147 {
148 A.diagonal() += VectorXd::Constant(sizeObservation, 1.0 / eta);
149 }
150 else if (type == KT_FADINGMEMORY)
151 {
152 A.diagonal() += VectorXd::Constant(sizeObservation, lambda);
153 }
154
155 // Calculate Kalman gain matrix.
156 // K = X . A^-1
157 K.resize(sizeState, sizeObservation);
158 K = X * A.inverse();
159
160 // Update error covariance matrix.
161 // P = P - K . X^T
162 P.noalias() -= K * X.transpose();
163
164 // Apply forgetting factor.
165 if (type == KT_FADINGMEMORY)
166 {
167 P *= 1.0 / lambda;
168 }
169 // Add process noise.
170 // P = P + Q
171 P.diagonal() += VectorXd::Constant(sizeState, q);
172
173 //double numAcc = (P - P.transpose()).cwiseAbs().maxCoeff();
174 //cout << "Max. deviation of symmetric form of P: " << numAcc << endl;
175
176 // Update state vector.
177 // w = w + K . xi
178 (*w) += K * (*xi);
179
180 // Anneal process noise.
181 // q(n) = q(0) * exp(-n * tau)
182 if (q > qmin) q *= exp(-qtau);
183
184 // Update forgetting factor.
185 if (type == KT_FADINGMEMORY)
186 {
187 lambda = nu * lambda + 1.0 - nu;
188 gamma = 1.0 / (1.0 + lambda / gamma);
189 }
190
191 numUpdates++;
192
193 if (timingReset) timingReset = false;
194 sw[prefix].stop();
195
196 return;
197}
198
199void KalmanFilter::setParametersStandard(double const epsilon,
200 double const q0,
201 double const qtau,
202 double const qmin,
203 double const eta0,
204 double const etatau,
205 double const etamax)
206{
207 this->epsilon = epsilon;
208 this->q0 = q0 ;
209 this->qtau = qtau ;
210 this->qmin = qmin ;
211 this->eta0 = eta0 ;
212 this->etatau = etatau ;
213 this->etamax = etamax ;
214
215 q = q0;
216 eta = eta0;
217 P /= epsilon;
218
219 return;
220}
221
223 double const q0,
224 double const qtau,
225 double const qmin,
226 double const lambda,
227 double const nu)
228{
229 this->epsilon = epsilon;
230 this->q0 = q0 ;
231 this->qtau = qtau ;
232 this->qmin = qmin ;
233 this->lambda = lambda ;
234 this->nu = nu ;
235
236 q = q0;
237 P /= epsilon;
238 gamma = 1.0;
239
240 return;
241}
242
243string KalmanFilter::status(size_t epoch) const
244{
245
246 double Pasym = 0.5 * (P - P.transpose()).array().abs().mean();
247 double Pdiag = P.diagonal().array().abs().sum();
248 double Poffdiag = (P.array().abs().sum() - Pdiag)
249 / (sizeState * (sizeState - 1));
250 Pdiag /= sizeState;
251 double Kmean = K.array().abs().mean();
252
253 string s = strpr("%10zu %10zu %16.8E %16.8E %16.8E %16.8E %16.8E",
254 epoch, numUpdates, Pdiag, Poffdiag, Pasym, Kmean, q);
255 if (type == KT_STANDARD)
256 {
257 s += strpr(" %16.8E", eta);
258 }
259 else if (type == KT_FADINGMEMORY)
260 {
261 s += strpr(" %16.8E %16.8E", lambda, numUpdates * gamma);
262 }
263 s += '\n';
264
265 return s;
266}
267
268vector<string> KalmanFilter::statusHeader() const
269{
270 vector<string> header;
271
272 vector<string> title;
273 vector<string> colName;
274 vector<string> colInfo;
275 vector<size_t> colSize;
276 title.push_back("Kalman filter status report.");
277 colSize.push_back(10);
278 colName.push_back("epoch");
279 colInfo.push_back("Training epoch.");
280 colSize.push_back(10);
281 colName.push_back("nupdates");
282 colInfo.push_back("Number of updates performed.");
283 colSize.push_back(16);
284 colName.push_back("Pdiag");
285 colInfo.push_back("Mean of absolute diagonal values of error covariance "
286 "matrix P.");
287 colSize.push_back(16);
288 colName.push_back("Poffdiag");
289 colInfo.push_back("Mean of absolute off-diagonal values of error "
290 "covariance matrix P.");
291 colSize.push_back(16);
292 colName.push_back("Pasym");
293 colInfo.push_back("Asymmetry of P, i.e. mean of absolute values of "
294 "asymmetric part 0.5*(P - P^T).");
295 colSize.push_back(16);
296 colName.push_back("Kmean");
297 colInfo.push_back("Mean of abolute compontents of Kalman gain matrix K.");
298 colSize.push_back(16);
299 colName.push_back("q");
300 colInfo.push_back("Magnitude of process noise (= diagonal entries of Q).");
301 if (type == KT_STANDARD)
302 {
303 colSize.push_back(16);
304 colName.push_back("eta");
305 colInfo.push_back("Learning rate.");
306 }
307 else if (type == KT_FADINGMEMORY)
308 {
309 colSize.push_back(16);
310 colName.push_back("lambda");
311 colInfo.push_back("Forgetting factor for fading memory KF.");
312 colSize.push_back(16);
313 colName.push_back("kgamma");
314 colInfo.push_back("Forgetting gain k * gamma(k).");
315 }
316 header = createFileHeader(title, colSize, colName, colInfo);
317
318 return header;
319}
320
321vector<string> KalmanFilter::info() const
322{
323 vector<string> v;
324
325 if (type == KT_STANDARD)
326 {
327 v.push_back(strpr("KalmanType::KT_STANDARD (%d)\n", type));
328 v.push_back(strpr("sizeState = %zu\n", sizeState));
329 v.push_back(strpr("sizeObservation = %zu\n", sizeObservation));
330 v.push_back(strpr("epsilon = %12.4E\n", epsilon));
331 v.push_back(strpr("q0 = %12.4E\n", q0 ));
332 v.push_back(strpr("qtau = %12.4E\n", qtau ));
333 v.push_back(strpr("qmin = %12.4E\n", qmin ));
334 v.push_back(strpr("eta0 = %12.4E\n", eta0 ));
335 v.push_back(strpr("etatau = %12.4E\n", etatau ));
336 v.push_back(strpr("etamax = %12.4E\n", etamax ));
337 }
338 else if (type == KT_FADINGMEMORY)
339 {
340 v.push_back(strpr("KalmanType::KT_FADINGMEMORY (%d)\n", type));
341 v.push_back(strpr("sizeState = %zu\n", sizeState));
342 v.push_back(strpr("sizeObservation = %zu\n", sizeObservation));
343 v.push_back(strpr("epsilon = %12.4E\n", epsilon));
344 v.push_back(strpr("q0 = %12.4E\n", q0 ));
345 v.push_back(strpr("qtau = %12.4E\n", qtau ));
346 v.push_back(strpr("qmin = %12.4E\n", qmin ));
347 v.push_back(strpr("lambda = %12.4E\n", lambda));
348 v.push_back(strpr("nu = %12.4E\n", nu ));
349 }
350 v.push_back(strpr("OpenMP threads used: %d\n", nbThreads()));
351
352 return v;
353}
void update()
Update error covariance matrix and state vector.
void setParametersStandard(double const epsilon, double const q0, double const qtau, double const qmin, double const eta0, double const etatau, double const etamax)
Set parameters for standard Kalman filter.
double eta0
Learning rate initial value .
Definition: KalmanFilter.h:214
Eigen::Map< Eigen::MatrixXd const > * H
Derivative matrix.
Definition: KalmanFilter.h:230
double nu
Parameter for fading memory Kalman filter.
Definition: KalmanFilter.h:222
double qtau
Process noise exponential decay parameter .
Definition: KalmanFilter.h:208
std::string status(std::size_t epoch) const
Status report.
std::size_t sizeObservation
Size of observation (measurement) vector.
Definition: KalmanFilter.h:198
KalmanType type
Kalman filter type.
Definition: KalmanFilter.h:196
Eigen::Map< Eigen::VectorXd > * w
State vector.
Definition: KalmanFilter.h:226
double q0
Process noise initial value .
Definition: KalmanFilter.h:206
double lambda
Forgetting factor for fading memory Kalman filter.
Definition: KalmanFilter.h:220
void setParametersFadingMemory(double const epsilon, double const q0, double const qtau, double const qmin, double const lambda, double const nu)
Set parameters for fading memory Kalman filter.
Eigen::MatrixXd X
Intermediate result X = P . H.
Definition: KalmanFilter.h:236
void setState(double *state)
Set pointer to current state.
std::vector< std::string > statusHeader() const
Header for status report file.
double etatau
Learning rate exponential increase parameter .
Definition: KalmanFilter.h:216
double etamax
Learning rate maximum value .
Definition: KalmanFilter.h:218
double gamma
Forgetting gain factor gamma for fading memory Kalman filter.
Definition: KalmanFilter.h:224
std::vector< std::string > info() const
Information about Kalman filter settings.
void setError(double const *const error)
Set pointer to current error vector.
std::size_t numUpdates
Total number of updates performed.
Definition: KalmanFilter.h:200
KalmanType
Enumerate different Kalman filter types.
Definition: KalmanFilter.h:36
@ KT_STANDARD
Regular Kalman filter.
Definition: KalmanFilter.h:38
@ KT_FADINGMEMORY
Kalman filtering with fading memory modification.
Definition: KalmanFilter.h:40
double q
Process noise .
Definition: KalmanFilter.h:204
double qmin
Process noise minimum value .
Definition: KalmanFilter.h:210
void setSizeObservation(std::size_t const sizeObservation)
Set observation vector size.
Eigen::MatrixXd K
Kalman gain matrix.
Definition: KalmanFilter.h:234
double eta
Learning rate .
Definition: KalmanFilter.h:212
double epsilon
Error covariance initialization parameter .
Definition: KalmanFilter.h:202
Eigen::Map< Eigen::VectorXd const > * xi
Error vector.
Definition: KalmanFilter.h:228
Eigen::MatrixXd P
Error covariance matrix.
Definition: KalmanFilter.h:232
virtual ~KalmanFilter()
Destructor.
void setJacobian(double const *const jacobian)
Set pointer to current Jacobi matrix.
Base class for different weight update methods.
Definition: Updater.h:32
std::string prefix
Prefix for timing stopwatches.
Definition: Updater.h:112
std::size_t sizeState
Number of neural network connections (weights + biases).
Definition: Updater.h:110
bool timingReset
Internal loop timer reset switch.
Definition: Updater.h:108
std::map< std::string, Stopwatch > sw
Stopwatch map for timing.
Definition: Updater.h:114
Definition: Atom.h:29
string strpr(const char *format,...)
String version of printf function.
Definition: utility.cpp:90
vector< string > createFileHeader(vector< string > const &title, vector< size_t > const &colSize, vector< string > const &colName, vector< string > const &colInfo, char const &commentChar)
Definition: utility.cpp:111