Program Listing for File rotations.cpp#
↰ Return to documentation for file (source/rotations.cpp)
#include "pele/rotations.hpp"
#include "pele/vecn.hpp"
namespace pele {
MatrixNM<3, 3> aa_to_rot_mat(pele::VecN<3> const &p) {
double theta2 = pele::dot<3>(p, p);
MatrixNM<3, 3> rm;
if (theta2 < 1e-12) {
MatrixNM<3, 3> temp;
rot_mat_derivatives_small_theta(p, rm, temp, temp, temp, false);
return rm;
}
// Execute for the general case, where THETA dos not equal zero
// Find values of THETA, CT, ST and THETA3
double theta = std::sqrt(theta2);
double ct = std::cos(theta);
double st = std::sin(theta);
// Set THETA to 1/THETA purely for convenience
theta = 1. / theta;
// Normalise p and construct the skew-symmetric matrix E
// ESQ is calculated as the square of E
pele::VecN<3> pn = p;
pn *= theta;
MatrixNM<3, 3> e(0);
e(0, 1) = -pn[2];
e(0, 2) = pn[1];
e(1, 2) = -pn[0];
e(1, 0) = -e(0, 1);
e(2, 0) = -e(0, 2);
e(2, 1) = -e(1, 2);
auto esq = pele::dot<3, 3, 3>(e, e);
// RM is calculated from Rodrigues' rotation formula [equation [1]
// in the paper]
// rm = np.eye(3) + [1. - ct] * esq + st * e
rm.assign(0);
for (size_t i = 0; i < 3; ++i) rm(i, i) = 1; // identiy matrix
for (size_t i = 0; i < 3; ++i) {
for (size_t j = 0; j < 3; ++j) {
rm(i, j) += (1. - ct) * esq(i, j) + st * e(i, j);
}
}
return rm;
}
pele::VecN<4> rot_mat_to_quaternion(pele::MatrixNM<3, 3> const &mx) {
VecN<4> q(0);
auto m = pele::transpose<3, 3>(mx);
double trace = m.trace();
double s;
if (trace > 0.) {
s = std::sqrt(trace + 1.0) * 2.0;
q[0] = 0.25 * s;
q[1] = (m(1, 2) - m(2, 1)) / s;
q[2] = (m(2, 0) - m(0, 2)) / s;
q[3] = (m(0, 1) - m(1, 0)) / s;
} else if ((m(0, 0) > m(1, 1)) and (m(0, 0) > m(2, 2))) {
s = std::sqrt(1.0 + m(0, 0) - m(1, 1) - m(2, 2)) * 2.0;
q[0] = (m(1, 2) - m(2, 1)) / s;
q[1] = 0.25 * s;
q[2] = (m(1, 0) + m(0, 1)) / s;
q[3] = (m(2, 0) + m(0, 2)) / s;
} else if (m(1, 1) > m(2, 2)) {
s = std::sqrt(1.0 + m(1, 1) - m(0, 0) - m(2, 2)) * 2.0;
q[0] = (m(2, 0) - m(0, 2)) / s;
q[1] = (m(1, 0) + m(0, 1)) / s;
q[2] = 0.25 * s;
q[3] = (m(2, 1) + m(1, 2)) / s;
} else {
s = std::sqrt(1.0 + m(2, 2) - m(0, 0) - m(1, 1)) * 2.0;
q[0] = (m(0, 1) - m(1, 0)) / s;
q[1] = (m(2, 0) + m(0, 2)) / s;
q[2] = (m(2, 1) + m(1, 2)) / s;
q[3] = 0.25 * s;
}
if (q[0] < 0) {
q *= -1;
}
return q;
}
pele::VecN<3> quaternion_to_aa(pele::VecN<4> const &qin) {
double eps = 1e-6;
VecN<3> p;
VecN<4> q = qin;
if (q[0] < 0.) {
q *= -1;
}
if (q[0] > 1.0) {
q /= std::sqrt(pele::dot<4>(q, q));
}
double theta = 2. * std::acos(q[0]);
double s = std::sqrt(1. - q[0] * q[0]);
for (size_t i = 0; i < 3; ++i) {
p[i] = q[i + 1];
}
if (s < eps) {
p *= 2.;
} else {
p *= theta / s;
// p = q[1:4] / s * theta
}
return p;
}
pele::VecN<4> aa_to_quaternion(pele::VecN<3> const &aa) {
static const double rot_epsilon = 1e-6;
VecN<4> q;
double thetah = 0.5 * pele::norm<3>(aa);
q[0] = std::cos(thetah);
// do linear expansion for small epsilon
VecN<3> v = aa;
if (thetah < rot_epsilon) {
v *= 0.5;
} else {
v *= 0.5 * std::sin(thetah) / thetah;
}
for (size_t i = 0; i < 3; ++i) {
q[i + 1] = v[i];
}
// make sure to have normal form
if (q[0] < 0.0) q *= -1;
return q;
}
pele::VecN<4> quaternion_multiply(pele::VecN<4> const &q0,
pele::VecN<4> const &q1) {
VecN<4> q3;
q3[0] = q0[0] * q1[0] - q0[1] * q1[1] - q0[2] * q1[2] - q0[3] * q1[3];
q3[1] = q0[0] * q1[1] + q0[1] * q1[0] + q0[2] * q1[3] - q0[3] * q1[2];
q3[2] = q0[0] * q1[2] - q0[1] * q1[3] + q0[2] * q1[0] + q0[3] * q1[1];
q3[3] = q0[0] * q1[3] + q0[1] * q1[2] - q0[2] * q1[1] + q0[3] * q1[0];
return q3;
}
void rot_mat_derivatives_small_theta(pele::VecN<3> const &p,
MatrixNM<3, 3> &rmat, MatrixNM<3, 3> &drm1,
MatrixNM<3, 3> &drm2, MatrixNM<3, 3> &drm3,
bool with_grad) {
double theta2 = pele::dot<3>(p, p);
if (theta2 > 1e-2) {
throw std::invalid_argument("theta must be small");
}
// Execute if the angle of rotation is zero
// In this case the rotation matrix is the identity matrix
rmat.assign(0);
for (size_t i = 0; i < 3; ++i) rmat(i, i) = 1; // identity matrix
// vr274> first order corrections to rotation matrix
rmat(0, 1) = -p[2];
rmat(1, 0) = p[2];
rmat(0, 2) = p[1];
rmat(2, 0) = -p[1];
rmat(1, 2) = -p[0];
rmat(2, 1) = p[0];
// If derivatives do not need to found, we're finished
if (not with_grad) {
return;
}
// hk286 - now up to the linear order in theta
drm1.assign(0);
drm1(0, 0) = 0.0;
drm1(0, 1) = p[1];
drm1(0, 2) = p[2];
drm1(1, 0) = p[1];
drm1(1, 1) = -2.0 * p[0];
drm1(1, 2) = -2.0;
drm1(2, 0) = p[2];
drm1(2, 1) = 2.0;
drm1(2, 2) = -2.0 * p[0];
drm1 *= 0.5;
drm2.assign(0);
drm2(0, 0) = -2.0 * p[1];
drm2(0, 1) = p[0];
drm2(0, 2) = 2.0;
drm2(1, 0) = p[0];
drm2(1, 1) = 0.0;
drm2(1, 2) = p[2];
drm2(2, 0) = -2.0;
drm2(2, 1) = p[2];
drm2(2, 2) = -2.0 * p[1];
drm2 *= 0.5;
drm3.assign(0);
drm3(0, 0) = -2.0 * p[2];
drm3(0, 1) = -2.0;
drm3(0, 2) = p[0];
drm3(1, 0) = 2.0;
drm3(1, 1) = -2.0 * p[2];
drm3(1, 2) = p[1];
drm3(2, 0) = p[0];
drm3(2, 1) = p[1];
drm3(2, 2) = 0.0;
drm3 *= 0.5;
}
void rot_mat_derivatives(pele::VecN<3> const &p, MatrixNM<3, 3> &rmat,
MatrixNM<3, 3> &drm1, MatrixNM<3, 3> &drm2,
MatrixNM<3, 3> &drm3) {
assert(p.size() == 3);
if (rmat.shape() != std::pair<size_t, size_t>(3, 3)) {
throw std::invalid_argument("rmat matrix has the wrong size");
}
if (drm1.shape() != std::pair<size_t, size_t>(3, 3)) {
throw std::invalid_argument("drm1 matrix has the wrong size");
}
if (drm2.shape() != std::pair<size_t, size_t>(3, 3)) {
throw std::invalid_argument("drm2 matrix has the wrong size");
}
if (drm3.shape() != std::pair<size_t, size_t>(3, 3)) {
throw std::invalid_argument("drm2 matrix has the wrong size");
}
double theta2 = pele::dot<3>(p, p);
if (theta2 < 1e-12) {
return rot_mat_derivatives_small_theta(p, rmat, drm1, drm2, drm3, true);
}
// Execute for the general case, where THETA dos not equal zero
// Find values of THETA, CT, ST and THETA3
double theta = std::sqrt(theta2);
double ct = std::cos(theta);
double st = std::sin(theta);
double theta3 = 1. / (theta2 * theta);
// Set THETA to 1/THETA purely for convenience
theta = 1. / theta;
// Normalise p and construct the skew-symmetric matrix E
// ESQ is calculated as the square of E
pele::VecN<3> pn = p;
pn *= theta;
MatrixNM<3, 3> e(0);
e(0, 1) = -pn[2];
e(0, 2) = pn[1];
e(1, 2) = -pn[0];
e(1, 0) = -e(0, 1);
e(2, 0) = -e(0, 2);
e(2, 1) = -e(1, 2);
auto esq = pele::dot<3, 3, 3>(e, e);
// compute the rotation matrix
// RM is calculated from Rodrigues' rotation formula [equation [1]
// in the paper]
// rm = np.eye(3) + [1. - ct] * esq + st * e
rmat.assign(0.);
for (size_t i = 0; i < 3; ++i) rmat(i, i) = 1; // identiy matrix
for (size_t i = 0; i < 3; ++i) {
for (size_t j = 0; j < 3; ++j) {
rmat(i, j) += (1. - ct) * esq(i, j) + st * e(i, j);
}
}
MatrixNM<3, 3> de1(0.);
de1(0, 1) = p[2] * p[0] * theta3;
de1(0, 2) = -p[1] * p[0] * theta3;
de1(1, 2) = -(theta - p[0] * p[0] * theta3);
de1(1, 0) = -de1(0, 1);
de1(2, 0) = -de1(0, 2);
de1(2, 1) = -de1(1, 2);
MatrixNM<3, 3> de2(0.);
de2(0, 1) = p[2] * p[1] * theta3;
de2(0, 2) = theta - p[1] * p[1] * theta3;
de2(1, 2) = p[0] * p[1] * theta3;
de2(1, 0) = -de2(0, 1);
de2(2, 0) = -de2(0, 2);
de2(2, 1) = -de2(1, 2);
MatrixNM<3, 3> de3(0.);
de3(0, 1) = -(theta - p[2] * p[2] * theta3);
de3(0, 2) = -p[1] * p[2] * theta3;
de3(1, 2) = p[0] * p[2] * theta3;
de3(1, 0) = -de3(0, 1);
de3(2, 0) = -de3(0, 2);
de3(2, 1) = -de3(1, 2);
// compute the x derivative of the rotation matrix
// drm1 = (st*pn[0]*esq + (1.-ct)*(de1.dot(e) + e.dot(de1))
// + ct*pn[0]*e + st*de1)
auto de_e = pele::dot<3, 3, 3>(de1, e);
auto ede_ = pele::dot<3, 3, 3>(e, de1);
for (size_t i = 0; i < 3; ++i) {
for (size_t j = 0; j < 3; ++j) {
drm1(i, j) =
(st * pn[0] * esq(i, j) + (1. - ct) * (de_e(i, j) + ede_(i, j)) +
ct * pn[0] * e(i, j) + st * de1(i, j));
}
}
// compute the y derivative of the rotation matrix
de_e = pele::dot<3, 3, 3>(de2, e);
ede_ = pele::dot<3, 3, 3>(e, de2);
for (size_t i = 0; i < 3; ++i) {
for (size_t j = 0; j < 3; ++j) {
drm2(i, j) =
(st * pn[1] * esq(i, j) + (1. - ct) * (de_e(i, j) + ede_(i, j)) +
ct * pn[1] * e(i, j) + st * de2(i, j));
}
}
// compute the z derivative of the rotation matrix
de_e = pele::dot<3, 3, 3>(de3, e);
ede_ = pele::dot<3, 3, 3>(e, de3);
for (size_t i = 0; i < 3; ++i) {
for (size_t j = 0; j < 3; ++j) {
drm3(i, j) =
(st * pn[2] * esq(i, j) + (1. - ct) * (de_e(i, j) + ede_(i, j)) +
ct * pn[2] * e(i, j) + st * de3(i, j));
}
}
}
} // namespace pele