CASM  1.1.0
A Clusters Approach to Statistical Mechanics
IrrepDecompositionImpl.cc
Go to the documentation of this file.
2 
3 #include <iostream>
4 
6 #include "casm/misc/CASM_math.hh"
9 
10 namespace CASM {
11 
12 namespace SymRepTools_v2 {
13 
14 namespace IrrepDecompositionImpl {
15 
18 Eigen::MatrixXcd prettyc(const Eigen::MatrixXcd &M) {
19  double tol = 1e-10;
20  Eigen::MatrixXcd Mp(M);
21  for (int i = 0; i < M.rows(); i++) {
22  for (int j = 0; j < M.cols(); j++) {
23  if (std::abs(std::round(M(i, j).real()) - M(i, j).real()) < tol) {
24  Mp(i, j).real(std::round(M(i, j).real()));
25  }
26  if (std::abs(std::round(M(i, j).imag()) - M(i, j).imag()) < tol) {
27  Mp(i, j).imag(std::round(M(i, j).imag()));
28  }
29  }
30  }
31  return Mp;
32 }
33 
37  double tol = 1e-10;
38  Eigen::MatrixXd Mp(M);
39  for (int i = 0; i < M.rows(); i++) {
40  for (int j = 0; j < M.cols(); j++) {
41  if (std::abs(std::round(M(i, j)) - M(i, j)) < tol) {
42  Mp(i, j) = std::round(M(i, j));
43  }
44  }
45  }
46  return Mp;
47 }
48 
49 // note: there are a number of floating point comparisons, currently all set
50 // to use CASM::TOL as the tolerance. If necessary, they could be tuned here or
51 // via function parameters. To find them search for TOL or "almost".
52 
54  return Eigen::MatrixXd::Identity(rows, cols);
55 }
56 
58  return Eigen::MatrixXd::Zero(rows, cols);
59 }
60 
61 Eigen::MatrixXcd complex_I(Index rows, Index cols) {
62  return Eigen::MatrixXcd::Identity(rows, cols);
63 }
64 
65 Eigen::MatrixXcd complex_Zero(Index rows, Index cols) {
66  return Eigen::MatrixXcd::Zero(rows, cols);
67 }
68 
70 
71 void CommuterParamsCounter::reset(Eigen::MatrixXcd const &kernel) {
72  m_valid = true;
73  m_max_cols = kernel.cols();
74  m_phase_index = 0;
75 
76  kernel_column_i = 0;
78  phase = std::complex<double>(1., 0.);
79 }
80 
81 bool CommuterParamsCounter::valid() const { return m_valid; }
82 
84  if (!m_valid) {
85  return false;
86  }
87 
88  // inner loop is column j
90 
91  // next inner loop is column i
95  }
96 
97  // outer loop is phase: 1, i
98  if (kernel_column_i == m_max_cols) {
99  ++m_phase_index;
100  kernel_column_i = 0;
102 
103  // once m_phase_index gets to 2, we've finished all possibilities
104  if (m_phase_index == 2) {
105  m_valid = false;
106  return false;
107  } else if (m_phase_index == 1) {
108  phase = std::complex<double>(0., 1.);
109  }
110  }
111 
112  // skip i==j when phase==i
114  return increment();
115  }
116 
117  return true;
118 }
119 
120 Eigen::MatrixXcd make_commuter(CommuterParamsCounter const &params,
121  MatrixRep const &rep,
122  GroupIndices const &head_group,
123  Eigen::MatrixXcd const &kernel) {
124  Index dim = rep[0].rows();
125 
126  // construct outer product space of kernel columns i and j
127  // commuters are constructed to be self-adjoint,
128  // which assures eigenvalues are real
129  auto const &col_i = kernel.col(params.kernel_column_i);
130  auto const &col_j = kernel.col(params.kernel_column_j);
131  auto const &phase = params.phase;
132  Eigen::MatrixXcd M_init = phase * col_i * col_j.adjoint() +
133  std::conj(phase) * col_j * col_i.adjoint();
134  Eigen::MatrixXcd M = complex_Zero(dim, dim);
135 
136  // Reynolds operation to symmetrize:
137  for (Index element_index : head_group) {
138  M += rep[element_index] * M_init * rep[element_index].transpose();
139  }
140  return M;
141 }
142 
143 Eigen::MatrixXcd make_kernel(Eigen::MatrixXcd const &subspace) {
144  Eigen::HouseholderQR<Eigen::MatrixXcd> qr;
145  qr.compute(subspace);
146  return Eigen::MatrixXcd(qr.householderQ())
147  .rightCols(subspace.rows() - subspace.cols());
148 }
150  Eigen::HouseholderQR<Eigen::MatrixXd> qr;
151  qr.compute(subspace);
152  return Eigen::MatrixXd(qr.householderQ())
153  .rightCols(subspace.rows() - subspace.cols());
154 }
155 
157  Eigen::VectorXd const &eigenvalues) {
158  Index end = begin + 1;
159  while (end < eigenvalues.size() &&
160  almost_equal(eigenvalues(begin), eigenvalues(end), TOL)) {
161  end++;
162  }
163  return end;
164 }
165 
180 Eigen::MatrixXcd make_irrep_subspace(Eigen::MatrixXcd const &KV_matrix,
181  Index begin, Index end,
182  bool allow_complex) {
183  Index dim = KV_matrix.rows();
184 
185  // get columns of (K * V) corresponding to equal eigenvalues
186  Eigen::MatrixXcd X = (KV_matrix).block(0, begin, dim, end - begin);
187  Eigen::MatrixXcd subspace_init;
188  if (allow_complex) {
189  subspace_init = X;
190  } else {
191  subspace_init = Eigen::MatrixXcd::Zero(dim, 2 * X.cols());
192  subspace_init.leftCols(X.cols()) =
193  sqrt(2.0) * X.real().cast<std::complex<double>>();
194  subspace_init.rightCols(X.cols()) =
195  sqrt(2.0) * X.imag().cast<std::complex<double>>();
196  }
197 
198  // QR decomposition
199 
200  // "it seems stupid", but Eigen::HouseholderQR is not rank revealing, and
201  // Eigen::ColPivHouseholderQR permutes columns of Q
202  Eigen::HouseholderQR<Eigen::MatrixXcd> qr;
203  Eigen::ColPivHouseholderQR<Eigen::MatrixXcd> colqr;
204  colqr.setThreshold(TOL);
205 
206  qr.compute(subspace_init);
207  colqr.compute(subspace_init);
208  Eigen::MatrixXcd Q = qr.householderQ();
209  Eigen::MatrixXcd irrep_subspace = Q.leftCols(colqr.rank());
210  return irrep_subspace;
211 }
212 
214 Eigen::VectorXcd make_characters(std::vector<Eigen::MatrixXcd> const &rep) {
215  Eigen::VectorXcd characters(rep.size());
216 
217  Index element_index = 0;
218  for (Eigen::MatrixXcd const &matrix : rep) {
219  characters(element_index) = matrix.trace();
220  ++element_index;
221  }
222  return characters;
223 }
224 
226 Eigen::VectorXd make_characters(std::vector<Eigen::MatrixXd> const &rep) {
227  Eigen::VectorXd characters(rep.size());
228 
229  Index element_index = 0;
230  for (Eigen::MatrixXd const &matrix : rep) {
231  characters(element_index) = matrix.trace();
232  ++element_index;
233  }
234  return characters;
235 }
236 
240 bool make_is_block_diagonal(std::vector<Eigen::MatrixXcd> const &rep,
241  Index begin, Index end, double tol) {
242  Index element_index = 0;
243  for (Eigen::MatrixXcd const &matrix : rep) {
244  // left
245  if (begin != 0) {
246  if (!almost_zero(matrix.block(begin, 0, end - begin, begin), tol)) {
247  return false;
248  }
249  }
250  // right
251  if (end != matrix.cols()) {
252  if (!almost_zero(
253  matrix.block(begin, end, end - begin, matrix.cols() - end),
254  tol)) {
255  return false;
256  }
257  }
258  // top
259  if (begin != 0) {
260  if (!almost_zero(matrix.block(0, begin, begin, end - begin), tol)) {
261  return false;
262  }
263  }
264  // bottom
265  if (end != matrix.rows()) {
266  if (!almost_zero(
267  matrix.block(end, begin, matrix.rows() - end, end - begin),
268  tol)) {
269  return false;
270  }
271  }
272 
273  ++element_index;
274  }
275  return true;
276 }
277 
279 Eigen::VectorXcd make_irrep_characters(std::vector<Eigen::MatrixXcd> const &rep,
280  Index begin, Index end) {
281  Eigen::VectorXcd characters(rep.size());
282 
283  Index element_index = 0;
284  for (Eigen::MatrixXcd const &matrix : rep) {
285  characters(element_index) =
286  matrix.block(begin, begin, end - begin, end - begin).trace();
287  ++element_index;
288  }
289  return characters;
290 }
291 
292 double make_squared_norm(Eigen::VectorXcd const &characters) {
293  double squared_norm = 0.0;
294  for (Index i = 0; i < characters.size(); ++i) {
295  squared_norm += std::norm(characters(i));
296  }
297  return squared_norm;
298 }
299 
300 double make_squared_norm(Eigen::VectorXd const &characters) {
301  double squared_norm = 0.0;
302  for (Index i = 0; i < characters.size(); ++i) {
303  squared_norm += characters(i) * characters(i);
304  }
305  return squared_norm;
306 }
307 
308 std::complex<double> frobenius_product(Eigen::MatrixXcd const &matrix) {
309  return (matrix.array().conjugate() * matrix.array()).sum();
310 }
311 
312 Eigen::MatrixXcd normalize_commuter(Eigen::MatrixXcd const &commuter) {
313  return commuter / sqrt(frobenius_product(commuter).real());
314 }
315 
317 bool is_extended_by(Eigen::MatrixXcd const &space_A,
318  Eigen::MatrixXcd const &space_B) {
319  return almost_zero((space_B.adjoint() * space_A).norm(), TOL);
320 }
321 
323 Eigen::MatrixXcd extend(Eigen::MatrixXcd const &space_A,
324  Eigen::MatrixXcd const &space_B) {
325  Eigen::MatrixXcd result(space_A.rows(), space_A.cols() + space_B.cols());
326  result.leftCols(space_A.cols()) = space_A;
327  result.rightCols(space_B.cols()) = space_B;
328  return result;
329 }
330 
333  Eigen::MatrixXd const &space_B) {
334  Eigen::MatrixXd result(space_A.rows(), space_A.cols() + space_B.cols());
335  result.leftCols(space_A.cols()) = space_A;
336  result.rightCols(space_B.cols()) = space_B;
337  return result;
338 }
339 
340 Index get_total_dim(std::set<PossibleIrrep> const &irreps) {
341  Index total_dim = 0;
342  for (PossibleIrrep const &irrep : irreps) {
343  total_dim += irrep.irrep_dim;
344  }
345  return total_dim;
346 }
347 
356  Eigen::VectorXd const &eigenvalues, Eigen::MatrixXcd const &KV_matrix,
357  std::vector<Eigen::MatrixXcd> const &transformed_rep,
358  Index _head_group_size, double _tol, bool allow_complex, Index _begin,
359  Index _end)
360  : head_group_size(_head_group_size),
361  tol(_tol),
362  begin(_begin),
363  end(_end),
364  irrep_dim(end - begin) {
365  is_block_diagonal = make_is_block_diagonal(transformed_rep, begin, end, tol);
366  characters = make_irrep_characters(transformed_rep, begin, end);
369  double(head_group_size), tol);
370  subspace = make_irrep_subspace(KV_matrix, begin, end, allow_complex);
371 }
372 
377  std::complex<double> complex_one{1., 0.};
378  std::complex<double> first = characters(0);
379  std::complex<double> complex_size{double(characters.size()), 0.};
380  std::complex<double> sum = characters.sum();
381 
382  return almost_equal(first, complex_one, TOL) &&
383  almost_equal(sum, complex_size, TOL);
384 }
385 
391  std::complex<double> first = characters(0);
392  std::complex<double> last = characters(characters.size() - 1);
393  return almost_equal(first, last, TOL);
394 }
395 
396 bool PossibleIrrep::operator<(PossibleIrrep const &other) const {
397  // Identity comes first
398  bool this_is_identity = this->is_identity();
399  bool other_is_identity = other.is_identity();
400  if (this_is_identity != other_is_identity) {
401  return this_is_identity;
402  }
403 
404  // Low-dimensional irreps come before higher dimensional
405  if (!almost_equal(this->characters(0), other.characters(0))) {
406  return this->characters(0).real() < other.characters(0).real();
407  }
408 
409  // 'gerade' irreps come before 'ungerade' irreps
410  // This check may need to be improved to know whether inversion is actually
411  // present
412  bool this_is_gerade = this->is_gerade();
413  bool other_is_gerade = other.is_gerade();
414  if (this_is_gerade != other_is_gerade) {
415  return this_is_gerade;
416  }
417 
418  // Finally, compare lexicographically (real first, then imag)
419  for (Index i = 0; i < this->characters.size(); ++i) {
420  if (!almost_equal(this->characters(i).real(), other.characters(i).real()))
421  return this->characters(i).real() > other.characters(i).real();
422  }
423  for (Index i = 0; i < this->characters.size(); ++i) {
424  if (!almost_equal(this->characters(i).imag(), other.characters(i).imag()))
425  return this->characters(i).imag() > other.characters(i).imag();
426  }
427 
428  // Now, any possible irrep that are still equal, we break the tie by
429  // comparing subspace vectors
430  if (this->subspace.cols() != other.subspace.cols()) {
431  return this->subspace.cols() < other.subspace.cols();
432  }
433  for (Index col = 0; col < this->subspace.cols(); ++col) {
434  for (Index i = 0; i < this->subspace.size(); ++i) {
435  if (!almost_equal(this->subspace(i, col).real(),
436  other.subspace(i, col).real()))
437  return this->subspace(i, col).real() > other.subspace(i, col).real();
438  }
439  for (Index i = 0; i < this->subspace.size(); ++i) {
440  if (!almost_equal(this->subspace(i, col).imag(),
441  other.subspace(i, col).imag()))
442  return this->subspace(i, col).imag() > other.subspace(i, col).imag();
443  }
444  }
445 
446  throw std::runtime_error("Error comparing PossibleIrrep, tied");
447 
448  return false;
449 }
450 
456 std::vector<PossibleIrrep> make_possible_irreps(
457  Eigen::MatrixXcd const &commuter, Eigen::MatrixXcd const &kernel,
458  MatrixRep const &rep, GroupIndices const &head_group, double is_irrep_tol,
459  bool allow_complex) {
460  // magnify the range of eigenvalues to be (I think) independent of
461  // matrix dimension by multiplying by dim^{3/2}
462  //
463  // solve for eigenvalues and eigenvectors of:
464  // dim^(3/2) * kernel.adjoint() * M * kernel
465  //
466  double dim = kernel.rows();
467  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXcd> esolve;
468  double scale = dim * sqrt(dim);
469  esolve.compute(scale * kernel.adjoint() * commuter * kernel);
470  Eigen::MatrixXd eigenvalues = esolve.eigenvalues();
471  Eigen::MatrixXcd KV_matrix = kernel * esolve.eigenvectors();
472 
473  // Columns of KV_matrix are orthonormal eigenvectors of commuter in terms of
474  // natural basis (they were calculated in terms of kernel as basis)
475 
476  // When the matrix representation is transformed to operate on coordinates
477  // with KV_matrix basis, it becomes block diagonalized
478  std::vector<Eigen::MatrixXcd> transformed_rep;
479  transformed_rep.reserve(head_group.size());
480  for (auto const &element_index : head_group) {
481  transformed_rep.push_back(KV_matrix.adjoint() * rep[element_index] *
482  KV_matrix);
483  }
484 
485  // make possible irreps:
486  // - The possible irrep corresponds to a range eigenvectors with equal
487  // eigenvalues, could be irrep or could be reducible with degenerate
488  // eigenvalues
489  // - When the possible irrep for a range of equal eigenvectors is constructed,
490  // its characters vector is constructed, and if the squared norm of the
491  // characters vectors equals the head group size, then the corresponding
492  // columns of the KV_matrix are an irrep subspace
493  std::vector<PossibleIrrep> possible_irreps;
494  Index begin = 0;
495  do {
496  Index end = find_end_of_equal_eigenvalues(begin, eigenvalues);
497  possible_irreps.emplace_back(eigenvalues, KV_matrix, transformed_rep,
498  head_group.size(), is_irrep_tol, allow_complex,
499  begin, end);
500  begin = end;
501  } while (begin != eigenvalues.size());
502 
503  return possible_irreps;
504 }
505 
507 std::vector<IrrepInfo> make_irrep_info(std::set<PossibleIrrep> const &irreps) {
508  std::vector<IrrepInfo> irrep_info;
509  for (PossibleIrrep const &irrep : irreps) {
510  irrep_info.emplace_back(irrep.subspace.adjoint(), irrep.characters);
511 
512  if (irrep.subspace.cols() == 2 * (irrep.end - irrep.begin))
513  irrep_info.back().pseudo_irrep = true;
514  else
515  irrep_info.back().pseudo_irrep = false;
516  }
517 
518  // set sequential indices to differentiate irreps
519  // with identical character vectors
520  Index irrep_index = 0;
521  for (Index i = 0; i < irrep_info.size() - 1; ++i) {
522  irrep_info[i].index = irrep_index;
523  if (almost_equal(irrep_info[i + 1].characters, irrep_info[i].characters,
524  TOL)) {
525  irrep_index++;
526  } else {
527  irrep_index = 0;
528  }
529  }
530 
531  return irrep_info;
532 }
533 
545  Eigen::MatrixXd const &subspace) {
546  IrrepInfo result(subspace_irrep);
547 
548  result.trans_mat = subspace_irrep.trans_mat *
549  subspace.adjoint().template cast<std::complex<double>>();
550 
551  result.directions.clear();
552  for (const auto &direction_orbit : subspace_irrep.directions) {
553  std::vector<Eigen::VectorXd> new_orbit;
554  new_orbit.reserve(direction_orbit.size());
555  for (const auto &directions : direction_orbit) {
556  new_orbit.push_back(subspace * directions);
557  }
558  result.directions.push_back(std::move(new_orbit));
559  }
560  return result;
561 }
562 
567 bool is_irrep(MatrixRep const &rep, GroupIndices const &head_group) {
568  double characters_squared_norm = 0;
569  for (Index element_index : head_group) {
570  double character = rep[element_index].trace();
571  characters_squared_norm += character * character;
572  }
573  return almost_equal(characters_squared_norm, double(head_group.size()), TOL);
574 }
575 
641 
663 std::vector<IrrepInfo> irrep_decomposition(MatrixRep const &rep,
664  GroupIndices const &head_group,
665  bool allow_complex) {
666  if (!rep.size()) {
667  return std::vector<IrrepInfo>();
668  }
669  int dim = rep[0].rows();
670 
671  // This method iteratively finds irreducible spaces, which are used to extend
672  // the "adapted_subspace" (combined space of found irreducible spaces). The
673  // "adapted_subspace" is not aligned along high symmetry directions by this
674  // function. When the "adapted_subspace" is of dimenions equal to `dim`, all
675  // irreps have been found.
676 
677  // start with all kernel, end with all adapted_subspace
678  Eigen::MatrixXcd kernel = complex_I(dim, dim);
679  Eigen::MatrixXcd adapted_subspace{dim, 0};
680 
681  // In this set, as they are discovered we will save PossibleIrrep that:
682  // - i) actually are irreducible,
683  // - and ii) have distinct subspaces (detected when their subspace extends
684  // the adapted_subspace space) BP: not necessary?
685  std::set<PossibleIrrep> irreps;
686 
687  // count over possible commuter matrices for this kernel:
688  // - kernel column pairs (i,j), j>=i & phase = [1, i]; skips i==j if phase==i
689  CommuterParamsCounter commuter_params;
690  commuter_params.reset(kernel);
691 
692  double is_irrep_tol = TOL;
693 
694  do { // while adapated_subspace.cols() != dim
695 
696  if (!commuter_params.valid()) {
697  // The commuter construction method does not currently guarantee that
698  // all irreps will be revealed. The caller may have a way to handle this
699  // and so this does not throw an exception.
700  break;
701  }
702 
703  // make next commuter, M, and check if not zero
704  Eigen::MatrixXcd commuter =
705  make_commuter(commuter_params, rep, head_group, kernel);
706 
707  if (almost_equal(frobenius_product(commuter).real(), 0., TOL)) {
708  commuter_params.increment();
709  continue;
710  }
711 
712  // make possible irreps:
713  //
714  // Given kernel, K, and commuter matrix, M, perform eigenvalue decomposition
715  // K.adjoint() * M * K = V * D * V.inverse()
716  // and construct matrix representation that acts on vectors in the K*V
717  // basis, which will be block diagonalized and sorted by eigenvalue. Each
718  // block corresponds to a possible irrep, which can be checked by its
719  // characters. The columns in K*V corresponding to an irrep are the irrep
720  // subspace.
721  std::vector<PossibleIrrep> possible_irreps = make_possible_irreps(
722  commuter, kernel, rep, head_group, is_irrep_tol, allow_complex);
723 
724  // save any possible irrep that:
725  // - i) is an irrep,
726  // - and ii) extends the adapted_subspace space (BP: not necessary?)
727  bool any_new_irreps = false;
728  for (auto const &possible_irrep : possible_irreps) {
729  if (possible_irrep.is_irrep &&
730  is_extended_by(adapted_subspace, possible_irrep.subspace)) {
731  irreps.insert(possible_irrep);
732  adapted_subspace = extend(adapted_subspace, possible_irrep.subspace);
733  any_new_irreps = true;
734  }
735  }
736 
737  // if any new irreps were found, recalculate kernel, reset commuter params
738  // counter, and go again
739  if (any_new_irreps && adapted_subspace.cols() != dim) {
740  kernel = make_kernel(adapted_subspace);
741  commuter_params.reset(kernel);
742  if (kernel.cols() + adapted_subspace.cols() != adapted_subspace.rows()) {
743  throw std::runtime_error(
744  "Unknown error finding irreps: dimension mismatch");
745  }
746  } else {
747  commuter_params.increment();
748  }
749  } while (adapted_subspace.cols() != dim);
750 
751  // Make irrep info (no directions yet, orthogonalized but not aligned along
752  // high symmetry directions)
753  std::vector<IrrepInfo> irrep_info = make_irrep_info(irreps);
754 
755  return irrep_info;
756 }
757 
765 std::vector<IrrepInfo> make_fullspace_irreps(
766  std::vector<IrrepInfo> const &subspace_irreps,
767  Eigen::MatrixXd const &subspace) {
768  std::vector<IrrepInfo> fullspace_irreps;
769  fullspace_irreps.reserve(subspace_irreps.size());
770  for (auto const &irrep : subspace_irreps) {
771  fullspace_irreps.push_back(subspace_to_full_space(irrep, subspace));
772  }
773  return fullspace_irreps;
774 }
775 
778  GroupIndices const &head_group,
779  Eigen::MatrixXd const &subspace) {
780  if (!subspace.isIdentity()) {
781  Eigen::MatrixXd symspace(subspace.rows(),
782  subspace.cols() * head_group.size());
783  Index l = 0;
784  for (Index element_index : head_group) {
785  symspace.block(0, l, subspace.rows(), subspace.cols()) =
786  rep[element_index] * subspace;
787  l += subspace.cols();
788  }
789  Eigen::ColPivHouseholderQR<Eigen::MatrixXd> colqr(symspace);
790  colqr.setThreshold(TOL);
791  Eigen::MatrixXd Q = colqr.householderQ();
792  Eigen::MatrixXd result = Q.leftCols(colqr.rank());
793  return result;
794  }
795  return subspace;
796 }
797 
798 // Create `subspace_rep`, a transformed copy of `fullspace_rep` that acts
799 // on coordinates with `subspace` columns as a basis. Matrices in
800 // `subspace_rep` are shape (subspace.cols() x subspace.cols())
801 MatrixRep make_subspace_rep(MatrixRep const &fullspace_rep,
802  Eigen::MatrixXd const &subspace) {
803  Eigen::MatrixXd trans_mat = subspace.transpose();
804  Eigen::MatrixXd rightmat =
805  subspace.jacobiSvd(Eigen::ComputeThinU | Eigen::ComputeThinV)
806  .solve(Eigen::MatrixXd::Identity(trans_mat.cols(), trans_mat.cols()))
807  .transpose();
808  MatrixRep subspace_rep;
809  for (Index i = 0; i < fullspace_rep.size(); ++i) {
810  subspace_rep.push_back(trans_mat * fullspace_rep[i] * rightmat);
811  }
812  return subspace_rep;
813 }
814 
817 std::vector<IrrepInfo> symmetrize_irreps(
818  MatrixRep const &subspace_rep, GroupIndices const &head_group,
819  std::vector<IrrepInfo> const &irreps,
820  GroupIndicesOrbitVector const &cyclic_subgroups,
821  GroupIndicesOrbitVector const &all_subgroups) {
822  std::vector<IrrepInfo> symmetrized_irreps;
823  double vec_compare_tol = TOL;
824  bool use_all_subgroups = false;
825  for (const auto &irrep : irreps) {
826  Eigen::MatrixXcd irrep_subspace = irrep.trans_mat.adjoint();
827 
828  multivector<Eigen::VectorXcd>::X<2> irrep_special_directions =
829  make_irrep_special_directions(subspace_rep, head_group, irrep_subspace,
830  vec_compare_tol, cyclic_subgroups,
831  all_subgroups, use_all_subgroups);
832 
833  Eigen::MatrixXcd symmetrizer_matrix = make_irrep_symmetrizer_matrix(
834  irrep_special_directions, irrep_subspace, vec_compare_tol);
835 
836  IrrepInfo symmetrized_irrep{irrep};
837  symmetrized_irrep.trans_mat =
838  (irrep_subspace * symmetrizer_matrix).adjoint();
839  symmetrized_irrep.directions = to_real(irrep_special_directions);
840  symmetrized_irreps.push_back(symmetrized_irrep);
841  }
842  return symmetrized_irreps;
843 }
844 
845 } // namespace IrrepDecompositionImpl
846 
847 } // namespace SymRepTools_v2
848 
849 } // namespace CASM
Eigen::CwiseUnaryOp< decltype(Local::round_l< typename Derived::Scalar >), const Derived > round(const Eigen::MatrixBase< Derived > &val)
Round Eigen::MatrixXd.
T norm(const Tensor< T > &ttens)
Definition: Tensor.hh:932
IdentitySymRepBuilder Identity()
Eigen::MatrixXd real_Zero(Index rows, Index cols)
Eigen::MatrixXcd make_commuter(CommuterParamsCounter const &params, MatrixRep const &rep, GroupIndices const &head_group, Eigen::MatrixXcd const &kernel)
std::vector< IrrepInfo > make_fullspace_irreps(std::vector< IrrepInfo > const &subspace_irreps, Eigen::MatrixXd const &subspace)
Convert irreps generated for a subspace to full space dimension.
Eigen::MatrixXd make_invariant_space(MatrixRep const &rep, GroupIndices const &head_group, Eigen::MatrixXd const &subspace)
Expand subspace by application of group, and orthogonalize.
Eigen::VectorXcd make_characters(std::vector< Eigen::MatrixXcd > const &rep)
Calculate character for all matrices in rep.
Eigen::VectorXcd make_irrep_characters(std::vector< Eigen::MatrixXcd > const &rep, Index begin, Index end)
Find characters for block in range [begin, end)
Eigen::MatrixXcd normalize_commuter(Eigen::MatrixXcd const &commuter)
Eigen::MatrixXcd prettyc(const Eigen::MatrixXcd &M)
Round entries that are within tol of being integer to that integer value.
Eigen::MatrixXcd extend(Eigen::MatrixXcd const &space_A, Eigen::MatrixXcd const &space_B)
Return matrix combining columns of space_A and space_B.
Eigen::MatrixXcd make_irrep_subspace(Eigen::MatrixXcd const &KV_matrix, Index begin, Index end, bool allow_complex)
Make an irreducible subspace.
Index find_end_of_equal_eigenvalues(Index begin, Eigen::VectorXd const &eigenvalues)
bool is_extended_by(Eigen::MatrixXcd const &space_A, Eigen::MatrixXcd const &space_B)
Return true if space_A is extended by space_B.
bool is_irrep(MatrixRep const &rep, GroupIndices const &head_group)
Eigen::MatrixXcd complex_Zero(Index rows, Index cols)
Eigen::MatrixXcd complex_I(Index rows, Index cols)
Eigen::MatrixXcd make_kernel(Eigen::MatrixXcd const &subspace)
IrrepInfo subspace_to_full_space(IrrepInfo const &subspace_irrep, Eigen::MatrixXd const &subspace)
Transforms IrrepInfo constructed for a subspace to be IrrepInfo appropriate for the full space (full ...
Index get_total_dim(std::set< PossibleIrrep > const &irreps)
Eigen::MatrixXd pretty(const Eigen::MatrixXd &M)
Round entries that are within tol of being integer to that integer value.
bool make_is_block_diagonal(std::vector< Eigen::MatrixXcd > const &rep, Index begin, Index end, double tol)
Check if approximately zero outside block along diagonal.
std::complex< double > frobenius_product(Eigen::MatrixXcd const &matrix)
std::vector< IrrepInfo > irrep_decomposition(MatrixRep const &rep, GroupIndices const &head_group, bool allow_complex)
Finds irreducible subspaces that comprise an underlying subspace.
double make_squared_norm(Eigen::VectorXcd const &characters)
Eigen::MatrixXd real_I(Index rows, Index cols)
std::vector< PossibleIrrep > make_possible_irreps(Eigen::MatrixXcd const &commuter, Eigen::MatrixXcd const &kernel, MatrixRep const &rep, GroupIndices const &head_group, bool allow_complex)
std::vector< IrrepInfo > make_irrep_info(std::set< PossibleIrrep > const &irreps)
Make a vector of IrrepInfo from PossibleIrreps.
MatrixRep make_subspace_rep(MatrixRep const &fullspace_rep, Eigen::MatrixXd const &subspace)
std::vector< IrrepInfo > symmetrize_irreps(MatrixRep const &subspace_rep, GroupIndices const &head_group, std::vector< IrrepInfo > const &irreps, GroupIndicesOrbitVector const &cyclic_subgroups, GroupIndicesOrbitVector const &all_subgroups)
Symmetrize IrrepInfo, by finding high symmetry directions and aligning the irrep subspace basis with ...
Eigen::MatrixXcd make_irrep_symmetrizer_matrix(multivector< Eigen::VectorXcd >::X< 2 > const &irrep_special_directions, Eigen::MatrixXcd const &irrep_subspace, double vec_compare_tol)
Make an irreducible space symmetrizer matrix using special directions.
Definition: Symmetrizer.cc:101
std::set< Index > GroupIndices
_Real< Derived > to_real(Eigen::MatrixBase< Derived > const &mat)
Definition: to_real.hh:29
std::vector< GroupIndicesOrbit > GroupIndicesOrbitVector
multivector< Eigen::VectorXcd >::X< 2 > make_irrep_special_directions(MatrixRep const &rep, GroupIndices const &head_group, Eigen::MatrixXcd const &irrep_subspace, double vec_compare_tol, GroupIndicesOrbitVector const &cyclic_subgroups, GroupIndicesOrbitVector const &all_subgroups, bool use_all_subgroups=false)
Find high-symmetry directions in a irreducible space.
Definition: Symmetrizer.cc:39
std::vector< Eigen::MatrixXd > MatrixRep
Main CASM namespace.
Definition: APICommand.hh:8
bool almost_equal(ClusterInvariants const &A, ClusterInvariants const &B, double tol)
Check if ClusterInvariants are equal.
Eigen::MatrixXd MatrixXd
const double TOL
Definition: definitions.hh:30
Container::value_type sum(const Container &container, typename Container::value_type init_val=0)
Definition: algorithm.hh:131
bool almost_zero(const T &val, double tol=TOL)
If T is not integral, use std::abs(val) < tol;.
Definition: CASM_math.hh:104
INDEX_TYPE Index
For long integer indexing:
Definition: definitions.hh:39
Eigen::VectorXd VectorXd
Data structure used for storing and checking possible irreps.
bool is_irrep
is_block_diagonal && characters_squared_norm ~= head_group_size;
bool is_block_diagonal
irrep dimension / number of equal eigenvalues
Index end
col index in eigenvalues/eigvectors for this irrep
PossibleIrrep(Eigen::VectorXd const &eigenvalues, Eigen::MatrixXcd const &KV_matrix, std::vector< Eigen::MatrixXcd > const &transformed_rep, Index _head_group_size, double _tol, bool allow_complex, Index _begin, Index _end)
Eigen::MatrixXcd subspace
(K * V).block(0, begin, K.rows(), irrep_dim)
std::vector< std::vector< Eigen::VectorXd > > directions
typename multivector_impl::multivector_tmp< T, N >::type X
Definition: multivector.hh:28