pele
Python energy landscape explorer
|
00001 #include "pele/rotations.h" 00002 #include "pele/vecn.h" 00003 00004 namespace pele{ 00005 00006 00010 MatrixNM<3,3> aa_to_rot_mat(pele::VecN<3> const & p) 00011 { 00012 00013 double theta2 = pele::dot<3>(p,p); 00014 MatrixNM<3,3> rm; 00015 if (theta2 < 1e-12) { 00016 MatrixNM<3,3> temp; 00017 rot_mat_derivatives_small_theta(p, rm, temp, temp, temp, false); 00018 return rm; 00019 } 00020 // Execute for the general case, where THETA dos not equal zero 00021 // Find values of THETA, CT, ST and THETA3 00022 double theta = std::sqrt(theta2); 00023 double ct = std::cos(theta); 00024 double st = std::sin(theta); 00025 00026 // Set THETA to 1/THETA purely for convenience 00027 theta = 1./theta; 00028 00029 // Normalise p and construct the skew-symmetric matrix E 00030 // ESQ is calculated as the square of E 00031 pele::VecN<3> pn = p; 00032 pn*= theta; 00033 MatrixNM<3,3> e(0); 00034 e(0,1) = -pn[2]; 00035 e(0,2) = pn[1]; 00036 e(1,2) = -pn[0]; 00037 e(1,0) = -e(0,1); 00038 e(2,0) = -e(0,2); 00039 e(2,1) = -e(1,2); 00040 auto esq = pele::dot<3,3,3>(e, e); 00041 00042 // RM is calculated from Rodrigues' rotation formula [equation [1] 00043 // in the paper] 00044 // rm = np.eye(3) + [1. - ct] * esq + st * e 00045 rm.assign(0); 00046 for (size_t i = 0; i < 3; ++i) rm(i,i) = 1; // identiy matrix 00047 for (size_t i = 0; i < 3; ++i) { 00048 for (size_t j = 0; j < 3; ++j) { 00049 rm(i,j) += (1.-ct) * esq(i,j) + st * e(i,j); 00050 } 00051 } 00052 return rm; 00053 } 00054 00055 pele::VecN<4> rot_mat_to_quaternion(pele::MatrixNM<3,3> const & mx) 00056 { 00057 VecN<4> q(0); 00058 auto m = pele::transpose<3,3>(mx); 00059 double trace = m.trace(); 00060 00061 double s; 00062 if (trace > 0.){ 00063 s = std::sqrt(trace+1.0) * 2.0; 00064 q[0] = 0.25 * s; 00065 q[1] = (m(1,2) - m(2,1)) / s; 00066 q[2] = (m(2,0) - m(0,2)) / s; 00067 q[3] = (m(0,1) - m(1,0)) / s; 00068 } else if ((m(0,0) > m(1,1)) and (m(0,0) > m(2,2))) { 00069 s = std::sqrt(1.0 + m(0,0) - m(1,1) - m(2,2)) * 2.0; 00070 q[0] = (m(1,2) - m(2,1)) / s; 00071 q[1] = 0.25 * s; 00072 q[2] = (m(1,0) + m(0,1)) / s; 00073 q[3] = (m(2,0) + m(0,2)) / s; 00074 } else if (m(1,1) > m(2,2)) { 00075 s = std::sqrt(1.0 + m(1,1) - m(0,0) - m(2,2)) * 2.0; 00076 q[0] = (m(2,0) - m(0,2)) / s; 00077 q[1] = (m(1,0) + m(0,1)) / s; 00078 q[2] = 0.25 * s; 00079 q[3] = (m(2,1) + m(1,2)) / s; 00080 } else { 00081 s = std::sqrt(1.0 + m(2,2) - m(0,0) - m(1,1)) * 2.0; 00082 q[0] = (m(0,1) - m(1,0)) / s; 00083 q[1] = (m(2,0) + m(0,2)) / s; 00084 q[2] = (m(2,1) + m(1,2)) / s; 00085 q[3] = 0.25 * s; 00086 } 00087 00088 if (q[0] < 0) { 00089 q *= -1; 00090 } 00091 return q; 00092 } 00093 00094 pele::VecN<3> quaternion_to_aa(pele::VecN<4> const & qin) 00095 { 00096 double eps = 1e-6; 00097 VecN<3> p; 00098 VecN<4> q = qin; 00099 if (q[0] < 0.) { 00100 q *= -1; 00101 } 00102 if (q[0] > 1.0) { 00103 q /= std::sqrt(pele::dot<4>(q,q)); 00104 } 00105 double theta = 2. * std::acos(q[0]); 00106 double s = std::sqrt(1. - q[0]*q[0]); 00107 for (size_t i = 0; i < 3; ++i) { 00108 p[i] = q[i+1]; 00109 } 00110 if (s < eps) { 00111 p *= 2.; 00112 } else { 00113 p *= theta / s; 00114 // p = q[1:4] / s * theta 00115 } 00116 return p; 00117 } 00118 00119 pele::VecN<4> aa_to_quaternion(pele::VecN<3> const & aa) 00120 { 00121 static const double rot_epsilon = 1e-6; 00122 VecN<4> q; 00123 00124 double thetah = 0.5 * pele::norm<3>(aa); 00125 q[0] = std::cos(thetah); 00126 00127 // do linear expansion for small epsilon 00128 VecN<3> v = aa; 00129 if (thetah < rot_epsilon) { 00130 v *= 0.5; 00131 } else { 00132 v *= 0.5 * std::sin(thetah) / thetah; 00133 } 00134 for (size_t i = 0; i < 3; ++i) { 00135 q[i+1] = v[i]; 00136 } 00137 00138 // make sure to have normal form 00139 if (q[0] < 0.0) q *= -1; 00140 return q; 00141 00142 } 00143 00144 00145 pele::VecN<4> quaternion_multiply(pele::VecN<4> const & q0, pele::VecN<4> const & q1) 00146 { 00147 VecN<4> q3; 00148 q3[0] = q0[0]*q1[0]-q0[1]*q1[1]-q0[2]*q1[2]-q0[3]*q1[3]; 00149 q3[1] = q0[0]*q1[1]+q0[1]*q1[0]+q0[2]*q1[3]-q0[3]*q1[2]; 00150 q3[2] = q0[0]*q1[2]-q0[1]*q1[3]+q0[2]*q1[0]+q0[3]*q1[1]; 00151 q3[3] = q0[0]*q1[3]+q0[1]*q1[2]-q0[2]*q1[1]+q0[3]*q1[0]; 00152 return q3; 00153 } 00154 00155 00159 void rot_mat_derivatives_small_theta( 00160 pele::VecN<3> const & p, 00161 MatrixNM<3,3> & rmat, 00162 MatrixNM<3,3> & drm1, 00163 MatrixNM<3,3> & drm2, 00164 MatrixNM<3,3> & drm3, 00165 bool with_grad) 00166 { 00167 double theta2 = pele::dot<3>(p, p); 00168 if (theta2 > 1e-2) { 00169 throw std::invalid_argument("theta must be small"); 00170 } 00171 // Execute if the angle of rotation is zero 00172 // In this case the rotation matrix is the identity matrix 00173 rmat.assign(0); 00174 for (size_t i = 0; i<3; ++i) rmat(i,i) = 1; // identity matrix 00175 00176 00177 // vr274> first order corrections to rotation matrix 00178 rmat(0,1) = -p[2]; 00179 rmat(1,0) = p[2]; 00180 rmat(0,2) = p[1]; 00181 rmat(2,0) = -p[1]; 00182 rmat(1,2) = -p[0]; 00183 rmat(2,1) = p[0]; 00184 00185 // If derivatives do not need to found, we're finished 00186 if (not with_grad) { 00187 return; 00188 } 00189 00190 // hk286 - now up to the linear order in theta 00191 drm1.assign(0); 00192 drm1(0,0) = 0.0; 00193 drm1(0,1) = p[1]; 00194 drm1(0,2) = p[2]; 00195 drm1(1,0) = p[1]; 00196 drm1(1,1) = -2.0*p[0]; 00197 drm1(1,2) = -2.0; 00198 drm1(2,0) = p[2]; 00199 drm1(2,1) = 2.0; 00200 drm1(2,2) = -2.0*p[0]; 00201 drm1 *= 0.5; 00202 00203 drm2.assign(0); 00204 drm2(0,0) = -2.0*p[1]; 00205 drm2(0,1) = p[0]; 00206 drm2(0,2) = 2.0; 00207 drm2(1,0) = p[0]; 00208 drm2(1,1) = 0.0; 00209 drm2(1,2) = p[2]; 00210 drm2(2,0) = -2.0; 00211 drm2(2,1) = p[2]; 00212 drm2(2,2) = -2.0*p[1]; 00213 drm2 *= 0.5; 00214 00215 drm3.assign(0); 00216 drm3(0,0) = -2.0*p[2]; 00217 drm3(0,1) = -2.0; 00218 drm3(0,2) = p[0]; 00219 drm3(1,0) = 2.0; 00220 drm3(1,1) = -2.0*p[2]; 00221 drm3(1,2) = p[1]; 00222 drm3(2,0) = p[0]; 00223 drm3(2,1) = p[1]; 00224 drm3(2,2) = 0.0; 00225 drm3 *= 0.5; 00226 } 00227 00228 00232 void rot_mat_derivatives( 00233 pele::VecN<3> const & p, 00234 MatrixNM<3,3> & rmat, 00235 MatrixNM<3,3> & drm1, 00236 MatrixNM<3,3> & drm2, 00237 MatrixNM<3,3> & drm3) 00238 { 00239 assert(p.size() == 3); 00240 if (rmat.shape() != std::pair<size_t, size_t>(3,3)) { 00241 throw std::invalid_argument("rmat matrix has the wrong size"); 00242 } 00243 if (drm1.shape() != std::pair<size_t, size_t>(3,3)) { 00244 throw std::invalid_argument("drm1 matrix has the wrong size"); 00245 } 00246 if (drm2.shape() != std::pair<size_t, size_t>(3,3)) { 00247 throw std::invalid_argument("drm2 matrix has the wrong size"); 00248 } 00249 if (drm3.shape() != std::pair<size_t, size_t>(3,3)) { 00250 throw std::invalid_argument("drm2 matrix has the wrong size"); 00251 } 00252 00253 double theta2 = pele::dot<3>(p,p); 00254 if (theta2 < 1e-12) { 00255 return rot_mat_derivatives_small_theta(p, rmat, drm1, drm2, drm3, true); 00256 } 00257 // Execute for the general case, where THETA dos not equal zero 00258 // Find values of THETA, CT, ST and THETA3 00259 double theta = std::sqrt(theta2); 00260 double ct = std::cos(theta); 00261 double st = std::sin(theta); 00262 double theta3 = 1./(theta2*theta); 00263 00264 00265 // Set THETA to 1/THETA purely for convenience 00266 theta = 1./theta; 00267 00268 // Normalise p and construct the skew-symmetric matrix E 00269 // ESQ is calculated as the square of E 00270 pele::VecN<3> pn = p; 00271 pn *= theta; 00272 MatrixNM<3,3> e(0); 00273 e(0,1) = -pn[2]; 00274 e(0,2) = pn[1]; 00275 e(1,2) = -pn[0]; 00276 e(1,0) = -e(0,1); 00277 e(2,0) = -e(0,2); 00278 e(2,1) = -e(1,2); 00279 auto esq = pele::dot<3,3,3>(e, e); 00280 00281 // compute the rotation matrix 00282 // RM is calculated from Rodrigues' rotation formula [equation [1] 00283 // in the paper] 00284 // rm = np.eye(3) + [1. - ct] * esq + st * e 00285 rmat.assign(0.); 00286 for (size_t i = 0; i < 3; ++i) rmat(i,i) = 1; // identiy matrix 00287 for (size_t i = 0; i < 3; ++i) { 00288 for (size_t j = 0; j < 3; ++j) { 00289 rmat(i,j) += (1.-ct) * esq(i,j) + st * e(i,j); 00290 } 00291 } 00292 00293 MatrixNM<3,3> de1(0.); 00294 de1(0,1) = p[2]*p[0]*theta3; 00295 de1(0,2) = -p[1]*p[0]*theta3; 00296 de1(1,2) = -(theta - p[0]*p[0]*theta3); 00297 de1(1,0) = -de1(0,1); 00298 de1(2,0) = -de1(0,2); 00299 de1(2,1) = -de1(1,2); 00300 00301 MatrixNM<3,3> de2(0.); 00302 de2(0,1) = p[2]*p[1]*theta3; 00303 de2(0,2) = theta - p[1]*p[1]*theta3; 00304 de2(1,2) = p[0]*p[1]*theta3; 00305 de2(1,0) = -de2(0,1); 00306 de2(2,0) = -de2(0,2); 00307 de2(2,1) = -de2(1,2); 00308 00309 MatrixNM<3,3> de3(0.); 00310 de3(0,1) = -(theta - p[2]*p[2]*theta3); 00311 de3(0,2) = -p[1]*p[2]*theta3; 00312 de3(1,2) = p[0]*p[2]*theta3; 00313 de3(1,0) = -de3(0,1); 00314 de3(2,0) = -de3(0,2); 00315 de3(2,1) = -de3(1,2); 00316 00317 // compute the x derivative of the rotation matrix 00318 // drm1 = (st*pn[0]*esq + (1.-ct)*(de1.dot(e) + e.dot(de1)) 00319 // + ct*pn[0]*e + st*de1) 00320 auto de_e = pele::dot<3,3,3>(de1, e); 00321 auto ede_ = pele::dot<3,3,3>(e, de1); 00322 for (size_t i = 0; i < 3; ++i) { 00323 for (size_t j = 0; j < 3; ++j) { 00324 drm1(i,j) = (st * pn[0] * esq(i,j) 00325 + (1.-ct) * (de_e(i,j) + ede_(i,j)) 00326 + ct * pn[0] * e(i,j) 00327 + st * de1(i,j) 00328 ); 00329 } 00330 } 00331 00332 // compute the y derivative of the rotation matrix 00333 de_e = pele::dot<3,3,3>(de2, e); 00334 ede_ = pele::dot<3,3,3>(e, de2); 00335 for (size_t i = 0; i < 3; ++i) { 00336 for (size_t j = 0; j < 3; ++j) { 00337 drm2(i,j) = (st * pn[1] * esq(i,j) 00338 + (1.-ct) * (de_e(i,j) + ede_(i,j)) 00339 + ct * pn[1] * e(i,j) 00340 + st * de2(i,j) 00341 ); 00342 } 00343 } 00344 00345 // compute the z derivative of the rotation matrix 00346 de_e = pele::dot<3,3,3>(de3, e); 00347 ede_ = pele::dot<3,3,3>(e, de3); 00348 for (size_t i = 0; i < 3; ++i) { 00349 for (size_t j = 0; j < 3; ++j) { 00350 drm3(i,j) = (st * pn[2] * esq(i,j) 00351 + (1.-ct) * (de_e(i,j) + ede_(i,j)) 00352 + ct * pn[2] * e(i,j) 00353 + st * de3(i,j) 00354 ); 00355 } 00356 } 00357 } 00358 00359 } // namespace pele