00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00024
00025 #ifndef BTK_ALGORITHMS_RMSD_HPP
00026 #define BTK_ALGORITHMS_RMSD_HPP
00027
00028 #include <cmath>
00029 #include <functional>
00030 #include <algorithm>
00031
00032 #include <btk/core/math/btk_vector.hpp>
00033 #include <btk/core/math/btk_matrix.hpp>
00034 #include <btk/core/math/linear_algebra.hpp>
00035
00036 namespace BTK {
00037 namespace ALGORITHMS {
00038
00047
00048 #ifndef DOXYGEN_SHOULD_SKIP_THIS
00049 namespace internal {
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066 void fast_solve_3D_eigenvectors(BTK::MATH::BTKMatrix & M,
00067 double const evalues[3],
00068 BTK::MATH::BTKVector evecs[3]);
00069
00070
00071
00072
00073
00074
00075
00076
00077 void fast_solve_3D_eigenvalues(BTK::MATH::BTKMatrix const & M,
00078 double e_vals[3]);
00079
00080
00081
00082 inline double determinant(BTK::MATH::BTKMatrix const & M)
00083 {
00084 using boost::numeric::ublas::inner_prod;
00085 using BTK::MATH::cross;
00086 return inner_prod(row(M,0),cross(row(M,1),row(M,2)));
00087 }
00088
00089 template <typename AtomIterator1, typename AtomIterator2>
00090 void
00091 setup_rmsd(AtomIterator1 first1,
00092 AtomIterator1 last1,
00093 AtomIterator2 first2,
00094 AtomIterator2 last2,
00095 BTK::MATH::BTKVector & T1,
00096 BTK::MATH::BTKVector & T2,
00097 BTK::MATH::BTKMatrix & R,
00098 BTK::MATH::BTKMatrix & RtR,
00099 double & Eo,
00100 unsigned & N,
00101 double & omega,
00102 double e_vals[3])
00103 {
00104
00105
00106 T1 = BTK::MATH::BTKVector(0.0);
00107 T2 = BTK::MATH::BTKVector(0.0);
00108 AtomIterator1 i1(first1);
00109 AtomIterator2 i2(first2);
00110
00111 for (N = 0;
00112 i1 != last1 && i2 != last2;
00113 ++i1,++i2) {
00114 T1 += i2->position();
00115 T2 += i1->position();
00116 N++;
00117 }
00118
00119 if (i2 != last2 || i1 != last1) {
00120 std::cerr << "Warning: attempting to compute RMSD between "
00121 << "structures of different lengths! " << std::endl
00122 << "Assuming atom length of smaller structure (" << N << ")...."
00123 << std::endl;
00124 }
00125
00126 T1 /= -1.*static_cast<double>(N);
00127 T2 /= static_cast<double>(N);
00128
00129
00130
00131
00132 int i = 0;
00133 BTK::MATH::BTKMatrix tmp1(3,N),tmp2(3,N);
00134 for (i1 = first1, i2 = first2;
00135 i1 != last1 && i2 != last2;
00136 ++i1,++i2) {
00137 column(tmp1,i) = i1->position() - T2;
00138 column(tmp2,i) = i2->position() + T1;
00139 i++;
00140 }
00141
00142 Eo = 0;
00143 for (unsigned j = 0; j < 3; ++j) {
00144 Eo += inner_prod(row(tmp1,j),row(tmp1,j)) +
00145 inner_prod(row(tmp2,j),row(tmp2,j));
00146 for (unsigned k = 0; k < 3; ++k) {
00147 R(j,k) = inner_prod(row(tmp1,j),row(tmp2,k));
00148 }
00149 }
00150 Eo /= 2.0;
00151
00152
00153 if (determinant(R) > 0) omega = 1;
00154 else omega = -1;
00155
00156 RtR = prec_prod(trans(R),R);
00157
00158 internal::fast_solve_3D_eigenvalues(RtR,e_vals);
00159
00160
00161
00162
00163
00164
00165
00166
00167 for (unsigned i = 0; i < 3; ++i) {
00168 if (e_vals[i] > 0) e_vals[i] = std::sqrt(e_vals[i]);
00169 else e_vals[i] = 0.0;
00170 }
00171
00172 std::sort(e_vals,e_vals + 3,
00173 std::greater<double>());
00174 }
00175
00176 }
00177 #endif // DOXYGEN_SHOULD_SKIP_THIS
00178
00195 template <typename AtomIterator1, typename AtomIterator2>
00196 double
00197 leastsquares_superposition(AtomIterator1 fixed_first,
00198 AtomIterator1 fixed_last,
00199 AtomIterator2 superimpose_first,
00200 AtomIterator2 superimpose_last,
00201 BTK::MATH::BTKMatrix & U,
00202 BTK::MATH::BTKVector & T)
00203 {
00204 double Eo, omega;
00205 double e_vals[3];
00206 BTK::MATH::BTKMatrix R,RtR;
00207 BTK::MATH::BTKVector T1;
00208 BTK::MATH::BTKVector T2;
00209 unsigned N;
00210
00211 internal::setup_rmsd(fixed_first,
00212 fixed_last,
00213 superimpose_first,
00214 superimpose_last,
00215 T1,T2,R,RtR,Eo,N,
00216 omega,e_vals);
00217
00218 BTK::MATH::BTKVector right_evecs[3];
00219
00220
00221
00222
00223
00224 e_vals[0] *= e_vals[0];
00225 e_vals[1] *= e_vals[1];
00226 e_vals[2] *= e_vals[2];
00227
00228 internal::fast_solve_3D_eigenvectors(RtR,e_vals,right_evecs);
00229
00230
00231
00232
00233
00234 BTK::MATH::BTKVector left_evecs[3];
00235
00236 left_evecs[0] = prec_prod(R,right_evecs[0]);
00237 left_evecs[1] = prec_prod(R,right_evecs[1]);
00238
00239
00240 left_evecs[0] /= norm_2(left_evecs[0]);
00241 left_evecs[1] /= norm_2(left_evecs[1]);
00242
00243
00244
00245 left_evecs[2] = cross(left_evecs[0],left_evecs[1]);
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257 U.clear();
00258 for (unsigned i = 0; i < 3; ++i) {
00259 for (unsigned j = 0; j < 3; ++j) {
00260 U(i,j) = (left_evecs[0][i]*right_evecs[0][j]) +
00261 (left_evecs[1][i]*right_evecs[1][j]) +
00262 (left_evecs[2][i]*right_evecs[2][j]);
00263 }
00264 }
00265
00266
00267 T = prec_prod(U,T1)+T2;
00268
00269
00270
00271 double rmsd = (2.0/static_cast<double>(N))*
00272 (Eo - e_vals[0] - e_vals[1] - omega*e_vals[2]);
00273
00274 double const sqrt_min = 1e-8;
00275 if(rmsd > sqrt_min) { rmsd = std::sqrt(rmsd); }
00276 else { rmsd = 0; }
00277
00278 return rmsd;
00279 }
00280
00291 template <typename AtomIterator1, typename AtomIterator2, typename AtomIterator3>
00292 double
00293 leastsquares_superposition(AtomIterator1 fixed_first,
00294 AtomIterator1 fixed_last,
00295 AtomIterator2 superimpose_first,
00296 AtomIterator2 superimpose_last,
00297 AtomIterator3 transform_first,
00298 AtomIterator3 transform_last)
00299 {
00300 BTK::MATH::BTKVector T;
00301 BTK::MATH::BTKMatrix U;
00302 double rmsd =
00303 leastsquares_superposition(fixed_first,fixed_last,
00304 superimpose_first,superimpose_last,
00305 U,T);
00306
00307 for(AtomIterator3 ai=transform_first; ai != transform_last; ++ai)
00308 ai->set_position(prec_prod(U,ai->position())+T);
00309
00310 return rmsd;
00311 }
00312
00323 template <typename AtomIterator1, typename AtomIterator2>
00324 double
00325 get_rmsd(AtomIterator1 a1_first,
00326 AtomIterator1 a1_last,
00327 AtomIterator2 a2_first,
00328 AtomIterator2 a2_last)
00329 {
00330 double Eo, omega;
00331 double e_vals[3];
00332 BTK::MATH::BTKMatrix R,RtR;
00333 unsigned N;
00334 BTK::MATH::BTKVector T1,T2;
00335
00336 internal::setup_rmsd(a1_first,a1_last,a2_first,a2_last,
00337 T1,T2,R,RtR,Eo,N,omega,e_vals);
00338
00339 double rmsd = (2.0/static_cast<double>(N))*
00340 (Eo - e_vals[0] - e_vals[1] - omega*e_vals[2]);
00341
00342 double const sqrt_min = 1e-8;
00343 if(rmsd > sqrt_min) { rmsd = std::sqrt(rmsd); }
00344 else { rmsd = 0; }
00345
00346 return rmsd;
00347 }
00348
00356 template <typename AtomIterator1, typename AtomIterator2>
00357 double
00358 get_trivial_rmsd(AtomIterator1 a1_first,
00359 AtomIterator1 a1_last,
00360 AtomIterator2 a2_first,
00361 AtomIterator2 a2_last)
00362 {
00363 double rmsd2 = 0.;
00364 unsigned N = 0;
00365
00366 AtomIterator1 a1 = a1_first;
00367 AtomIterator2 a2 = a2_first;
00368
00369 for(;a1!=a1_last && a2!=a2_last;++a1,++a2){
00370 BTK::MATH::BTKVector v(a1->position()-a2->position());
00371 rmsd2 += inner_prod(v,v);
00372 N++;
00373 }
00374
00375 if(a1!=a1_last || a2!=a2_last) {
00376 std::cerr << "Warning: attempting to compute RMSD between "
00377 << "structures of different lengths! " << std::endl
00378 << "Assuming atom length of smaller structure (" << N << ")...."
00379 << std::endl;
00380 }
00381
00382 if(N) rmsd2 /= static_cast<double>(N);
00383
00384 double const sqrt_min = 1e-8;
00385 if(rmsd2 > sqrt_min) { rmsd2 = std::sqrt(rmsd2); }
00386 else { rmsd2 = 0; }
00387
00388 return rmsd2;
00389 }
00390
00391 }
00392 }
00393
00645 #endif
00646
00647