CASM
AClustersApproachtoStatisticalMechanics
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules
LinearAlgebra.hh
Go to the documentation of this file.
1 //
2 // LinearAlgebra.hh
3 // CASM
4 //
5 //
6 
7 #ifndef LINEARALGEBRA_HH
8 #define LINEARALGEBRA_HH
9 
10 #include <iostream>
11 #include <iomanip>
12 #include <cmath>
13 #include <complex>
14 #include <cassert>
15 
16 #include <boost/math/special_functions/round.hpp>
17 
18 #include "casm/external/Eigen/Dense"
19 #include "casm/external/Eigen/Eigenvalues"
20 
21 #include "casm/container/Array.hh"
22 #include "casm/misc/CASM_math.hh"
23 namespace CASM {
24 
33  void get_Hermitian(Eigen::MatrixXcd &original_mat, Eigen::MatrixXcd &hermitian_mat, Eigen::MatrixXcd &antihermitian_mat); //Ivy
34  bool is_Hermitian(Eigen::MatrixXcd &mat); //Ivy
35  void poly_fit(Eigen::VectorXcd &xvec, Eigen::VectorXcd &yvec, Eigen::VectorXcd &coeffs, int degree);
36 
38  std::pair<Eigen::MatrixXi, Eigen::MatrixXi> hermite_normal_form(const Eigen::MatrixXi &M);
39 
41  double angle(const Eigen::Ref<const Eigen::Vector3d> &a, const Eigen::Ref<const Eigen::Vector3d> &b);
42 
44  double signed_angle(const Eigen::Ref<const Eigen::Vector3d> &a,
45  const Eigen::Ref<const Eigen::Vector3d> &b,
46  const Eigen::Ref<const Eigen::Vector3d> &pos_ref);
47 
49  Eigen::MatrixXd pretty(const Eigen::MatrixXd &M, double tol);
50 
51 
52  template<typename Derived>
53  typename Derived::Scalar triple_prod(const Derived &vec0,
54  const Derived &vec1,
55  const Derived &vec2) {
56  return (vec0.cross(vec1)).dot(vec2);
57  }
58 
60  template<typename Derived>
61  bool is_integer(const Eigen::MatrixBase<Derived> &M, double tol) {
62  for(Index i = 0; i < M.rows(); i++) {
63  for(Index j = 0; j < M.cols(); j++) {
64  if(!almost_zero(boost::math::lround(M(i, j)) - M(i, j), tol))
65  return false;
66  }
67  }
68  return true;
69  }
70 
72  template<typename Derived>
73  bool is_unimodular(const Eigen::MatrixBase<Derived> &M, double tol) {
74  return is_integer(M, tol) && almost_equal(std::abs(M.determinant()), static_cast<typename Derived::Scalar>(1), tol);
75  }
76 
78  template<typename Derived>
79  bool is_diagonal(const Eigen::MatrixBase<Derived> &M, double tol = TOL) {
80  return M.isDiagonal(tol);
81  }
82 
91  template<typename Derived>
92  Eigen::CwiseUnaryOp< decltype(std::ptr_fun(boost::math::iround<typename Derived::Scalar>)) , const Derived >
93  iround(const Eigen::MatrixBase<Derived> &val) {
94  return val.unaryExpr(std::ptr_fun(boost::math::iround<typename Derived::Scalar>));
95  }
96 
105  template<typename Derived>
106  Eigen::CwiseUnaryOp< decltype(std::ptr_fun(boost::math::lround<typename Derived::Scalar>)) , const Derived >
107  lround(const Eigen::MatrixBase<Derived> &val) {
108  return val.unaryExpr(std::ptr_fun(boost::math::lround<typename Derived::Scalar>));
109  }
110 
121  template<typename Derived>
122  typename Derived::Scalar matrix_minor(const Eigen::MatrixBase<Derived> &M, int row, int col) {
123 
124  // create the submatrix of M which doesn't include any elements from M in 'row' or 'col'
125  Eigen::Matrix < typename Derived::Scalar,
126  Derived::RowsAtCompileTime - 1,
127  Derived::ColsAtCompileTime - 1 >
128  subM(M.rows() - 1, M.cols() - 1);
129  int _i, _j;
130  for(int i = 0; i < M.rows(); i++) {
131  for(int j = 0; j < M.cols(); j++) {
132  if(i == row || j == col)
133  continue;
134  (i < row) ? _i = i : _i = i - 1;
135  (j < col) ? _j = j : _j = j - 1;
136  subM(_i, _j) = M(i, j);
137  }
138  }
139 
140  // return determinant of subM
141  return subM.determinant();
142  }
143 
152  template<typename Derived>
153  Eigen::Matrix<typename Derived::Scalar,
154  Derived::RowsAtCompileTime,
155  Derived::ColsAtCompileTime>
156  cofactor(const Eigen::MatrixBase<Derived> &M) {
157  Eigen::Matrix<typename Derived::Scalar,
158  Derived::RowsAtCompileTime,
159  Derived::ColsAtCompileTime>
160  Mcof(M.rows(), M.cols());
161  for(int i = 0; i < Mcof.rows(); i++) {
162  for(int j = 0; j < Mcof.cols(); j++) {
163  (((i + j) % 2) == 0) ? Mcof(i, j) = matrix_minor(M, i, j) : Mcof(i, j) = -matrix_minor(M, i, j);
164  }
165  }
166  return Mcof;
167  }
168 
179  // Note: If Eigen ever implements integer factorizations, we should probably change how this works
180  template<typename Derived>
181  Eigen::Matrix<typename Derived::Scalar,
182  Derived::RowsAtCompileTime,
183  Derived::ColsAtCompileTime>
184  adjugate(const Eigen::MatrixBase<Derived> &M) {
185  return cofactor(M).transpose();
186  }
187 
188  namespace normal_form_impl {
197  template<typename Scalar>
198  Eigen::Matrix<Scalar, 3, 3> _elementary_hermite_op(Scalar a, Scalar b, Scalar i, Scalar j) {
199  typedef Eigen::Matrix<Scalar, 3, 3> matrix_type;
200  matrix_type tmat = matrix_type::Identity();
201  Scalar tgcf = extended_gcf(a, b, tmat(i, i), tmat(i, j));
202  if(tgcf == 0) {
203  return matrix_type::Identity();
204  }
205  tmat(j, i) = -b / tgcf;
206  tmat(j, j) = a / tgcf;
207  return tmat;
208  }
209 
210  }
211 
218  template<typename Derived>
219  Eigen::Matrix<typename Derived::Scalar,
220  Derived::RowsAtCompileTime,
221  Derived::ColsAtCompileTime>
222  inverse(const Eigen::MatrixBase<Derived> &M) {
223  return adjugate(M) / M.determinant();
224  }
225 
236  template<typename DerivedIn, typename DerivedOut>
237  void smith_normal_form(const Eigen::MatrixBase<DerivedIn> &M,
238  Eigen::MatrixBase<DerivedOut> &U,
239  Eigen::MatrixBase<DerivedOut> &S,
240  Eigen::MatrixBase<DerivedOut> &V) {
241 
242  static_assert(std::is_same<typename DerivedIn::Scalar, typename DerivedOut::Scalar>::value,
243  "ALL ARGUMENTS TO CASM::smith_normal_form() MUST BE BASED ON SAME SCALAR TYPE!");
244  using namespace normal_form_impl;
245  typedef typename DerivedOut::Scalar Scalar;
246 
247  U = V = DerivedOut::Identity();
248  S = M;
249  //std::cout << "S is:\n" << S << "\n\n";
250 
251  DerivedOut tmat = U;
252 
253  int i, j;
254 
255  // Bidiagonalize S with elementary Hermite transforms.
256  for(j = 0; j < 3; j++) {
257  //take j as column index and zero elements of j below the diagonal
258  for(i = j + 1; i < 3; i++) {
259  if(S(i, j) == 0) continue;
260  tmat = _elementary_hermite_op<Scalar>(S(j, j), S(i, j), j, i);
261  S = tmat * S;
262  U = U * inverse(tmat);
263  }
264 
265  //take j as row index and zero elements of j after superdiagonal
266  for(i = j + 2; i < 3; i++) {
267  if(S(j, i) == 0) continue;
268  tmat = _elementary_hermite_op<Scalar>(S(j, j + 1), S(j, i), j + 1, i);
269  S = S * tmat.transpose();
270  V = inverse(tmat.transpose()) * V;
271  }
272  }
273 
274  // std::cout << "About to hunt zeros, SNF decomposition is currently:\n" << U << "\n\n" << S << "\n\n" << V << "\n\n";
275  while(!S.isDiagonal()) {
276  //find off-diagonal element
277  int b = 0;
278  for(b = 0; b < 2; b++) {
279  if(S(b, b + 1))
280  break;
281  }
282 
283  // To guarantee reduction in S(b,b), first make S(b,b) positive
284  // and make S(b,b+1) nonnegative and less than S(b,b).
285  if(S(b, b) < 0) {
286  for(i = 0; i < 3; i++) {
287  S(b, i) = -S(b, i);
288  U(i, b) = -U(i, b);
289  }
290  }
291 
292  //std::cout << "S before q:\n" << S << '\n';
293 
294  if(S(b, b)) {
295  Scalar q = S(b, b + 1) / S(b, b);
296  if(S(b, b + 1) % S(b, b) < 0)
297  q -= 1;
298  tmat = DerivedOut::Identity();
299  tmat(b + 1, b) = -q;
300  S = S * tmat.transpose();
301  V = inverse(tmat.transpose()) * V;
302  //std::cout << "tmat for q:\n" << tmat << '\n';
303  //std::cout << "S after q:\n" << S << '\n';
304  }
305  else {
306  tmat = DerivedOut::Identity();
307  tmat(b, b) = 0;
308  tmat(b, b + 1) = 1;
309  tmat(b + 1, b + 1) = 0;
310  tmat(b + 1, b) = 1;
311  S = S * tmat.transpose();
312  V = inverse(tmat.transpose()) * V;
313 
314  }
315 
316  if(!S(b, b + 1))
317  continue;
318 
319  tmat = _elementary_hermite_op<Scalar>(S(b, b), S(b, b + 1), b, b + 1);
320  S = S * tmat.transpose();
321  V = inverse(tmat.transpose()) * V;
322  for(j = 0; j < 2; j++) {
323  tmat = _elementary_hermite_op<Scalar>(S(j, j), S(j + 1, j), j, j + 1);
324  S = tmat * S;
325  U = U * inverse(tmat);
326 
327  if(j + 2 >= 3)
328  continue;
329 
330  tmat = _elementary_hermite_op<Scalar>(S(j, j + 1), S(j, j + 2), j + 1, j + 2);
331  S = S * tmat.transpose();
332  V = inverse(tmat.transpose()) * V;
333  }
334  }
335 
336  //Now it's diagonal -- make it non-negative
337  for(j = 0; j < 3; j++) {
338  if(S(j, j) < 0) {
339  for(i = 0; i < 3; i++) {
340  S(j, i) = -S(j, i);
341  U(i, j) = -U(i, j);
342  }
343  }
344  }
345 
346  //sort diagonal elements
347  for(i = 0; i < 3; i++) {
348  for(j = i + 1; j < 3; j++) {
349  if(S(i, i) > S(j, j)) {
350  S.row(i).swap(S.row(j));
351  S.col(i).swap(S.col(j));
352  U.col(i).swap(U.col(j));
353  V.row(i).swap(V.row(j));
354  }
355  }
356  }
357  //enforce divisibility condition:
358  for(i = 0; i < 3; i++) {
359  if(S(i, i) == 0) continue;
360  for(j = i + 1; j < 3; j++) {
361  if(S(j, j) % S(i, i) == 0) continue;
362  //Replace S(i,i), S(j,j) by their gcd and lcm respectively.
363  tmat = DerivedOut::Identity();
364  DerivedOut tmat2(tmat);
365  Scalar a(S(i, i)), b(S(j, j)), c, d, tgcf;
366  tgcf = extended_gcf(a, b, c, d);
367  tmat(i, i) = 1;
368  tmat(i, j) = d;
369  tmat(j, i) = -b / tgcf;
370  tmat(j, j) = (a * c) / tgcf;
371 
372  tmat2(i, i) = c;
373  tmat2(i, j) = 1;
374  tmat2(j, i) = -(b * d) / tgcf;
375  tmat2(j, j) = a / tgcf;
376  S = tmat * S * tmat2.transpose();
377  U = U * inverse(tmat);
378  V = inverse(tmat2.transpose()) * V;
379  }
380  }
381  return;
382  }
383 
384  std::vector<Eigen::Matrix3i> _unimodular_matrices(bool positive, bool negative);
385  const std::vector<Eigen::Matrix3i> &positive_unimodular_matrices();
386  const std::vector<Eigen::Matrix3i> &negative_unimodular_matrices();
387  const std::vector<Eigen::Matrix3i> &unimodular_matrices();
388 
390 }
391 #endif
Eigen::MatrixXd MatrixXd
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].
bool almost_zero(const T &val, double tol=TOL)
If T is not integral, use std::abs(val) < tol;.
Definition: CASM_math.hh:41
bool is_Hermitian(Eigen::MatrixXcd &mat)
void get_Hermitian(Eigen::MatrixXcd &original_mat, Eigen::MatrixXcd &hermitian_mat, Eigen::MatrixXcd &antihermitian_mat)
IntType extended_gcf(IntType i1, IntType i2, IntType &p1, IntType &p2)
Calculate greatest common factor of two integers, and bezout coefficients.
Definition: CASM_math.hh:214
const std::vector< Eigen::Matrix3i > & negative_unimodular_matrices()
Main CASM namespace.
Definition: complete.cpp:8
void poly_fit(Eigen::VectorXcd &xvec, Eigen::VectorXcd &yvec, Eigen::VectorXcd &coeffs, int degree)
Definition: CASM_math.cc:308
const double TOL
Derived::Scalar triple_prod(const Derived &vec0, const Derived &vec1, const Derived &vec2)
const std::vector< Eigen::Matrix3i > & unimodular_matrices()
double tol
std::vector< Eigen::Matrix3i > _unimodular_matrices(bool positive, bool negative)
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 ...
const std::vector< Eigen::Matrix3i > & positive_unimodular_matrices()
Eigen::Matrix< typename Derived::Scalar, Derived::RowsAtCompileTime, Derived::ColsAtCompileTime > adjugate(const Eigen::MatrixBase< Derived > &M)
Return adjugate matrix.
EigenIndex Index
For long integer indexing:
Eigen::Matrix< Scalar, 3, 3 > _elementary_hermite_op(Scalar a, Scalar b, Scalar i, Scalar j)
Return an elementary hermite transformation.
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.
Eigen::CwiseUnaryOp< decltype(std::ptr_fun(boost::math::lround< typename Derived::Scalar >)), const Derived > lround(const Eigen::MatrixBase< Derived > &val)
Round Eigen::MatrixXd to Eigen::MatrixXl.
T dot(const Tensor< T > &LHS, const Tensor< T > &RHS)
Definition: Tensor.hh:961
std::pair< Eigen::MatrixXi, Eigen::MatrixXi > hermite_normal_form(const Eigen::MatrixXi &M)
Return the hermite normal form, M == H*V.
bool is_integer(const Eigen::MatrixBase< Derived > &M, double tol)
Check if Eigen::Matrix is integer.
Derived::Scalar matrix_minor(const Eigen::MatrixBase< Derived > &M, int row, int col)
Return the minor of integer Matrix M element row, col.
bool is_diagonal(const Eigen::MatrixBase< Derived > &M, double tol=TOL)
Check if Eigen::Matrix is diagonal.
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.
Eigen::CwiseUnaryOp< decltype(std::ptr_fun(boost::math::iround< typename Derived::Scalar >)), const Derived > iround(const Eigen::MatrixBase< Derived > &val)
Round Eigen::MatrixXd to Eigen::MatrixXi.
Eigen::MatrixXd pretty(const Eigen::MatrixXd &M, double tol)
Round entries that are within tol of being integer to that integer value.
Eigen::Matrix< typename Derived::Scalar, Derived::RowsAtCompileTime, Derived::ColsAtCompileTime > cofactor(const Eigen::MatrixBase< Derived > &M)
Return cofactor matrix.
bool almost_equal(const GenericCluster< CoordType > &LHS, const GenericCluster< CoordType > &RHS, double tol)
bool is_unimodular(const Eigen::MatrixBase< Derived > &M, double tol)
Check if Eigen::Matrix is unimodular.