All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
KernelFunctions.cpp
Go to the documentation of this file.
1 /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2  Copyright (c) 2013 The plumed team
3  (see the PEOPLE file at the root of the distribution for a list of names)
4 
5  See http://www.plumed-code.org for more information.
6 
7  This file is part of plumed, version 2.0.
8 
9  plumed is free software: you can redistribute it and/or modify
10  it under the terms of the GNU Lesser General Public License as published by
11  the Free Software Foundation, either version 3 of the License, or
12  (at your option) any later version.
13 
14  plumed is distributed in the hope that it will be useful,
15  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  GNU Lesser General Public License for more details.
18 
19  You should have received a copy of the GNU Lesser General Public License
20  along with plumed. If not, see <http://www.gnu.org/licenses/>.
21 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
22 #include "KernelFunctions.h"
23 #include "IFile.h"
24 #include <iostream>
25 #include <cmath>
26 
27 namespace PLMD {
28 
29 //+PLUMEDOC INTERNAL kernelfunctions
30 /*
31 Functions that are used to construct histograms
32 
33 Constructing histograms is something you learnt to do relatively early in life. You perform an experiment a number of times,
34 count the number of times each result comes up and then draw a bar graph that describes how often each of the results came up.
35 This only works when there are a finite number of possible results. If the result a number between 0 and 1 the bar chart is
36 less easy to draw as there are as many possible results as there are numbers between zero and one - an infinite number.
37 To resolve this problem we replace probability, \f$P\f$ with probability density, \f$\pi\f$, and write the probability of getting
38 a number between \f$a\f$ and \f$b\f$ as:
39 
40 \f[
41 P = \int_{a}^b \textrm{d}x \pi(x)
42 \f]
43 
44 To calculate probability densities from a set of results we use a process called kernel density estimation.
45 Histograms are accumulated by adding up kernel functions, \f$K\f$, with finite spatial extent, that integrate to one.
46 These functions are centered on each of the \f$n\f$-dimensional data points, \f$\mathbf{x}_i\f$. The overall effect of this
47 is that each result we obtain in our experiments contributes to the probability density in a finite sized region of the space.
48 
49 Expressing all this mathematically in kernel density estimation we write the probability density as:
50 
51 \f[
52 \pi(\mathbf{x}) = \sum_i K\left[ (\mathbf{x} - \mathbf{x}_i)^T \Sigma (\mathbf{x} - \mathbf{x}_i) \right]
53 \f]
54 
55 where \f$\Sigma\f$ is an \f$n \times n\f$ matrix called the bandwidth that controls the spatial extent of
56 the kernel. Whenever we accumulate a histogram (e.g. in \ref HISTOGRAM or in \ref METAD) we use this
57 technique.
58 
59 There is thus some flexibility in the particular function we use for \f$K[\mathbf{r}]\f$ in the above.
60 The following variants are available.
61 
62 <table align=center frame=void width=95%% cellpadding=5%%>
63 <tr>
64 <td> TYPE </td> <td> FUNCTION </td>
65 </tr> <tr>
66 <td> gaussian </td> <td> \f$f(r) = \frac{1}{(2 \pi)^{n} \sqrt{|\Sigma^{-1}|}} \exp\left(-0.5 r^2 \right)\f$ </td>
67 </tr> <tr>
68 <td> triangular </td> <td> \f$f(r) = \frac{3}{V} ( 1 - | r | )H(1-|r|) \f$ </td>
69 </tr> <tr>
70 <td> uniform </td> <td> \f$f(r) = \frac{1}{V}H(1-|r|)\f$ </td>
71 </tr>
72 </table>
73 
74 In the above \f$H(y)\f$ is a function that is equal to one when \f$y>0\f$ and zero when \f$y \le 0\f$. \f$n\f$ is
75 the dimensionality of the vector \f$\mathbf{x}\f$ and \f$V\f$ is the volume of an elipse in an \f$n\f$ dimensional
76 space which is given by:
77 
78 \f{eqnarray*}{
79 V &=& | \Sigma^{-1} | \frac{ \pi^{\frac{n}{2}} }{\left( \frac{n}{2} \right)! } \qquad \textrm{for even} \quad n \\
80 V &=& | \Sigma^{-1} | \frac{ 2^{\frac{n+1}{2}} \pi^{\frac{n-1}{2}} }{ n!! }
81 \f}
82 
83 In \ref METAD the normalization constants are ignored so that the value of the function at \f$r=0\f$ is equal
84 to one. In addition in \ref METAD we must be able to differentiate the bias in order to get forces. This limits
85 the kernels we can use in this method.
86 */
87 //+ENDPLUMEDOC
88 
89 KernelFunctions::KernelFunctions( const std::vector<double>& at, const std::vector<double>& sig, const std::string& type, const bool multivariate, const double& w, const bool norm ):
90 center(at),
91 width(sig)
92 {
93  if (multivariate==true)diagonal=false;
94  if (multivariate==false)diagonal=true;
95 
96  // Setup the kernel type
97  if(type=="GAUSSIAN" || type=="gaussian"){
99  } else if(type=="UNIFORM" || type=="uniform"){
100  ktype=uniform;
101  } else if(type=="TRIANGULAR" || type=="triangular"){
103  } else {
104  plumed_merror(type+" is an invalid kernel type\n");
105  }
106 
107 
108  if( norm ){
109  double det;
110  unsigned ncv=ndim();
111  if(diagonal){
112  det=1; for(unsigned i=0;i<width.size();++i) det*=width[i];
113  } else {
114  Matrix<double> mymatrix( getMatrix() ), myinv( ncv, ncv );
115  Invert(mymatrix,myinv); double logd;
116  logdet( myinv, logd );
117  det=std::exp(logd);
118  }
119  double volume;
120  if( ktype==gaussian ){
121  for(unsigned i=0;i<width.size();++i) det*=width[i];
122  volume=pow( 2*pi, 0.5*ncv ) * pow( det, 0.5 );
123  } else if( ktype==uniform || ktype==triangular ){
124  if( ncv%2==1 ){
125  double dfact=1;
126  for(unsigned i=1;i<ncv;i+=2) dfact*=static_cast<double>(i);
127  volume=( pow( pi, (ncv-1)/2 ) ) * ( pow( 2., (ncv+1)/2 ) ) / dfact;
128  } else {
129  double fact=1.;
130  for(unsigned i=1;i<ncv/2;++i) fact*=static_cast<double>(i);
131  volume=pow( pi,ncv/2 ) / fact;
132  }
133  if(ktype==uniform) volume*=det;
134  else if(ktype==triangular) volume*=det / 3.;
135  } else {
136  plumed_merror("not a valid kernel type");
137  }
138  height=w / volume;
139  } else {
140  height=w;
141  }
142 }
143 
144 double KernelFunctions::getCutoff( const double& width ) const {
145  const double DP2CUTOFF=6.25;
146  if( ktype==gaussian ) return sqrt(2.0*DP2CUTOFF)*width;
147  else if(ktype==triangular ) return width;
148  else if(ktype==uniform) return width;
149  else plumed_merror("No valid kernel type");
150  return 0.0;
151 }
152 
153 std::vector<double> KernelFunctions::getContinuousSupport( ) const {
154  unsigned ncv=ndim();
155  std::vector<double> support( ncv );
156  if(diagonal){
157  for(unsigned i=0;i<ncv;++i) support[i]=getCutoff(width[i]);
158  } else {
159  Matrix<double> mymatrix( getMatrix() ), myinv( ncv,ncv );
160  Invert(mymatrix,myinv);
161  Matrix<double> myautovec(ncv,ncv); std::vector<double> myautoval(ncv);
162  diagMat(myinv,myautoval,myautovec);
163  double maxautoval;maxautoval=0.;
164  unsigned ind_maxautoval;
165  for (unsigned i=0;i<ncv;i++){
166  if(myautoval[i]>maxautoval){maxautoval=myautoval[i];ind_maxautoval=i;}
167  }
168  for(unsigned i=0;i<ncv;++i){
169  double extent=fabs(sqrt(maxautoval)*myautovec(i,ind_maxautoval));
170  support[i]=getCutoff( extent );
171  }
172  }
173  return support;
174 }
175 
176 std::vector<unsigned> KernelFunctions::getSupport( const std::vector<double>& dx ) const {
177  plumed_assert( ndim()==dx.size() );
178  std::vector<unsigned> support( dx.size() );
179  std::vector<double> vv=getContinuousSupport( );
180  for(unsigned i=0;i<dx.size();++i) support[i]=static_cast<unsigned>(ceil( vv[i]/dx[i] ));
181  return support;
182 }
183 
184 double KernelFunctions::evaluate( const std::vector<Value*>& pos, std::vector<double>& derivatives, bool usederiv ) const {
185  plumed_dbg_assert( pos.size()==ndim() && derivatives.size()==ndim() );
186 #ifndef NDEBUG
187  if( usederiv ) plumed_massert( ktype!=uniform, "step function can not be differentiated" );
188 #endif
189 
190  double r2=0;
191  if(diagonal){
192  for(unsigned i=0;i<ndim();++i){
193  derivatives[i]=-pos[i]->difference( center[i] ) / width[i];
194  r2+=derivatives[i]*derivatives[i];
195  derivatives[i] /= width[i];
196  }
197  } else {
198  Matrix<double> mymatrix( getMatrix() );
199  for(unsigned i=0;i<mymatrix.nrows();++i){
200  double dp_i, dp_j; derivatives[i]=0;
201  dp_i=pos[i]->difference( center[i] );
202  for(unsigned j=0;j<mymatrix.ncols();++j){
203  if(i==j) dp_j=dp_i;
204  else dp_j=pos[j]->difference( center[j] );
205 
206  derivatives[i]+=mymatrix(i,j)*dp_j;
207  r2+=dp_i*dp_j*mymatrix(i,j);
208  }
209  }
210  }
211  double kderiv, kval;
212  if(ktype==gaussian){
213  kval=height*std::exp(-0.5*r2); kderiv=-kval;
214  } else {
215  double r=sqrt(r2);
216  if(ktype==triangular){
217  if( r<1.0 ){
218  if(r==0) kderiv=0;
219  kderiv=-1; kval=height*( 1. - fabs(r) );
220  } else {
221  kval=0.; kderiv=0.;
222  }
223  } else if(ktype==uniform){
224  kderiv=0.;
225  if(r<1.0) kval=height;
226  else kval=0;
227  } else {
228  plumed_merror("Not a valid kernel type");
229  }
230  kderiv*=height / r ;
231  }
232  for(unsigned i=0;i<ndim();++i) derivatives[i]*=kderiv;
233  return kval;
234 }
235 
236 KernelFunctions* KernelFunctions::read( IFile* ifile, const std::vector<std::string>& valnames ){
237  std::string sss; ifile->scanField("multivariate",sss);
238  std::vector<double> cc( valnames.size() ), sig;
239  bool multivariate;
240  if( sss=="false" ){
241  multivariate=false;
242  sig.resize( valnames.size() );
243  for(unsigned i=0;i<valnames.size();++i){
244  ifile->scanField(valnames[i],cc[i]);
245  ifile->scanField("sigma_"+valnames[i],sig[i]);
246  }
247  } else if( sss=="true" ){
248  multivariate=true;
249  unsigned ncv=valnames.size();
250  sig.resize( (ncv*(ncv+1))/2 );
251  Matrix<double> upper(ncv,ncv), lower(ncv,ncv);
252  for(unsigned i=0;i<ncv;++i){
253  ifile->scanField(valnames[i],cc[i]);
254  for(unsigned j=0;j<ncv-i;j++){ ifile->scanField("sigma_" +valnames[j+i] + "_" + valnames[j], lower(j+i,j) ); upper(j,j+i)=lower(j+i,j); }
255  }
256  Matrix<double> mymult( ncv, ncv ), invmatrix(ncv,ncv);
257  mult(lower,upper,mymult); Invert( mymult, invmatrix );
258  unsigned k=0;
259  for(unsigned i=0;i<ncv;i++){
260  for(unsigned j=i;j<ncv;j++){ sig[k]=invmatrix(i,j); k++; }
261  }
262  } else {
263  plumed_merror("multivariate flag should equal true or false");
264  }
265  double h; ifile->scanField("height",h);
266  return new KernelFunctions( cc, sig, "gaussian", multivariate ,h, false);
267 }
268 
269 }
std::vector< unsigned > getSupport(const std::vector< double > &dx) const
Get the support.
static KernelFunctions * read(IFile *ifile, const std::vector< std::string > &valnames)
Read a kernel function from a file.
int Invert(const Matrix< T > &A, Matrix< double > &inverse)
Definition: Matrix.h:246
double evaluate(const std::vector< Value * > &pos, std::vector< double > &derivatives, bool usederiv=true) const
Evaluate the kernel function.
double height
The height of the kernel.
Matrix< double > getMatrix() const
Convert the width into matrix form.
unsigned ncols() const
Return the number of columns.
Definition: Matrix.h:110
double getCutoff(const double &width) const
Get the cutoff for a kernel.
#define DP2CUTOFF
Definition: MetaD.cpp:37
std::vector< double > getContinuousSupport() const
get it in continuous form
unsigned ndim() const
Get the dimensionality of the kernel.
std::vector< double > center
The center of the kernel function.
void mult(const Matrix< T > &A, const Matrix< T > &B, Matrix< T > &C)
Definition: Matrix.h:165
enum PLMD::KernelFunctions::@4 ktype
What type of kernel are we using.
unsigned nrows() const
Return the number of rows.
Definition: Matrix.h:108
std::vector< double > width
The width of the kernel.
Class for input files.
Definition: IFile.h:40
void const char const char int double int double double int int double int double * w
Definition: Matrix.h:42
bool diagonal
Is the metric matrix diagonal.
int logdet(const Matrix< T > &M, double &ldet)
Definition: Matrix.h:314
int diagMat(const Matrix< T > &A, std::vector< double > &eigenvals, Matrix< double > &eigenvecs)
Definition: Matrix.h:203
const double pi(3.141592653589793238462643383279502884197169399375105820974944592307)
PI.
IFile & scanField(const std::string &, double &)
Read a double field.
Definition: IFile.cpp:121
KernelFunctions(const std::vector< double > &at, const std::vector< double > &sig, const std::string &type, const bool multivariate, const double &w, const bool norm)
T norm(const std::vector< T > &A)
Calculate the dot product between a vector and itself.
Definition: Matrix.h:61