1 #ifndef CASM_Eigen_math
2 #define CASM_Eigen_math
4 #include <boost/math/special_functions/round.hpp>
11 #include "./KroneckerTensorProduct.h"
12 #include "casm/external/Eigen/Eigenvalues"
19 inline static bool _compare(T
const &a, T
const &b,
double tol) {
24 inline bool _compare(std::complex<double>
const &a,
25 std::complex<double>
const &b,
double tol) {
27 return real(a) < real(b);
29 return imag(a) < imag(b);
36 auto round_l = [](T val) {
return boost::math::round<T>(val); };
38 auto iround_l = [](T val) {
return boost::math::iround<T>(val); };
40 auto lround_l = [](T val) {
return boost::math::lround<T>(val); };
46 const Eigen::Ref<const Eigen::MatrixXd> &A,
47 const Eigen::Ref<const Eigen::MatrixXd> &B,
double tol) {
49 B.data() + B.size(), tol);
53 template <
typename Derived>
55 const Eigen::MatrixBase<Derived> &B,
double tol) {
56 if (A.cols() == B.cols()) {
57 if (A.rows() == B.rows()) {
58 for (
Index j = 0; j < A.cols(); ++j) {
59 for (
Index i = 0; i < A.rows(); ++i) {
65 }
else if (A.rows() < B.rows())
69 }
else if (A.cols() < B.cols())
77 template <
typename Derived>
79 Eigen::MatrixBase<Derived>
const &M,
double _tol) {
80 typename Derived::PlainObject R(M);
82 for (
Index row = 0; row < R.rows(); ++row) {
84 for (i = col; i < R.cols(); ++i) {
86 if (i != col) R.col(i).swap(R.col(col));
90 if (i == R.cols())
continue;
91 R.col(col) /= R(row, col);
92 for (i = 0; i < R.cols(); ++i) {
93 if (i != col) R.col(i) -= R(row, i) * R.col(col);
97 return R.leftCols(col);
108 std::vector<Index> &optimal_assignments,
113 template <
typename Derived>
114 typename Derived::Scalar *
end_ptr(Eigen::PlainObjectBase<Derived> &container) {
115 return container.data() + container.size();
120 template <
typename Derived>
122 Eigen::PlainObjectBase<Derived>
const &container) {
123 return container.data() + container.size();
135 Eigen::MatrixXcd &hermitian_mat,
136 Eigen::MatrixXcd &antihermitian_mat);
141 const Eigen::MatrixXi &M);
144 double angle(
const Eigen::Ref<const Eigen::Vector3d> &a,
145 const Eigen::Ref<const Eigen::Vector3d> &b);
149 double signed_angle(
const Eigen::Ref<const Eigen::Vector3d> &a,
150 const Eigen::Ref<const Eigen::Vector3d> &b,
151 const Eigen::Ref<const Eigen::Vector3d> &pos_ref);
157 template <
typename Derived>
160 const Derived &vec2) {
161 return (vec0.cross(vec1)).dot(vec2);
165 template <
typename Derived>
166 bool is_integer(
const Eigen::MatrixBase<Derived> &M,
double tol) {
167 for (
Index i = 0; i < M.rows(); i++) {
168 for (
Index j = 0; j < M.cols(); j++) {
177 template <
typename Derived>
181 static_cast<typename Derived::Scalar
>(1), tol);
185 template <
typename Derived>
187 return M.isDiagonal(tol);
199 template <
typename Derived>
200 Eigen::CwiseUnaryOp<decltype(Local::round_l<typename Derived::Scalar>),
202 round(
const Eigen::MatrixBase<Derived> &val) {
203 return val.unaryExpr(Local::round_l<typename Derived::Scalar>);
215 template <
typename Derived>
216 Eigen::CwiseUnaryOp<decltype(Local::iround_l<typename Derived::Scalar>),
218 iround(
const Eigen::MatrixBase<Derived> &val) {
219 return val.unaryExpr(Local::iround_l<typename Derived::Scalar>);
231 template <
typename Derived>
232 Eigen::CwiseUnaryOp<decltype(Local::lround_l<typename Derived::Scalar>),
234 lround(
const Eigen::MatrixBase<Derived> &val) {
235 return val.unaryExpr(Local::lround_l<typename Derived::Scalar>);
249 template <
typename Derived>
250 typename Derived::Scalar
matrix_minor(
const Eigen::MatrixBase<Derived> &M,
254 Eigen::Matrix<
typename Derived::Scalar, Derived::RowsAtCompileTime - 1,
255 Derived::ColsAtCompileTime - 1>
256 subM(M.rows() - 1, M.cols() - 1);
258 for (i = 0; i < row; ++i) {
259 for (j = 0; j < col; ++j) {
260 subM(i, j) = M(i, j);
262 for (++j; j < M.cols(); ++j) {
263 subM(i, j - 1) = M(i, j);
267 for (++i; i < M.rows(); ++i) {
268 for (j = 0; j < col; ++j) {
269 subM(i - 1, j) = M(i, j);
271 for (++j; j < M.cols(); ++j) {
272 subM(i - 1, j - 1) = M(i, j);
277 return subM.determinant();
289 template <
typename Derived>
290 Eigen::Matrix<
typename Derived::Scalar, Derived::RowsAtCompileTime,
291 Derived::ColsAtCompileTime>
293 Eigen::Matrix<
typename Derived::Scalar, Derived::RowsAtCompileTime,
294 Derived::ColsAtCompileTime>
295 Mcof(M.rows(), M.cols());
296 for (
int i = 0; i < Mcof.rows(); i++) {
297 for (
int j = 0; j < Mcof.cols(); j++) {
298 Mcof(i, j) = ((((i + j) % 2) == 0) ?
matrix_minor(M, i, j)
317 template <
typename Derived>
318 Eigen::Matrix<
typename Derived::Scalar, Derived::RowsAtCompileTime,
319 Derived::ColsAtCompileTime>
324 namespace normal_form_impl {
334 template <
typename Scalar>
337 typedef Eigen::Matrix<Scalar, 3, 3> matrix_type;
339 Scalar tgcf =
extended_gcf(a, b, tmat(i, i), tmat(i, j));
343 tmat(j, i) = -b / tgcf;
344 tmat(j, j) = a / tgcf;
356 template <
typename Derived>
357 Eigen::Matrix<
typename Derived::Scalar, Derived::RowsAtCompileTime,
358 Derived::ColsAtCompileTime>
359 inverse(
const Eigen::MatrixBase<Derived> &M) {
360 return adjugate(M) / M.determinant();
375 template <
typename DerivedIn,
typename DerivedOut>
377 Eigen::MatrixBase<DerivedOut> &U,
378 Eigen::MatrixBase<DerivedOut> &S,
379 Eigen::MatrixBase<DerivedOut> &V) {
380 static_assert(std::is_same<
typename DerivedIn::Scalar,
381 typename DerivedOut::Scalar>::value,
382 "ALL ARGUMENTS TO CASM::smith_normal_form() MUST BE BASED ON "
383 "SAME SCALAR TYPE!");
384 using namespace normal_form_impl;
385 typedef typename DerivedOut::Scalar Scalar;
396 for (j = 0; j < 3; j++) {
398 for (i = j + 1; i < 3; i++) {
399 if (S(i, j) == 0)
continue;
400 tmat = _elementary_hermite_op<Scalar>(S(j, j), S(i, j), j, i);
406 for (i = j + 2; i < 3; i++) {
407 if (S(j, i) == 0)
continue;
408 tmat = _elementary_hermite_op<Scalar>(S(j, j + 1), S(j, i), j + 1, i);
409 S = S * tmat.transpose();
410 V =
inverse(tmat.transpose()) * V;
416 while (!S.isDiagonal()) {
419 for (b = 0; b < 2; b++) {
420 if (S(b, b + 1))
break;
426 for (i = 0; i < 3; i++) {
435 Scalar q = S(b, b + 1) / S(b, b);
436 if (S(b, b + 1) % S(b, b) < 0) q -= 1;
439 S = S * tmat.transpose();
440 V =
inverse(tmat.transpose()) * V;
447 tmat(b + 1, b + 1) = 0;
449 S = S * tmat.transpose();
450 V =
inverse(tmat.transpose()) * V;
453 if (!S(b, b + 1))
continue;
455 tmat = _elementary_hermite_op<Scalar>(S(b, b), S(b, b + 1), b, b + 1);
456 S = S * tmat.transpose();
457 V =
inverse(tmat.transpose()) * V;
458 for (j = 0; j < 2; j++) {
459 tmat = _elementary_hermite_op<Scalar>(S(j, j), S(j + 1, j), j, j + 1);
463 if (j + 2 >= 3)
continue;
465 tmat = _elementary_hermite_op<Scalar>(S(j, j + 1), S(j, j + 2), j + 1,
467 S = S * tmat.transpose();
468 V =
inverse(tmat.transpose()) * V;
473 for (j = 0; j < 3; j++) {
475 for (i = 0; i < 3; i++) {
483 for (i = 0; i < 3; i++) {
484 for (j = i + 1; j < 3; j++) {
485 if (S(i, i) > S(j, j)) {
486 S.row(i).swap(S.row(j));
487 S.col(i).swap(S.col(j));
488 U.col(i).swap(U.col(j));
489 V.row(i).swap(V.row(j));
494 for (i = 0; i < 3; i++) {
495 if (S(i, i) == 0)
continue;
496 for (j = i + 1; j < 3; j++) {
497 if (S(j, j) % S(i, i) == 0)
continue;
500 DerivedOut tmat2(tmat);
501 Scalar a(S(i, i)), b(S(j, j)), c, d, tgcf;
505 tmat(j, i) = -b / tgcf;
506 tmat(j, j) = (a * c) / tgcf;
510 tmat2(j, i) = -(b * d) / tgcf;
511 tmat2(j, j) = a / tgcf;
512 S = tmat * S * tmat2.transpose();
514 V =
inverse(tmat2.transpose()) * V;
527 template <
int range = 1>
529 static std::vector<Eigen::Matrix3i> static_all(
563 template <
typename Derived>
565 return val.isZero(tol);
568 template <
typename Derived>
569 double length(
const Eigen::MatrixBase<Derived> &value) {
576 template <
typename Derived>
577 Eigen::Matrix<int, Derived::RowsAtCompileTime, Derived::ColsAtCompileTime>
582 typedef Eigen::Matrix<int, Derived::RowsAtCompileTime,
583 Derived::ColsAtCompileTime>
585 typedef Eigen::Matrix<double, Derived::RowsAtCompileTime,
586 Derived::ColsAtCompileTime>
589 int_mat_type ints(int_mat_type::Zero(val.rows(), val.cols()));
591 dub_mat_type dubs(val);
594 double min_coeff = 2;
595 for (
Index i = 0; i < dubs.rows(); i++) {
596 for (
Index j = 0; j < dubs.cols(); j++) {
599 }
else if (std::abs(dubs(i, j)) < std::abs(min_coeff)) {
600 min_coeff = dubs(i, j);
606 dubs /= std::abs(min_coeff);
624 bool within_tol =
false;
628 for (
Index factor = 1; factor < 1000 && !within_tol; factor++) {
629 tdubs = double(factor) * dubs;
630 for (i = 0; i < dubs.rows(); i++) {
631 for (j = 0; j < dubs.cols(); j++) {
636 if (j < dubs.cols())
break;
638 if (dubs.rows() <= i) within_tol =
true;
642 for (i = 0; i < dubs.rows(); i++) {
643 for (j = 0; j < dubs.cols(); j++) {
652 template <
typename Derived1,
typename Derived2>
654 const Eigen::MatrixBase<Derived2> &val2,
666 template <
typename Derived>
669 return almost_zero(test_mat - test_mat.transpose(), test_tol);
679 template <
typename Derived>
683 auto rev_mat = test_mat.colwise().reverse().eval().rowwise().reverse().eval();
684 return almost_zero(test_mat - rev_mat.transpose(), test_tol);
694 template <
typename Derived>
701 template <
typename Derived>
703 const Eigen::MatrixBase<Derived> &value,
double tol =
CASM::TOL) {
704 std::vector<CASM::Index> subspace_dims;
708 subspace_dims.push_back(i - last_i);
712 subspace_dims.push_back(value.size() - last_i);
713 return subspace_dims;
716 template <
typename Derived,
typename Index =
typename Derived::Index>
718 for (
Index j = 0; j < m.cols(); ++j) {
719 for (
Index i = 0; i < m.rows(); ++i) {
720 std::stringstream sstr;
722 sstr << m.coeff(i, j);
723 width = std::max<Index>(width,
Index(sstr.str().length()));
std::set< std::string > & s
std::vector< Eigen::Matrix3i > _unimodular_matrices(bool positive, bool negative, int range=1)
bool is_diagonal(const Eigen::MatrixBase< Derived > &M, double tol=TOL)
Check if Eigen::Matrix is diagonal.
void get_Hermitian(Eigen::MatrixXcd &original_mat, Eigen::MatrixXcd &hermitian_mat, Eigen::MatrixXcd &antihermitian_mat)
Eigen::MatrixXd pretty(const Eigen::MatrixXd &M, double tol)
Round entries that are within tol of being integer to that integer value.
const std::vector< Eigen::Matrix3i > & positive_unimodular_matrices()
Eigen::Matrix3d polar_decomposition(Eigen::Matrix3d const &mat)
bool is_unimodular(const Eigen::MatrixBase< Derived > &M, double tol)
Check if Eigen::Matrix is unimodular.
void smith_normal_form(const Eigen::MatrixBase< DerivedIn > &M, Eigen::MatrixBase< DerivedOut > &U, Eigen::MatrixBase< DerivedOut > &S, Eigen::MatrixBase< DerivedOut > &V)
Return the smith normal form, M == U*S*V.
Derived::Scalar triple_product(const Derived &vec0, const Derived &vec1, const Derived &vec2)
Derived::Scalar matrix_minor(const Eigen::MatrixBase< Derived > &M, int row, int col)
Return the minor of integer Matrix M element row, col.
Eigen::CwiseUnaryOp< decltype(Local::lround_l< typename Derived::Scalar >), const Derived > lround(const Eigen::MatrixBase< Derived > &val)
Round Eigen::MatrixXd to Eigen::MatrixXl.
double angle(const Eigen::Ref< const Eigen::Vector3d > &a, const Eigen::Ref< const Eigen::Vector3d > &b)
Get angle, in radians, between two vectors on range [0,pi].
const std::vector< Eigen::Matrix3i > & negative_unimodular_matrices()
std::pair< Eigen::MatrixXi, Eigen::MatrixXi > hermite_normal_form(const Eigen::MatrixXi &M)
Return the hermite normal form, M == H*V.
Eigen::CwiseUnaryOp< decltype(Local::round_l< typename Derived::Scalar >), const Derived > round(const Eigen::MatrixBase< Derived > &val)
Round Eigen::MatrixXd.
Eigen::Matrix< typename Derived::Scalar, Derived::RowsAtCompileTime, Derived::ColsAtCompileTime > inverse(const Eigen::MatrixBase< Derived > &M)
Return the integer inverse matrix of an invertible integer matrix.
double signed_angle(const Eigen::Ref< const Eigen::Vector3d > &a, const Eigen::Ref< const Eigen::Vector3d > &b, const Eigen::Ref< const Eigen::Vector3d > &pos_ref)
signed angle, in radians, between -pi and pi that describe separation in direction of two vectors
Eigen::CwiseUnaryOp< decltype(Local::iround_l< typename Derived::Scalar >), const Derived > iround(const Eigen::MatrixBase< Derived > &val)
Round Eigen::MatrixXd to Eigen::MatrixXi.
bool is_Hermitian(Eigen::MatrixXcd &mat)
const std::vector< Eigen::Matrix3i > & unimodular_matrices()
Eigen::Matrix< typename Derived::Scalar, Derived::RowsAtCompileTime, Derived::ColsAtCompileTime > cofactor(const Eigen::MatrixBase< Derived > &M)
Return cofactor matrix.
Eigen::Matrix< typename Derived::Scalar, Derived::RowsAtCompileTime, Derived::ColsAtCompileTime > adjugate(const Eigen::MatrixBase< Derived > &M)
Return adjugate matrix.
bool is_integer(const Eigen::MatrixBase< Derived > &M, double tol)
Check if Eigen::Matrix is integer.
IdentitySymRepBuilder Identity()
bool almost_equal(ClusterInvariants const &A, ClusterInvariants const &B, double tol)
Check if ClusterInvariants are equal.
IntType extended_gcf(IntType i1, IntType i2, IntType &p1, IntType &p2)
Calculate greatest common factor of two integers, and bezout coefficients.
Eigen::VectorXd eigen_vector_from_string(const std::string &tstr, const int &size)
double hungarian_method(const Eigen::MatrixXd &cost_matrix, std::vector< Index > &optimal_assignments, const double _tol)
bool colmajor_lex_compare(const Eigen::MatrixBase< Derived > &A, const Eigen::MatrixBase< Derived > &B, double tol)
Floating point lexicographical comparison with tol.
bool almost_zero(const T &val, double tol=TOL)
If T is not integral, use std::abs(val) < tol;.
bool float_lexicographical_compare(const Eigen::Ref< const Eigen::MatrixXd > &A, const Eigen::Ref< const Eigen::MatrixXd > &B, double tol)
Floating point lexicographical comparison with tol.
ReturnArray< Index > partition_distinct_values(const Eigen::MatrixBase< Derived > &value, double tol=TOL)
bool valid_index(Index i)
T * end_ptr(std::vector< T > &container)
Return pointer one past end of vector. Equivalent to convainer.data()+container.size()
INDEX_TYPE Index
For long integer indexing:
Derived::PlainObject reduced_column_echelon(Eigen::MatrixBase< Derived > const &M, double _tol)
bool is_persymmetric(const Eigen::MatrixBase< Derived > &test_mat, double test_tol=CASM::TOL)
bool is_symmetric(const Eigen::MatrixBase< Derived > &test_mat, double test_tol=CASM::TOL)
double length(const Eigen::MatrixBase< Derived > &value)
Eigen::Matrix< int, Derived::RowsAtCompileTime, Derived::ColsAtCompileTime > scale_to_int(const Eigen::MatrixBase< Derived > &val, double _tol=CASM::TOL)
Index print_matrix_width(std::ostream &s, const Derived &m, Index width)
bool is_bisymmetric(const Eigen::MatrixBase< Derived > &test_mat, double test_tol=CASM::TOL)
static bool _compare(T const &a, T const &b, double tol)