CASM  1.1.0
A Clusters Approach to Statistical Mechanics
SymRepTools.cc
Go to the documentation of this file.
2 
3 #include <numeric>
4 
5 #include "casm/casm_io/Log.hh" // TODO: replace error log with exceptions
9 #include "casm/misc/CASM_math.hh"
10 #include "casm/misc/algorithm.hh"
17 
18 namespace CASM {
19 namespace {
22 Eigen::MatrixXcd prettyc(const Eigen::MatrixXcd &M) {
23  double tol = 1e-10;
24  Eigen::MatrixXcd Mp(M);
25  for (int i = 0; i < M.rows(); i++) {
26  for (int j = 0; j < M.cols(); j++) {
27  if (std::abs(std::round(M(i, j).real()) - M(i, j).real()) < tol) {
28  Mp(i, j).real(std::round(M(i, j).real()));
29  }
30  if (std::abs(std::round(M(i, j).imag()) - M(i, j).imag()) < tol) {
31  Mp(i, j).imag(std::round(M(i, j).imag()));
32  }
33  }
34  }
35  return Mp;
36 }
37 
41  double tol = 1e-10;
42  Eigen::MatrixXd Mp(M);
43  for (int i = 0; i < M.rows(); i++) {
44  for (int j = 0; j < M.cols(); j++) {
45  if (std::abs(std::round(M(i, j)) - M(i, j)) < tol) {
46  Mp(i, j) = std::round(M(i, j));
47  }
48  }
49  }
50  return Mp;
51 }
52 } // namespace
53 } // namespace CASM
54 
55 namespace CASM {
56 namespace Local {
57 
58 struct IrrepCompare {
59  bool operator()(SymRepTools::IrrepInfo const &irrep_a,
60  SymRepTools::IrrepInfo const &irrep_b) const {
61  Eigen::VectorXcd const &a = irrep_a.characters;
62  Eigen::VectorXcd const &b = irrep_b.characters;
63 
64  typedef std::complex<double> C;
65 
66  // Check if a and b are identity irrep, identity always compares less than
67  // other irreps
68  bool a_id = almost_equal(a[0], C(1., 0.)) &&
69  almost_equal(C(a.sum()), C(a.size(), 0.));
70  bool b_id = almost_equal(b[0], C(1., 0.)) &&
71  almost_equal(C(b.sum()), C(b.size(), 0.));
72 
73  if (a_id != b_id)
74  return a_id;
75  else if (a_id) {
76  // Index is used to break ties if they are both identity
77  return irrep_a.index < irrep_b.index;
78  }
79 
80  // Low-dimensional irreps come before higher dimensional
81  if (!almost_equal(a[0], b[0])) return a[0].real() < b[0].real();
82 
83  // 'gerade' irreps come before 'ungerade' irreps
84  // This check may need to be improved to know whether inversion is actually
85  // present
86  bool a_g = almost_equal(a[0], a[a.size() - 1]);
87  bool b_g = almost_equal(b[0], b[b.size() - 1]);
88  if (a_g != b_g) return a_g;
89 
90  // Finally, compare lexicographically (real first, then imag)
91  for (Index i = 0; i < a.size(); ++i) {
92  if (!almost_equal(a[i].real(), b[i].real()))
93  return a[i].real() > b[i].real();
94  }
95 
96  if (irrep_a.complex || irrep_b.complex) {
97  for (Index i = 0; i < a.size(); ++i) {
98  if (!almost_equal(a[i].imag(), b[i].imag()))
99  return a[i].imag() > b[i].imag();
100  }
101  }
102 
103  // Index is used to break ties
104  return irrep_a.index < irrep_b.index;
105  }
106 };
107 
108 //*******************************************************************************************
109 template <typename T>
110 struct _RealType;
111 
112 template <typename T>
113 using _Real = typename _RealType<T>::Type;
114 
115 template <typename T>
116 struct _RealType<std::vector<T>> {
117  using Type = std::vector<_Real<T>>;
118 };
119 
120 template <typename Scalar, int RowsAtCompileTime, int ColsAtCompileTime>
121 struct _RealType<Eigen::Matrix<Scalar, RowsAtCompileTime, ColsAtCompileTime>> {
122  using Type = Eigen::Matrix<double, RowsAtCompileTime, ColsAtCompileTime>;
123 };
124 
125 template <typename Derived>
126 _Real<Derived> _real(Eigen::MatrixBase<Derived> const &mat) {
127  return mat.real().template cast<double>();
128 }
129 
130 template <typename T>
131 _Real<std::vector<T>> _real(std::vector<T> const &vec) {
132  std::vector<_Real<T>> result;
133  result.reserve(vec.size());
134  for (T const &el : vec) {
135  result.emplace_back(_real(el));
136  }
137 
138  return result;
139 }
140 
141 //*******************************************************************************************
147  SymGroup const &head_group) {
148  if (!_rep.size() || !head_group.size() || !_rep.MatrixXd(head_group[0]))
149  return Eigen::MatrixXd();
150 
151  Eigen::MatrixXd block_shape(
152  _rep.MatrixXd(head_group[0])
153  ->cwiseProduct(*_rep.MatrixXd(head_group[0])));
154 
155  for (Index i = 1; i < head_group.size(); i++) {
156  block_shape += _rep.MatrixXd(head_group[i])
157  ->cwiseProduct(*_rep.MatrixXd(head_group[i]));
158  }
159 
160  return block_shape;
161 }
162 
163 //*******************************************************************************************
164 
165 // assumes that representation is real-valued and irreducible
166 static Eigen::MatrixXcd _irrep_symmetrizer_from_directions(
167  multivector<Eigen::VectorXcd>::X<2> const &special_directions,
168  Eigen::Ref<const Eigen::MatrixXcd> const &_subspace,
169  double vec_compare_tol) {
170  // Four strategies, in order of desparation
171  // 1) find a spanning set of orthogonal axes within a single orbit of special
172  // directions 2) find a spanning set of orthogonal axes within the total set
173  // of special directions 3) perform qr decomposition on lowest-multiplicity
174  // orbit to find spanning set of axes 4) find column-echelon form of _subspace
175  // matrix to get a sparse/pretty set of axes
176  Eigen::MatrixXcd result;
177  Index dim = _subspace.cols();
178  Index min_mult = 10000;
179  bool orb_orthog = false;
180  bool tot_orthog = false;
181 
182  Eigen::MatrixXcd axes, orb_axes, tot_axes;
183  Index tot_col(0);
184  tot_axes.setZero(_subspace.rows(), dim);
185 
186  for (auto const &orbit : special_directions) {
187  // Strategy 1
188  if ((orb_orthog && orbit.size() < min_mult) || !orb_orthog) {
189  orb_axes.setZero(_subspace.rows(), dim);
190  Index col = 0;
191  for (auto const &el : orbit) {
192  if (almost_zero((el.adjoint() * orb_axes).eval(), vec_compare_tol)) {
193  if (col < orb_axes.cols())
194  orb_axes.col(col++) = el;
195  else {
196  std::stringstream errstr;
197  errstr
198  << "Error in irrep_symmtrizer_from_directions(). Constructing "
199  "coordinate axes from special directions of space spanned "
200  "by row vectors:\n "
201  << _subspace.transpose()
202  << "\nAxes collected thus far are the row vectors:\n"
203  << orb_axes.transpose()
204  << "\nBut an additional orthogonal row vector has been found:\n"
205  << el.transpose()
206  << "\nindicating that subspace matrix is malformed.";
207  throw std::runtime_error(errstr.str());
208  }
209  }
210  }
211  if (col == dim) {
212  // std::cout << "PLAN A-- col: " << col << "; dim: " << dim << ";
213  // min_mult: " << min_mult << "; orthog: " << orb_orthog << ";\naxes:
214  // \n"
215  // << axes << "\norb_axes: \n" << orb_axes << "\n\n";
216  orb_orthog = true;
217  min_mult = orbit.size();
218  axes = orb_axes;
219  }
220  }
221 
222  // Greedy(ish) implementation of strategy 2 -- may not find a solution, even
223  // if it exists
224  if (!orb_orthog && !tot_orthog) {
225  for (auto const &el : orbit) {
226  if (almost_zero((el.adjoint() * tot_axes).eval(), vec_compare_tol)) {
227  if (tot_col < tot_axes.cols())
228  tot_axes.col(tot_col++) = el;
229  else {
230  std::stringstream errstr;
231  errstr
232  << "Error in irrep_symmtrizer_from_directions(). Constructing "
233  "coordinate axes from special directions of space spanned "
234  "by row vectors:\n "
235  << _subspace.transpose()
236  << "\nAxes collected thus far are the row vectors:\n"
237  << tot_axes.transpose()
238  << "\nBut an additional orthogonal row vector has been found:\n"
239  << el.transpose()
240  << "\nindicating that subspace matrix is malformed.";
241  throw std::runtime_error(errstr.str());
242  }
243  }
244  }
245  if (tot_col == dim) {
246  // std::cout << "PLAN B-- col: " << tot_col << "; dim: " << dim << ";
247  // min_mult: " << min_mult << "; orthog: " << tot_orthog << ";\naxes:
248  // \n"
249  // << axes << "\ntot_axes: \n" << tot_axes << "\n\n";
250  tot_orthog = true;
251  axes = tot_axes;
252  }
253  }
254 
255  // Strategy 3
256  if (!orb_orthog && !tot_orthog && orbit.size() < min_mult) {
257  orb_axes.setZero(_subspace.rows(), orbit.size());
258  for (Index col = 0; col < orbit.size(); ++col) {
259  orb_axes.col(col) = orbit[col];
260  }
261  // std::cout << "PLAN C-- dim: " << dim << "; min_mult: " << min_mult <<
262  // "; orthog: " << orb_orthog << ";\naxes: \n" << axes;
263  min_mult = orbit.size();
264  axes = Eigen::MatrixXcd(orb_axes.colPivHouseholderQr().matrixQ())
265  .leftCols(dim);
266  // std::cout << "\norb_axes: \n" << orb_axes << "\n\n";
267  }
268  }
269  // std::cout << "axes: \n" << axes << "\n";
270  if (axes.cols() == 0) {
271  // std::cout << "Plan D\n";
272  // SubspaceSymCompare doesn't actually use _rep
273  result = _subspace.colPivHouseholderQr().solve(
274  representation_prepare_impl(_subspace, vec_compare_tol));
275  } else {
276  // Strategy 4
277  result = _subspace.colPivHouseholderQr().solve(axes);
278  }
279 
280  // std::cout << "result: \n" << result << "\n"
281  //<< "_subspace*result: \n" << _subspace *result << "\n";
282  return result; //.transpose();
283 }
284 
285 //*******************************************************************************************
286 
288  SymRepTools::IrrepInfo const &irrep, SymGroupRep const &_rep,
289  SymGroup const &head_group) {
290  Eigen::MatrixXd t_axes = irrep.trans_mat.transpose().real();
292  Eigen::VectorXd v = axes.col(0);
293  Eigen::VectorXd vbest;
294 
295  axes.setZero(irrep.vector_dim(), irrep.irrep_dim());
296 
297  axes.col(0) = v;
298 
299  for (Index i = 1; i < axes.cols(); ++i) {
300  double bestproj = -1;
301  for (SymOp const &op : head_group) {
302  v = (*_rep.MatrixXd(op)) * axes.col(0);
303  // std::cout << "v: " << v.transpose() << std::endl;
304  bool skip_op = false;
305  for (Index j = 0; j < i; ++j) {
306  if (almost_equal(v, axes.col(j))) {
307  skip_op = true;
308  break;
309  }
310  }
311  if (skip_op) continue;
312 
313  // std::cout << "bproj: " << bestproj << " proj: " <<
314  // (v.transpose()*axes).transpose() << std::endl;
315  if (bestproj < (v.transpose() * axes).sum()) {
316  bestproj = (v.transpose() * axes).sum();
317  vbest = v;
318  }
319  }
320  axes.col(i) = vbest;
321  }
322  return SymRepTools::IrrepWedge(irrep, axes);
323 }
324 
325 //*******************************************************************************************
326 
327 bool _rep_check(SymGroupRep const &_rep, SymGroup const &head_group,
328  bool verbose) {
329  bool passed = true;
330  for (Index ns = 0; ns < head_group.size(); ns++) {
331  Eigen::MatrixXd tmat = *(_rep.MatrixXd(head_group[ns]));
332  if (!almost_equal<double>((tmat * tmat.transpose()).trace(), tmat.cols())) {
333  passed = false;
334  if (verbose)
335  std::cout << " Representation failed full-rank/orthogonality check!\n"
336  << " ns: " << ns << "\n"
337  << " Matrix: \n"
338  << tmat << "\n\n"
339  << " Matrix*transpose: \n"
340  << tmat.transpose() * tmat << "\n\n";
341  }
342  for (Index ns2 = ns; ns2 < head_group.size(); ns2++) {
343  auto prod = (*(_rep.MatrixXd(head_group[ns]))) *
344  (*(_rep.MatrixXd(head_group[ns2])));
345  Index iprod = head_group[ns].ind_prod(head_group[ns2]);
346  double norm = (prod - *(_rep.MatrixXd(iprod))).norm();
347  if (!almost_zero(norm)) {
348  passed = false;
349  if (verbose)
350  std::cout << " Representation failed multiplication check!\n"
351  << " ns: " << ns << " ns2: " << ns2 << " iprod: " << iprod
352  << " NO MATCH\n"
353  << " prod: \n"
354  << prod << "\n\n"
355  << " mat(ns): \n"
356  << *(_rep.MatrixXd(ns)) << "\n\n"
357  << " mat(ns2): \n"
358  << *(_rep.MatrixXd(ns2)) << "\n\n"
359  << " mat(iprod): \n"
360  << *(_rep.MatrixXd(iprod)) << "\n\n";
361  }
362  }
363  }
364  return passed;
365 }
366 
367 template <typename T>
369  SymRepTools::IrrepInfo const &irrep, Eigen::MatrixBase<T> const &subspace) {
370  SymRepTools::IrrepInfo result(irrep);
371  result.trans_mat = irrep.trans_mat *
372  subspace.adjoint().template cast<std::complex<double>>();
373 
374  result.directions.clear();
375  for (const auto &direction_orbit : irrep.directions) {
376  std::vector<Eigen::VectorXd> new_orbit;
377  new_orbit.reserve(direction_orbit.size());
378  for (const auto &directions : direction_orbit) {
379  new_orbit.push_back(subspace * directions);
380  }
381  result.directions.push_back(std::move(new_orbit));
382  }
383  return result;
384 }
385 
386 } // namespace Local
387 } // namespace CASM
388 
389 namespace CASM {
390 namespace SymRepTools {
391 IrrepInfo::IrrepInfo(Eigen::MatrixXcd _trans_mat, Eigen::VectorXcd _characters)
392  : index(0),
393  trans_mat(std::move(_trans_mat)),
394  characters(std::move(_characters)) {
395  complex = !almost_zero(trans_mat.imag());
396 }
397 
398 } // namespace SymRepTools
399 
400 //*******************************************************************************************
401 
402 Index num_blocks(SymGroupRep const &_rep, SymGroup const &head_group) {
403  Eigen::MatrixXd bmat(Local::_block_shape_matrix(_rep, head_group));
404 
405  if (bmat.cols() == 0) return 0;
406 
407  Index Nb = 1;
408 
409  // count zeros on first superdiagonal
410  for (EigenIndex i = 0; i + 1 < bmat.rows(); i++) {
411  if (almost_zero(bmat(i, i + 1))) Nb++;
412  }
413 
414  return Nb;
415 }
416 
417 //*******************************************************************************************
418 
419 // assumes that representation is real-valued and irreducible
421  SymGroup const &head_group,
422  double vec_compare_tol) {
423  Index dim = _rep.MatrixXd(0)->cols();
424  return irrep_symmetrizer(
425  _rep, head_group, Eigen::MatrixXcd::Identity(dim, dim), vec_compare_tol);
426 }
427 
428 //*******************************************************************************************
429 
430 // assumes that representation is real-valued and irreducible
432  SymGroupRep const &_rep, SymGroup const &head_group,
433  Eigen::Ref<const Eigen::MatrixXcd> const &_subspace,
434  double vec_compare_tol) {
435  // Result of sdirs is ordered by symmetry breaking properties of different
436  // directions. We won't do any element-based comparisons after this point, in
437  // order to ensure that our choice of axes is totally determined by symmetry
439  special_irrep_directions(_rep, head_group, _subspace, vec_compare_tol);
440  return std::make_pair(Local::_irrep_symmetrizer_from_directions(
441  sdirs, _subspace, vec_compare_tol),
442  std::move(sdirs));
443 }
444 
445 //*******************************************************************************************
446 
448  std::vector<SymRepTools::IrrepInfo> const &irreps) {
449  Index row = 0;
450  Index col = 0;
451  for (auto const &irrep : irreps) {
452  col = irrep.vector_dim();
453  row += irrep.irrep_dim();
454  }
455  Eigen::MatrixXd trans_mat(row, col);
456  row = 0;
457  for (auto const &irrep : irreps) {
458  trans_mat.block(row, 0, irrep.irrep_dim(), irrep.vector_dim()) =
459  irrep.trans_mat.real();
460  row += irrep.irrep_dim();
461  }
462  return trans_mat;
463 }
464 
465 //*******************************************************************************************
466 
468  SymGroupRep const &_rep, SymGroup const &head_group) {
469  std::vector<SymRepTools::IrrepInfo> irreps =
470  irrep_decomposition(_rep, head_group, false);
471 
473  result.reserve(irreps.size());
474 
475  for (auto const &irrep : irreps) {
476  result.emplace_back(Local::_real(irrep.directions));
477  }
478  return result;
479 }
480 
481 //*******************************************************************************************
487 
489  SymGroupRep const &_rep, SymGroup const &head_group,
490  double vec_compare_tol) {
491  Index dim = _rep.dim();
493  _rep, head_group, Eigen::MatrixXcd::Identity(dim, dim), vec_compare_tol);
494 }
495 
496 //*******************************************************************************************
502 
504  SymGroupRep const &_rep, SymGroup const &head_group,
505  Eigen::Ref<const Eigen::MatrixXcd> const &_subspace, double vec_compare_tol,
506  bool all_subgroups) {
507  auto sgroups =
508  (all_subgroups ? head_group.subgroups() : head_group.small_subgroups());
509 
510  std::vector<Eigen::VectorXcd> tdirs;
511  Eigen::MatrixXd R;
512  Index dim = _rep.dim();
513 
514  // Loop over small (i.e., cyclic) subgroups and hope that each special
515  // direction is invariant to at least one small subgroup
516  for (auto const &orbit : sgroups) {
517  // Reynolds for small subgroup *(orbit.begin()) in irrep subspace i
518  R.setZero(dim, dim);
519 
520  for (Index op : *(orbit.begin())) {
521  R += *(_rep.MatrixXd(head_group[op]));
522  }
523 
524  if ((R * _subspace).norm() < TOL) continue;
525 
526  // Find spanning vectors of column space of R*_subspace, which is projection
527  // of _subspace into its invariant component
528  auto QR = (R * _subspace).colPivHouseholderQr();
529  QR.setThreshold(TOL);
530  // std::cout<< " ----------------------------------------------------------
531  // " << std::endl; std::cout << "Identity projector: \n" << R << "\n\n"
532  // << "Projected coordinates:\n" << R*_subspace << "\n\n";
533  // std::cout << "Q matrix: \n" << Eigen::MatrixXd(QR.matrixQ()) << "\n";
534  // std::cout << "R matrix: \n" << Eigen::MatrixXd(QR.matrixR().template
535  // triangularView<Eigen::Upper>()) << "\n";
536  // // If only one spanning vector, it is special direction
537  if (QR.rank() > 1) continue;
538  Eigen::MatrixXcd Q = QR.matrixQ();
539  // std::cout << "Special direction identified:\n" << Q.col(0).transpose() <<
540  // "\n\n"; std::cout << "MatrixR:a\n" <<
541  // Eigen::MatrixXd(QR.matrixR().template triangularView<Eigen::Upper>()) <<
542  // "\n";
543  // // PRINT OUT the Reynolds matrix if its going to be added
544  // std::cout<< " ----------------------------------------------------------
545  // " << std::endl; std::cout<< R <<std::endl;
546  // Convert from irrep subspace back to total space and push_back
547  tdirs.push_back(Q.col(0));
548  tdirs.push_back(-Q.col(0));
549  // std::cout<< " The added vector is :
550  // "<<t_result.back().back().transpose()<<std::endl;
551  // //Calculate the rank using an SVD decomposition
552  // Eigen::JacobiSVD<Eigen::MatrixXd> svd(R);
553  // svd.setThreshold(1e-5);
554  // std::cout<<"The SVD rank is : "<<svd.rank()<<std::endl;
555  // std::cout<<"The singular values are :
556  // "<<svd.singularValues().transpose()<<std::endl;
557  // //Loop over the SVD and count the number of non-zero singular values
558  // int svd_rank = 0;
559  // for(auto singular_index = 0 ; singular_index<svd.singularValues().size()
560  // ; singular_index++)
561  // svd_rank += (std::abs(svd.singularValues()[singular_index]) > 1e-5);
562  // std::cout<<"The calculated rank is : "<<svd_rank<<std::endl;
563  // std::cout<<"The threshold is : "<<svd.threshold()<<std::endl;
564  // std::cout<<"The maxPivot is : "<<QR.maxPivot()<<std::endl;
565  // std::cout<< " ----------------------------------------------------------
566  // " << std::endl;
567  }
568 
569  // t_result may contain duplicates, or elements that are equivalent by
570  // symmetry. To discern more info, we need to exclude duplicates and find
571  // the orbit of the directions. this should also
572  // reveal the invariant subgroups.
573 
574  using SymCompareType =
576  using VectorOrbit = Orbit<SymCompareType>;
577  std::set<VectorOrbit> orbit_result;
578  make_orbits(tdirs.begin(), tdirs.end(), head_group,
579  SymCompareType(vec_compare_tol, _rep),
580  std::inserter(orbit_result, orbit_result.begin()));
581 
583  // std::cout << "subspace: \n" << _subspace << std::endl;
584  // std::cout << "Found " << orbit_result.size() << " direction orbits:\n";
585  for (VectorOrbit const &orbit : orbit_result) {
586  // std::cout << orbit.begin()->transpose() << std::endl;
587  // std::cout << "---------\n";
588  result.emplace_back(orbit.begin(), orbit.end());
589  }
590 
591  if (all_subgroups || result.size() >= _subspace.cols()) {
592  // std::cout << "special_irrep_direcions RETURNING RESULT\n";
593  return result;
594  } else {
595  // std::cout << "result size: " << result.size() << "; _subspace cols: " <<
596  // _subspace.cols() << "\n"; std::cout << "special_irrep_direcions REDOING
597  // CALCULATION\n";
598  return special_irrep_directions(_rep, head_group, _subspace,
599  vec_compare_tol, true);
600  }
601 }
602 
603 //*******************************************************************************************
604 
605 // Similar to algorithm for finding special directions, except we also find
606 // special planes, n-planes, etc. The matrices that are returned have column
607 // vectors that span the special subspaces. The subspaces are arranged in
608 // orbits of subspaces that are equivalent by symmetry
609 
610 std::vector<std::vector<Eigen::MatrixXd>> special_subspaces(
611  SymGroupRep const &_rep, SymGroup const &head_group) {
612  if (!_rep.size() || !_rep.MatrixXd(0)) {
613  err_log() << "CRITICAL ERROR: In special_subspaces() called on "
614  "imcompatible SymGroupRep.\n Exiting...\n";
615  exit(1);
616  }
617 
618  Index i, j, k;
619 
620  std::vector<std::vector<Eigen::MatrixXd>>
621  ssubs; // std::vector of orbits of special directions
622 
623  Eigen::MatrixXd tsub, ttrans;
624 
625  // Operation indices of subgroups
626  auto const &sg_op_inds(head_group.subgroups());
627 
628  Eigen::ColPivHouseholderQR<Eigen::MatrixXd> QR(*(_rep.MatrixXd(0)));
629 
630  QR.setThreshold(TOL);
631 
632  Eigen::MatrixXd Reynolds(*(_rep.MatrixXd(0)));
633 
634  // 'i' loops over subgroup "orbits". There are 0 to 'dim' special directions
635  // associated with each orbit.
636  for (i = 0; i < sg_op_inds.size(); i++) {
637  if (sg_op_inds[i].size() == 0 || sg_op_inds[i].begin()->size() == 0) {
638  err_log() << "CRITICAL ERROR: In special_subspaces(), attempting to use "
639  "a zero-size subgroup.\n Exiting...\n";
640  exit(1);
641  }
642 
643  Reynolds.setZero();
644  // j loops over elements of "prototype" subgroup to get the Reynold's
645  // operator for the vector space on which the representation is defined
646  for (Index op : *(sg_op_inds[i].begin())) {
647  Reynolds += *(_rep.MatrixXd(head_group[op]));
648  }
649 
650  Reynolds /= double(sg_op_inds[i].begin()->size());
651 
652  // Column space of Reynold's matrix is invariant under the subgroup
653  QR.compute(Reynolds);
654 
655  // If there are no special subspaces, the Reynold's operator has zero
656  if (QR.rank() < 1) continue;
657 
658  // The first (QR.rank()) columns of QR.matrixQ() span a special subspace --
659  // this can be accessed as QR.matrixQ().leftCols(QR.rank())
660  tsub = Eigen::MatrixXd(QR.householderQ()).leftCols(QR.rank());
661 
662  // See if tsub has been found. Because we only keep orthonormal spanning
663  // vectors, this check is significantly simplified
664  // -- the matrix (tsub.transpose()*ssubs[j][k]) must have a determinant of
665  // +/-1 if the two matrices span the same column space
666  for (j = 0; j < ssubs.size(); j++) {
667  if (ssubs[j][0].cols() != tsub.cols()) continue;
668 
669  for (k = 0; k < ssubs[j].size(); k++) {
670  double tdet = (tsub.transpose() * ssubs[j][k]).determinant();
671  if (almost_equal(std::abs(tdet), 1.0)) break;
672  }
673  if (k < ssubs[j].size()) break;
674  }
675  if (j < ssubs.size()) continue;
676 
677  // tsub is new -- get equivalents
678  ssubs.push_back(std::vector<Eigen::MatrixXd>());
679  for (j = 0; j < head_group.size(); j++) {
680  ttrans = (*(_rep.MatrixXd(head_group[j]))) * tsub;
681  for (k = 0; k < ssubs.back().size(); k++) {
682  double tdet = (ttrans.transpose() * ssubs.back()[k]).determinant();
683  if (almost_equal(std::abs(tdet), 1.0)) break;
684  }
685  if (k == ssubs.back().size()) ssubs.back().push_back(ttrans);
686  }
687 
688  // Unlike directions, the negative orientation of a subspace matrix is not
689  // considered distinct. This is because we are only concerned about
690  // coordinate axes.
691  }
692 
693  // Order subspaces by dimension, small to large; within blocks of equal
694  // dimension, order from lowest multiplicity to highest
695  for (i = 0; i < ssubs.size(); i++) {
696  for (j = i + 1; j < ssubs.size(); j++) {
697  if (ssubs[i][0].cols() > ssubs[j][0].cols()) ssubs[i].swap(ssubs[j]);
698  if (ssubs[i][0].cols() == ssubs[j][0].cols() &&
699  ssubs[i].size() > ssubs[j].size())
700  ssubs[i].swap(ssubs[j]);
701  }
702  }
703 
704  return ssubs;
705 }
706 //*******************************************************************************************
707 
708 bool is_irrep(SymGroupRep const &_rep, SymGroup const &head_group) {
709  double tvalue = 0;
710 
711  for (Index i = 0; i < head_group.size(); i++) {
712  tvalue += (_rep[head_group[i].index()]->character()) *
713  (_rep[head_group[i].index()]->character());
714  }
715 
716  if (Index(round(tvalue)) == head_group.size()) {
717  return true;
718  } else {
719  return false;
720  }
721 }
722 
723 //*******************************************************************************************
724 
726  SymGroupRep const &_rep, SymGroup const &head_group,
727  Eigen::Ref<const Eigen::MatrixXd> const &_subspace, bool calc_wedge) {
728  VectorSpaceSymReport result;
729 
730  if (calc_wedge) {
731  result.irreducible_wedge =
732  SymRepTools::symrep_subwedges(_rep, head_group, _subspace);
733 
734  if (!result.irreducible_wedge.empty()) {
735  result.irreps.reserve(result.irreducible_wedge[0].irrep_wedges().size());
736  for (auto const &wedge : result.irreducible_wedge[0].irrep_wedges()) {
737  result.irreps.push_back(wedge.irrep_info);
738  }
739  }
740  } else {
741  result.irreps = irrep_decomposition(_rep, head_group, _subspace, false);
742  }
744  full_trans_mat(result.irreps).adjoint();
745 
746  result.axis_glossary = std::vector<std::string>(
747  result.symmetry_adapted_dof_subspace.rows(), "x");
748  Index i = 0;
749  for (std::string &x : result.axis_glossary) {
750  x += std::to_string(++i);
751  }
752 
753  for (SymOp const &op : head_group) {
754  result.symgroup_rep.push_back(*(_rep.MatrixXd(op)));
755  }
756  return result;
757 }
758 
759 //*******************************************************************************************
760 
761 std::vector<SymRepTools::IrrepInfo> irrep_decomposition(
762  SymGroupRep const &_rep, SymGroup const &head_group, bool allow_complex) {
763  return irrep_decomposition(_rep, head_group,
764  Eigen::MatrixXd::Identity(_rep.dim(), _rep.dim()),
765  allow_complex);
766 }
767 
768 //*******************************************************************************************
769 
770 std::vector<SymRepTools::IrrepInfo> irrep_decomposition(
771  SymGroupRep const &_rep, SymGroup const &head_group,
772  Eigen::Ref<const Eigen::MatrixXd> const &_subspace, bool allow_complex) {
773  return irrep_decomposition(
774  _rep, head_group,
775  [&](Eigen::Ref<const Eigen::MatrixXcd> const &f_subspace) {
776  return irrep_symmetrizer(_rep, head_group, f_subspace, TOL);
777  },
778  _subspace, allow_complex);
779 }
780 
781 //*******************************************************************************************
782 
783 // Finds the transformation matrix that block-diagonalizes this representation
784 // into irrep blocks The ROWS of trans_mat are the new basis vectors in terms of
785 // the old such that new_symrep_matrix = trans_mat * old_symrep_matrix *
786 // trans_mat.transpose();
787 std::vector<SymRepTools::IrrepInfo> irrep_decomposition(
788  SymGroupRep const &_rep, SymGroup const &head_group,
789  SymRepTools::SymmetrizerFunction const &symmetrizer_func,
790  Eigen::MatrixXd subspace, bool allow_complex) {
791  if (_rep.dim() != subspace.rows()) {
792  throw std::runtime_error(
793  "In irrep_decomposition, subspace matrix does not have proper number "
794  "of rows (should have " +
795  std::to_string(_rep.dim()) + ", but has " +
796  std::to_string(subspace.rows()));
797  }
798 
799  Eigen::MatrixXd symspace(subspace.rows(),
800  subspace.cols() * head_group.size());
801  Index l = 0;
802  if (!subspace.isIdentity()) {
803  // Expand subpsace if it isn't already an invariant subspace
804  for (auto const &op : head_group) {
805  symspace.block(0, l, subspace.rows(), subspace.cols()) =
806  (*(_rep[op.index()]->MatrixXd())) * subspace;
807  l += subspace.cols();
808  }
809  Eigen::ColPivHouseholderQR<Eigen::MatrixXd> qr(symspace);
810  qr.setThreshold(TOL);
811  subspace = Eigen::MatrixXd(qr.householderQ()).leftCols(qr.rank());
812  }
813 
814  // std::cout << "subspace after expansion: \n" << subspace << std::endl;
815 
816  SymGroupRep sub_rep = coord_transformed_copy(_rep, subspace.transpose());
817 
818  auto subspace_symmetrizer =
819  [&](const Eigen::Ref<const Eigen::MatrixXcd> &_subspace) {
820  return irrep_symmetrizer(sub_rep, head_group, _subspace, TOL);
821  };
822 
823  std::vector<SymRepTools::IrrepInfo> irreps = irrep_decomposition(
824  sub_rep, head_group, subspace_symmetrizer, allow_complex);
825 
826  std::vector<SymRepTools::IrrepInfo> result;
827  result.reserve(irreps.size());
828  l = 0;
829  for (auto const &irrep : irreps) {
830  // std::cout << "irrep " << ++l << " index: " << irrep.index << "\n";
831 
832  result.push_back(Local::_subspace_to_full_space(irrep, subspace));
833  }
834  return result;
835 }
836 //*******************************************************************************************
837 
838 // Finds the transformation matrix that block-diagonalizes this representation
839 // into irrep blocks The ROWS of trans_mat are the new basis vectors in terms of
840 // the old such that new_symrep_matrix = trans_mat * old_symrep_matrix *
841 // trans_mat.transpose();
842 std::vector<SymRepTools::IrrepInfo> irrep_decomposition(
843  SymGroupRep const &_rep, SymGroup const &head_group,
844  SymRepTools::SymmetrizerFunction const &symmetrizer_func,
845  bool allow_complex) {
846  std::set<SymRepTools::IrrepInfo, Local::IrrepCompare> result{
848  if (!_rep.size() || !head_group.size() || !_rep.MatrixXd(head_group[0])) {
849  err_log()
850  << "WARNING: In irrep_decomposition(), size of representation is "
851  << _rep.size() << " and MatrixXd address is "
852  << _rep.MatrixXd(head_group[0]) << std::endl
853  << " No valid irreps will be returned.\n";
854  return {};
855  }
856  assert(Local::_rep_check(_rep, head_group, true) &&
857  "REPRESENTATION IS ILL-DEFINED!!");
858  int dim(_rep.dim());
859  std::vector<Eigen::MatrixXcd> commuters;
860  // std::cout.precision(8);
861  // std::cout.flags(std::ios::showpoint | std::ios::fixed | std::ios::right);
862  if (!is_irrep(_rep, head_group)) {
863  // Identity always commutes, and by putting it first we prevent some
864  // accidental degeneracies
865  commuters.push_back(Eigen::MatrixXcd::Identity(dim, dim) /
866  sqrt(double(dim)));
867  }
868  typedef std::complex<double> cplx;
869  std::vector<cplx> phase;
870  phase.push_back(cplx(1.0, 0.0)); // 1+0i
871  phase.push_back(cplx(0.0, 1.0)); // 0+1i
872 
873  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXcd> esolve;
874  Eigen::HouseholderQR<Eigen::MatrixXcd> qr;
875  Eigen::ColPivHouseholderQR<Eigen::MatrixXcd> colqr;
876  colqr.setThreshold(0.001);
877  int Nfound(0);
878  Eigen::MatrixXcd tmat(Eigen::MatrixXcd::Zero(dim, dim)),
879  tcommute(Eigen::MatrixXcd::Zero(dim, dim));
880  Eigen::MatrixXcd adapted_subspace(Eigen::MatrixXcd::Zero(dim, dim));
881 
882  // Initialize kernel as a random orthogonal matrix
883  Eigen::MatrixXcd kernel(((1000 * Eigen::MatrixXcd::Random(dim, dim)) / 1000.)
884  .householderQr()
885  .householderQ());
886  kernel.setIdentity(dim, dim);
887  // std::cout << "kernel matrix:\n" << kernel << std::endl;
888  // Build 'commuters', which span space of real matrices that commute with
889  // SymOpReps 'kernel' is the kernel of adapted_subspace, and as the loop
890  // progresses, 'kernel' shrinks and the rank of adapted_subspace increases
891  for (Index nph = 0; nph < 2; nph++) {
892  for (Index kci = 0; kci < kernel.cols(); kci++) {
893  bool found_new_irreps(false);
894  for (Index kcj = kernel.cols() - 1; kci <= kcj; kcj--) {
895  // std::cout << "nph: " << nph << " kci: " << kci << " kcj: " << kcj
896  // << std::endl;
897  if (kci == kcj && nph > 0) continue;
898  tcommute.setZero();
899  // std::cout << "kernel imag:\n" << kernel.imag() << std::endl;
900  // form commuters by taking outer product of i'th column of the kernel
901  // with the j'th column commuters are constructed to be self-adjoint,
902  // which assures eigenvalues are real
903  tmat = phase[nph] * kernel.col(kci) *
904  kernel.col(kcj).adjoint() // outer product
905  + std::conj(phase[nph]) * kernel.col(kcj) *
906  kernel.col(kci).adjoint(); // adjoint of outer product
907  // std::cout << "tmat:\n" << prettyc(tmat) << std::endl;
908  // apply reynolds operator
909 
910  for (SymOp const &op : head_group) {
911  // std::cout << "Op " << op.index() << ":\n" << (*(_rep.MatrixXd(op)))
912  // << std::endl;
913  tcommute += (*(_rep.MatrixXd(op))) * tmat *
914  (*(_rep.MatrixXd(op))).transpose();
915  }
916 
917  // std::cout << "Raw commuter: \n" << prettyc(tcommute) << std::endl;
918 
919  // Do Gram-Shmidt while building 'commuters'
920 
921  for (Index nc = 0; nc < commuters.size(); nc++) {
922  cplx tproj((commuters[nc].array() * tcommute.array().conjugate())
923  .sum()); // Frobenius product
924  // std::cout << "tproj" << tproj << std::endl;
925  tcommute -= tproj * commuters[nc];
926  }
927 
928  // std::cout << std::endl << "###" << std::endl;
929  // std::cout << "commuter_params: (" << kci << ", " << kcj << ", "
930  // << phase[nph] << ") " << std::endl;
931  // std::cout << "commuters.size(): " << commuters.size() << std::endl;
932  // std::cout << "commuter: \n" << prettyc(tcommute) << std::endl;
933 
934  double tnorm(
935  (tcommute.array().conjugate() * tcommute.array()).sum().real());
936 
937  // std::cout << "Commuter: \n" << tcommute << std::endl;
938  // std::cout << "norm: " << tnorm << std::endl;
939  if (tnorm > TOL) {
940  commuters.push_back(tcommute / sqrt(tnorm));
941  } else
942  continue; // Attempt to construct the next commuter...
943 
944  // Finished building commuter now we can try to harvest irreps from it
945 
946  // construct adapted_subspace from the non-degenerate irreps obtained
947  // from the commuter
948 
949  Index nc = commuters.size() - 1;
950 
951  // magnify the range of eigenvalues to be (I think) independent of
952  // matrix dimension by multiplying by dim^{3/2}
953  esolve.compute(double(dim) * sqrt(double(dim)) * kernel.adjoint() *
954  commuters[nc] * kernel);
955 
956  std::vector<Index> subspace_dims =
957  partition_distinct_values(esolve.eigenvalues());
958 
959  // std::cout << "My big matrix: \n" << commuters[nc] << std::endl;
960  // std::cout << "My little matrix: \n" <<
961  // double(dim)*sqrt(double(dim))*kernel.adjoint()*commuters[nc]*kernel
962  // << std::endl;
963 
964  // Columns of tmat are orthonormal eigenvectors of commuter in terms of
965  // natural basis (they were calculated in terms of kernel as basis)
966  // tmat = kernel/*.conjugate()*/ *
967  // (esolve.eigenvectors().householderQr().householderQ());
968 
969  tmat = kernel * esolve.eigenvectors();
970 
971  // std::cout << "Raw eigenvecs: \n" << esolve.eigenvectors() <<
972  // std::endl; std::cout << "Rotated eigenvecs: \n" << tmat << std::endl;
973 
974  // std::cout << "Eigenvec unitarity: \n" <<
975  // esolve.eigenvectors().adjoint()* esolve.eigenvectors() << std::endl;
976 
977  // std::cout << "rebuild little 1:\n"
978  //<<
979  // esolve.eigenvectors()*esolve.eigenvalues().asDiagonal()*esolve.eigenvectors().inverse()
980  //<< std::endl;
981 
982  // std::cout << "rebuild big 1:\n"
983  //<<
984  // kernel*esolve.eigenvectors()*esolve.eigenvalues().asDiagonal()*esolve.eigenvectors().inverse()*kernel.adjoint()
985  //<< std::endl;
986 
987  // std::cout << "rebuild big 2:\n"
988  //<< tmat*esolve.eigenvalues().asDiagonal()*tmat.adjoint()
989  //<< std::endl;
990 
991  // make transformed copy of the representation
992  std::vector<Eigen::MatrixXcd> trans_rep(head_group.size());
993 
994  for (Index i = 0; i < head_group.size(); i++) {
995  trans_rep[i] =
996  tmat.adjoint() * (*_rep.MatrixXd(head_group[i])) * tmat;
997  }
998  // std::cout << "Eigenvals: " << esolve.eigenvalues().transpose() << "
999  // \ndims: " << subspace_dims << std::endl;
1000 
1001  Index last_i = 0;
1002  for (Index ns = 0; ns < subspace_dims.size(); ns++) {
1003  double sqnorm(0);
1004  Eigen::VectorXcd char_vec(head_group.size());
1005 
1006  // 'ns' indexes an invariant subspace
1007  // Loop over group operation and calculate character vector for the
1008  // group representation on this invariant subspace. If the squared
1009  // norm of the character vector is equal to the group order, the
1010  // invariant subspace is also irreducible
1011  for (Index ng = 0; ng < trans_rep.size(); ng++) {
1012  char_vec[ng] = cplx(0, 0);
1013  for (Index i = last_i; i < last_i + subspace_dims[ns]; i++)
1014  char_vec[ng] += trans_rep[ng](i, i);
1015  // std::norm is squared norm
1016  sqnorm += std::norm(char_vec[ng]);
1017  }
1018  // std::cout << "char_vec:\n" << char_vec << std::endl;
1019  // std::cout << "subspace: " << ns << "; sqnorm: " << sqnorm << "\n";
1020  if (almost_equal(sqnorm,
1021  double(head_group.size()))) { // this representation
1022  // is irreducible
1023  Eigen::MatrixXcd t_irrep_subspace;
1024 
1025  if (allow_complex) {
1026  t_irrep_subspace = tmat.block(0, last_i, dim, subspace_dims[ns]);
1027  } else {
1028  t_irrep_subspace.resize(dim, 2 * subspace_dims[ns]);
1029 
1030  t_irrep_subspace.leftCols(subspace_dims[ns]) =
1031  sqrt(2.0) * tmat.block(0, last_i, dim, subspace_dims[ns])
1032  .real()
1033  .cast<std::complex<double>>();
1034  t_irrep_subspace.rightCols(subspace_dims[ns]) =
1035  sqrt(2.0) * tmat.block(0, last_i, dim, subspace_dims[ns])
1036  .imag()
1037  .cast<std::complex<double>>();
1038  }
1039  // Only append to adapted_subspace if the new columns extend the
1040  // space (i.e., are orthogonal)
1041 
1042  if (almost_zero(
1043  (t_irrep_subspace.adjoint() * adapted_subspace).norm(),
1044  0.001)) {
1045  // std::cout << "---" << std::endl;
1046  // std::cout << "new irrep: " << std::endl;
1047  // std::cout << "characters_squared_norm: " << sqnorm <<
1048  // std::endl;
1049 
1050  qr.compute(t_irrep_subspace);
1051  // it seems stupid to use two different decompositions that do
1052  // almost the same thing, but
1053  // HouseholderQR is not rank-revealing, and colPivHouseholder
1054  // mixes up the columns of the Q matrix.
1055  colqr.compute(t_irrep_subspace);
1056  Index rnk = colqr.rank();
1057  // std::cout << "t_irrep_subspace with rank: " << rnk << " --\n"
1058  // << t_irrep_subspace << std::endl << std::endl;
1059  t_irrep_subspace =
1060  Eigen::MatrixXcd(qr.householderQ()).leftCols(rnk);
1061  // std::cout << "MatrixR: \n" << colqr.matrixR() << std::endl;
1062  auto symmetrizer = symmetrizer_func(t_irrep_subspace);
1063  SymRepTools::IrrepInfo t_irrep(
1064  (t_irrep_subspace * symmetrizer.first).adjoint(), char_vec);
1065  t_irrep.directions = Local::_real(symmetrizer.second);
1066 
1067  if (rnk == 2 * subspace_dims[ns])
1068  t_irrep.pseudo_irrep = true;
1069  else
1070  t_irrep.pseudo_irrep = false;
1071 
1072  // extend adapted_subspace (used to simplify next iteration)
1073  adapted_subspace.block(0, Nfound, dim, rnk) =
1074  t_irrep_subspace; // t_irrep.trans_mat.adjoint();
1075 
1076  auto it = result.find(t_irrep);
1077  if (it != result.end()) {
1078  t_irrep.index++;
1079  ++it;
1080  }
1081 
1082  while (it != result.end() && it->index > 0) {
1083  t_irrep.index++;
1084  ++it;
1085  }
1086 
1087  result.emplace_hint(it, std::move(t_irrep));
1088 
1089  Nfound += rnk;
1090  found_new_irreps = true;
1091  }
1092  // else {
1093  // std::cout << "---" << std::endl;
1094  // std::cout << "Irrep, but does not expand space: " << std::endl;
1095  // std::cout << "characters_squared_norm: " << sqnorm <<
1096  // std::endl;
1097  // }
1098  }
1099  // else {
1100  // std::cout << "---" << std::endl;
1101  // std::cout << "Not an irrep: " << std::endl;
1102  // std::cout << "characters_squared_norm: " << sqnorm << std::endl;
1103  // }
1104  last_i += subspace_dims[ns];
1105  }
1106  if (found_new_irreps) {
1107  qr.compute(adapted_subspace);
1108  kernel = Eigen::MatrixXcd(qr.householderQ()).rightCols(dim - Nfound);
1109  kci = 0;
1110  kci--;
1111  break;
1112  }
1113  }
1114  }
1115  }
1116 
1117  return std::vector<SymRepTools::IrrepInfo>(
1118  std::make_move_iterator(result.begin()),
1119  std::make_move_iterator(result.end()));
1120 }
1121 
1122 //*******************************************************************************************
1123 
1124 // Finds the transformation matrix that block-diagonalizes this representation
1125 // into irrep blocks The ROWS of trans_mat are the new basis vectors in terms of
1126 // the old such that new_symrep_matrix = trans_mat * old_symrep_matrix *
1127 // trans_mat.transpose();
1129  SymGroup const &head_group) {
1131  _rep, head_group,
1132  [&](Eigen::Ref<const Eigen::MatrixXcd> const &_subspace) {
1133  return irrep_symmetrizer(_rep, head_group, _subspace,
1134  TOL); //.transpose();
1135  },
1136  false));
1137 }
1138 
1139 //*******************************************************************************************
1140 
1142  SymGroupRep const &permute_rep,
1143  const std::vector<std::set<Index>> &subsets) {
1144  SymGroupRep new_rep(SymGroupRep::NO_HOME, permute_rep.size());
1145  if (permute_rep.has_valid_master())
1146  new_rep = SymGroupRep(permute_rep.master_group());
1147 
1148  std::vector<Index> tperm(subsets.size());
1149  for (Index np = 0; np < permute_rep.size(); ++np) {
1150  Permutation const *perm_ptr(permute_rep.permutation(np));
1151  if (perm_ptr == NULL) {
1152  // This check may not make sense. Some representations may not have all
1153  // operations. If it's causing problems, comment it out
1154  err_log() << "CRITICAL ERROR: In "
1155  "subset_permutation_rep(SymGroupRep,std::vector<Index>::X2),"
1156  " permute_rep is missing at least one permutation!\n"
1157  << " Exiting...\n";
1158  assert(0);
1159  exit(1);
1160  }
1161  Permutation iperm = perm_ptr->inverse();
1162  for (Index ns = 0; ns < subsets.size(); ++ns) {
1163  std::set<Index> perm_sub;
1164 
1165  for (Index i : subsets[ns]) {
1166  perm_sub.insert(iperm[i]);
1167  }
1168 
1169  Index ns2;
1170  for (ns2 = 0; ns2 < subsets.size(); ++ns2) {
1171  if (subsets[ns] == perm_sub) {
1172  tperm[ns] = ns2;
1173  break;
1174  }
1175  }
1176 
1177  if (ns2 >= subsets.size()) {
1178  err_log()
1179  << "CRITICAL ERROR: In "
1180  "subset_permutation_rep(SymGroupRep,std::vector<Index>::X2), "
1181  "subsets are not closed under permute_rep permutations!\n"
1182  << " Exiting...\n";
1183  assert(0);
1184  exit(1);
1185  }
1186  }
1187  new_rep.set_rep(np, SymPermutation(tperm.begin(), tperm.end()));
1188  }
1189  return new_rep;
1190 }
1191 
1192 //*******************************************************************************************
1193 
1195  SymGroupRep const &permute_rep,
1196  const std::vector<SymGroupRep const *> &sum_reps) {
1197  SymGroupRep new_rep(SymGroupRep::NO_HOME, permute_rep.size());
1198  if (permute_rep.has_valid_master())
1199  new_rep = SymGroupRep(permute_rep.master_group());
1200 
1201  std::vector<Index> rep_dims(sum_reps.size(), 0);
1202  if (permute_rep.permutation(0) == NULL) {
1203  err_log() << "CRITICAL ERROR: In "
1204  "permuted_direct_sum_rep(SymGroupRep,std::vector<SymGroupRep "
1205  "const*>), permute_rep does not contain permutations!\n"
1206  << " Exiting...\n";
1207  assert(0);
1208  exit(1);
1209  }
1210 
1211  for (Index i = 0; i < sum_reps.size(); i++) {
1212  if (sum_reps[i] == NULL) {
1213  continue;
1214  }
1215  if ((permute_rep.has_valid_master() != sum_reps[i]->has_valid_master()) ||
1216  (permute_rep.has_valid_master() &&
1217  &(permute_rep.master_group()) != &(sum_reps[i]->master_group())) ||
1218  sum_reps[i]->MatrixXd(0) == NULL) {
1219  err_log() << "CRITICAL ERROR: In "
1220  "permuted_direct_sum_rep(SymGroupRep,std::vector<"
1221  "SymGroupRep const*>), found incompatible SymGroupReps!\n"
1222  << " Exiting...\n";
1223  assert(0);
1224  exit(1);
1225  }
1226  rep_dims[i] = sum_reps[i]->dim();
1227  }
1228 
1229  std::vector<Index> sum_inds(1, 0);
1230  std::partial_sum(rep_dims.begin(), rep_dims.end(),
1231  std::back_inserter(sum_inds));
1232  auto tot = std::accumulate(rep_dims.begin(), rep_dims.end(), 0);
1233  Eigen::MatrixXd sum_mat(tot, tot);
1234  Eigen::MatrixXd const *rep_mat_ptr(NULL);
1235  Permutation const *perm_ptr;
1236  for (Index g = 0; g < permute_rep.size(); g++) {
1237  if (permute_rep[g] == NULL) {
1238  continue;
1239  }
1240  sum_mat.setZero();
1241  perm_ptr = permute_rep.permutation(g);
1242  for (Index r = 0; r < perm_ptr->size(); r++) {
1243  if (rep_dims[r] == 0) continue;
1244  Index new_r = (*perm_ptr)[r];
1245  rep_mat_ptr = sum_reps[new_r]->MatrixXd(g);
1246  sum_mat.block(sum_inds[r], sum_inds[new_r], rep_dims[new_r],
1247  rep_dims[new_r]) = *rep_mat_ptr;
1248  }
1249  new_rep.set_rep(g, SymMatrixXd(sum_mat));
1250  }
1251  return new_rep;
1252 }
1253 
1254 //*******************************************************************************************
1255 
1256 SymGroupRep kron_rep(SymGroupRep const &LHS, SymGroupRep const &RHS) {
1257  if ((LHS.has_valid_master() != RHS.has_valid_master()) ||
1258  (LHS.has_valid_master() && &LHS.master_group() != &RHS.master_group())) {
1259  err_log() << "CRITICAL ERROR: In kron_rep(SymGroupRep,SymGroupRep), taking "
1260  "product of two incompatible SymGroupReps!\n"
1261  << " Exiting...\n";
1262  exit(1);
1263  }
1264  SymGroupRep new_rep(SymGroupRep::NO_HOME, LHS.size());
1265  if (LHS.has_valid_master()) new_rep = SymGroupRep(LHS.master_group());
1266 
1267  Eigen::MatrixXd tmat;
1268  for (Index i = 0; i < LHS.size(); i++) {
1269  // std::cout << "rep1 matrix:\n";
1270  // std::cout << *(rep1->MatrixXd(i)) << '\n';
1271  // std::cout << "rep2 matrix:\n";
1272  // std::cout << *(rep2->MatrixXd(i)) << '\n';
1273  assert(LHS.MatrixXd(i) && RHS.MatrixXd(i));
1274  kroneckerProduct(*(LHS.MatrixXd(i)), *(RHS.MatrixXd(i)), tmat);
1275  // std::cout << "Total matrix:\n" << tmat << '\n';
1276  new_rep.set_rep(i, SymMatrixXd(tmat));
1277  }
1278  return new_rep;
1279 }
1280 
1281 namespace SymRepTools {
1282 
1284  IrrepWedge irrep_wedge(IrrepInfo::make_dummy(axes), axes);
1285  irrep_wedge.mult.reserve(axes.cols());
1286  for (Index i = 0; i < axes.cols(); ++i) {
1287  irrep_wedge.mult.push_back(1);
1288  }
1289  return irrep_wedge;
1290 }
1291 
1293  return SubWedge({IrrepWedge::make_dummy(axes)});
1294 }
1295 
1297  std::vector<IrrepWedge> const &_iwedges) {
1298  if (_iwedges.empty()) return Eigen::MatrixXd();
1299  Eigen::MatrixXd result(_iwedges[0].axes.rows(), _iwedges[0].axes.rows());
1300  Index i = 0;
1301  for (Index w = 0; w < _iwedges.size(); ++w) {
1302  result.block(0, i, _iwedges[w].axes.rows(), _iwedges[w].axes.cols()) =
1303  _iwedges[w].axes;
1304  i += _iwedges[w].axes.cols();
1305  }
1306  if (i < result.cols()) result.conservativeResize(Eigen::NoChange, i);
1307  return result;
1308 }
1309 
1310 //*******************************************************************************************
1311 
1312 std::vector<IrrepWedge> irrep_wedges(
1313  SymGroup const &head_group, SymGroupRepID id,
1314  Eigen::Ref<const Eigen::MatrixXd> const &_subspace) {
1315  return irrep_wedges(head_group.master_group().representation(id), head_group,
1316  _subspace);
1317 }
1318 
1319 //*******************************************************************************************
1320 
1321 std::vector<IrrepWedge> irrep_wedges(
1322  SymGroupRep const &_rep, SymGroup const &head_group,
1323  Eigen::Ref<const Eigen::MatrixXd> const &_subspace) {
1324  std::vector<SymRepTools::IrrepInfo> irreps =
1325  irrep_decomposition(_rep, head_group, _subspace, false);
1326  std::vector<IrrepWedge> wedges;
1327  wedges.reserve(irreps.size());
1328  double best_proj, tproj;
1329 
1330  // std::cout << "subspace: \n" << _subspace << std::endl;
1331  // std::cout << "irrep decomposition size: " << irreps.size() << std::endl;
1332 
1333  for (SymRepTools::IrrepInfo const &irrep : irreps) {
1334  // 1D irreps directions can have positive and negative directions, but we
1335  // only want to include one. only 1D irreps can have singly-degenerate
1336  // directions and two singly-degenerate directions indicate the same vector
1337  // duplicated in positive and negative direction (because they are not
1338  // equivalent by symmetry) If irrep.directions[0] is singly degenerate
1339  // (orbits size == 1) then irrepdim is 1 and we only need one direction to
1340  // define wedge
1341  wedges.emplace_back(
1342  irrep, Eigen::MatrixXd::Zero(irrep.vector_dim(), irrep.irrep_dim()));
1343  // std::cout << "Irrep characters: \n" << irrep.characters << std::endl;
1344  // std::cout << "Irrep directions: " << irrep.directions.size() <<
1345  // std::endl;
1346  if (irrep.directions.empty()) {
1347  wedges.back() = Local::_wedge_from_pseudo_irrep(irrep, _rep, head_group);
1348  continue;
1349  }
1350 
1351  // std::cout << "Irrep direction orbit" << 0 << " : " <<
1352  // irrep.directions[0].size() << std::endl; std::cout << "Irrep direction: "
1353  // << irrep.directions[0][0].transpose() << std::endl;
1354  wedges.back().axes.col(0) = irrep.directions[0][0];
1355  wedges.back().mult.push_back(irrep.directions[0].size());
1356  for (Index i = 1; i < irrep.irrep_dim(); i++) {
1357  // std::cout << "Irrep direction orbit" << i << " : " <<
1358  // irrep.directions[i].size() << std::endl; std::cout << "Irrep direction:
1359  // " << irrep.directions[i][0].transpose() << std::endl;
1360  Index j_best = 0;
1361  best_proj =
1362  (wedges.back().axes.transpose() * irrep.directions[i][0]).sum();
1363  for (Index j = 1; j < irrep.directions[i].size(); j++) {
1364  tproj = (wedges.back().axes.transpose() * irrep.directions[i][j]).sum();
1365  if (tproj > best_proj) {
1366  best_proj = tproj;
1367  j_best = j;
1368  }
1369  }
1370 
1371  wedges.back().axes.col(i) = irrep.directions[i][j_best];
1372  wedges.back().mult.push_back(irrep.directions[i].size());
1373  }
1374  // std::cout << "New irrep wedge: \n" << wedges.back().axes.transpose() <<
1375  // std::endl;
1376  }
1377  return wedges;
1378 }
1379 
1380 //*******************************************************************************************
1381 
1382 std::vector<SubWedge> symrep_subwedges(SymGroup const &head_group,
1383  SymGroupRepID id) {
1384  return symrep_subwedges(head_group.master_group().representation(id),
1385  head_group);
1386 }
1387 
1388 //*******************************************************************************************
1389 
1390 std::vector<SubWedge> symrep_subwedges(
1391  SymGroup const &head_group, SymGroupRepID id,
1392  Eigen::Ref<const Eigen::MatrixXd> const &_subspace) {
1393  return symrep_subwedges(head_group.master_group().representation(id),
1394  head_group, _subspace);
1395 }
1396 
1397 //*******************************************************************************************
1398 
1399 std::vector<SubWedge> symrep_subwedges(SymGroupRep const &_rep,
1400  SymGroup const &head_group) {
1401  return symrep_subwedges(_rep, head_group,
1402  Eigen::MatrixXd::Identity(_rep.dim(), _rep.dim()));
1403 }
1404 //*******************************************************************************************
1405 
1406 std::vector<SubWedge> symrep_subwedges(
1407  SymGroupRep const &_rep, SymGroup const &head_group,
1408  Eigen::Ref<const Eigen::MatrixXd> const &_subspace) {
1409  auto irrep_wedge_compare = [](const IrrepWedge &a,
1410  const IrrepWedge &b) -> bool {
1411  return Eigen::almost_equal(a.axes, b.axes);
1412  };
1413 
1414  auto tot_wedge_compare = [irrep_wedge_compare](
1415  const std::vector<IrrepWedge> &a,
1416  const std::vector<IrrepWedge> &b) -> bool {
1417  if (a.size() != b.size()) return false;
1418  for (auto ita = a.begin(), itb = b.begin(); ita != a.end(); ++ita, ++itb)
1419  if (!irrep_wedge_compare(*ita, *itb)) return false;
1420  // std::cout << "Equal: \n" << SubWedge(a).trans_mat() << ",\n" <<
1421  // SubWedge(b).trans_mat() << "\n\n";
1422  return true;
1423  };
1424 
1425  if (!_rep.MatrixXd(0))
1426  throw std::runtime_error(
1427  "In symrep_subwedges, SymGroupRep does not describe matrix "
1428  "representation");
1429 
1430  std::vector<IrrepWedge> init_wedges =
1431  irrep_wedges(_rep, head_group, _subspace);
1432 
1433  std::vector<SubWedge> result;
1434 
1435  // irrep_wedge_orbits[w] is orbit of wedges[w]
1436  multivector<IrrepWedge>::X<2> irrep_wedge_orbits;
1437  irrep_wedge_orbits.reserve(init_wedges.size());
1438  // max_equiv[w] is irrep_wedge_orbits[w].size()-1
1439  std::vector<Index> max_equiv;
1440  max_equiv.reserve(init_wedges.size());
1441  // std::cout << "irreducible wedges for group of order " << head_group.size()
1442  // << std::endl;
1443  Index imax = 0;
1444  multivector<Index>::X<2> subgroups;
1445  for (IrrepWedge const &wedge : init_wedges) {
1446  // std::cout << "Working wedge with axes: \n" << wedge.axes.transpose() <<
1447  // std::endl;
1448  irrep_wedge_orbits.push_back({wedge});
1449 
1450  // Start getting orbit of wedges[w]
1451  subgroups.push_back({});
1452  for (SymOp const &op : head_group) {
1453  IrrepWedge test_wedge{wedge};
1454  test_wedge.axes = (*(_rep[op.index()]->MatrixXd())) * wedge.axes;
1455  Index o = 0;
1456  for (; o < irrep_wedge_orbits.back().size(); ++o) {
1457  if (irrep_wedge_compare(irrep_wedge_orbits.back()[o], test_wedge)) {
1458  if (o == 0) {
1459  subgroups.back().push_back(op.index());
1460  }
1461  break;
1462  }
1463  }
1464  if (o < irrep_wedge_orbits.back().size()) continue;
1465  irrep_wedge_orbits.back().push_back(test_wedge);
1466  }
1467  // std::cout << "wedge mult: " << irrep_wedge_orbits.back().size();
1468  // std::cout << "; subgroups[" << subgroups.size() << "]: " <<
1469  // subgroups.back() << std::endl; std::cout << "N equiv wedges found: " <<
1470  // irrep_wedge_orbits.back().size() << std::endl;
1471  max_equiv.push_back(irrep_wedge_orbits.back().size() - 1);
1472  if (max_equiv.back() > max_equiv[imax]) imax = max_equiv.size() - 1;
1473  }
1474  max_equiv[imax] = 0;
1475 
1476  // Counter over combinations of equivalent wedges
1477  Counter<std::vector<Index>> wcount(std::vector<Index>(init_wedges.size(), 0),
1478  max_equiv,
1479  std::vector<Index>(init_wedges.size(), 1));
1480 
1481  // std::cout << "max_equiv: " << max_equiv << std::endl;
1482  // std::cout << "init wcount: " << wcount() << std::endl;
1483  multivector<IrrepWedge>::X<3> tot_wedge_orbits;
1484  // std::cout << "Starting slow bit!\n";
1485  for (; wcount; ++wcount) {
1486  std::vector<IrrepWedge> twedge = init_wedges;
1487  for (Index i = 0; i < init_wedges.size(); i++)
1488  twedge[i].axes = irrep_wedge_orbits[i][wcount[i]].axes;
1489 
1490  if (contains_if(
1491  tot_wedge_orbits,
1492  [tot_wedge_compare, &twedge](
1493  const multivector<IrrepWedge>::X<2> &wedge_orbit) -> bool {
1494  return contains(wedge_orbit, twedge, tot_wedge_compare);
1495  }))
1496  continue;
1497 
1498  tot_wedge_orbits.push_back({twedge});
1499  result.emplace_back(twedge);
1500  for (Index p : subgroups[imax]) {
1501  for (Index i = 0; i < twedge.size(); i++)
1502  twedge[i].axes =
1503  (*(_rep[p]->MatrixXd())) * result.back().irrep_wedges()[i].axes;
1504  if (!contains(tot_wedge_orbits.back(), twedge, tot_wedge_compare)) {
1505  tot_wedge_orbits.back().push_back(twedge);
1506  }
1507  }
1508  }
1509  // std::cout << "Num subwedges: " << result.size() << std::endl;
1510  return result;
1511 }
1512 
1513 } // namespace SymRepTools
1514 } // namespace CASM
A Counter allows looping over many incrementing variables in one loop.
Definition: Counter.hh:109
SymGroupRep const & representation(SymGroupRepID i) const
Const access of alternate Representations of a SymGroup.
Definition: SymGroup.cc:717
An Orbit of Element.
Definition: Orbit.hh:43
Permutation inverse() const
Construct permutation that undoes the permutation performed by 'this'.
Definition: Permutation.cc:111
Index size() const
Definition: Permutation.hh:60
SymGroup is a collection of symmetry operations that satisfy the group property The symmetry operatio...
Definition: SymGroup.hh:42
const std::vector< std::set< std::set< Index > > > & subgroups() const
Definition: SymGroup.cc:1695
std::vector< std::set< std::set< Index > > > small_subgroups() const
Definition: SymGroup.hh:206
Index ind_prod(Index i, Index j) const
Get index of operation that is result of multiplication of at(i)*at(j)
Definition: SymGroup.cc:1550
const MasterSymGroup & master_group() const
Definition: SymGroup.hh:73
SymGroupRep is an alternative representation of a SymGroup for something other than real space....
Definition: SymGroupRep.hh:31
const MasterSymGroup & master_group() const
Reference to MasterSymGroup for which this SymGroupRep is a group representation If a MasterSymGroup ...
Definition: SymGroupRep.hh:76
void set_rep(Index op_index, const SymOpRepresentation &new_rep)
Sets the representation of operation at entry 'op_index' of this group representation Throws if this ...
Definition: SymGroupRep.cc:114
bool has_valid_master() const
Returns true if this SymGroupRep has valid pointer to a MasterSymGroup.
Definition: SymGroupRep.hh:86
Eigen::MatrixXd const * MatrixXd(Index i) const
pointer to MatrixXd corresponding to SymOpRepresentation at entry 'i' of this SymGroupRep Returns nul...
Definition: SymGroupRep.cc:144
Permutation const * permutation(Index i) const
pointer to Permutation corresponding to SymOpRepresentation at entry 'i' of this SymGroupRep Returns ...
Definition: SymGroupRep.cc:157
Index dim() const
Matrix dimension of representation.
Definition: SymGroupRep.hh:82
Type-safe ID object for communicating and accessing Symmetry representation info.
Generalized symmetry matrix representation for arbitrary dimension Can be used to describe applicatio...
Definition: SymMatrixXd.hh:26
SymOp is the Coordinate representation of a symmetry operation it keeps fraction (FRAC) and Cartesian...
Definition: SymOp.hh:28
SymPermutation describes how a symmetry operation permutes a list of 'things' For example,...
SubWedge is a vector of IrrepWedge, one from each irreducible subspace Together, the IrrepWedges that...
Definition: SymRepTools.hh:108
static Eigen::MatrixXd _subwedge_to_trans_mat(std::vector< IrrepWedge > const &_iwedges)
static SubWedge make_dummy(Eigen::MatrixXd const &axes)
SubWedge(std::vector< IrrepWedge > const &_iwedges)
Definition: SymRepTools.hh:110
std::string to_string(ENUM val)
Return string representation of enum class.
Definition: io_traits.hh:172
Eigen::MatrixXd pretty(const Eigen::MatrixXd &M, double tol)
Round entries that are within tol of being integer to that integer value.
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
SymRepTools::IrrepInfo _subspace_to_full_space(SymRepTools::IrrepInfo const &irrep, Eigen::MatrixBase< T > const &subspace)
Definition: SymRepTools.cc:368
static SymRepTools::IrrepWedge _wedge_from_pseudo_irrep(SymRepTools::IrrepInfo const &irrep, SymGroupRep const &_rep, SymGroup const &head_group)
Definition: SymRepTools.cc:287
bool _rep_check(SymGroupRep const &_rep, SymGroup const &head_group, bool verbose)
Definition: SymRepTools.cc:327
static Eigen::MatrixXd _block_shape_matrix(SymGroupRep const &_rep, SymGroup const &head_group)
Matrix such that result(i,j) is sum of squares over 'p' of op_rep[p].matrix(i,j). 'p' only spans oper...
Definition: SymRepTools.cc:146
static Eigen::MatrixXcd _irrep_symmetrizer_from_directions(multivector< Eigen::VectorXcd >::X< 2 > const &special_directions, Eigen::Ref< const Eigen::MatrixXcd > const &_subspace, double vec_compare_tol)
Definition: SymRepTools.cc:166
typename _RealType< T >::Type _Real
Definition: SymRepTools.cc:113
_Real< Derived > _real(Eigen::MatrixBase< Derived > const &mat)
Definition: SymRepTools.cc:126
IdentitySymRepBuilder Identity()
Eigen::MatrixXcd prettyc(const Eigen::MatrixXcd &M)
Round entries that are within tol of being integer to that integer value.
std::pair< Eigen::MatrixXcd, multivector< Eigen::VectorXcd >::X< 2 > > Symmetrizer
Definition: SymRepTools.hh:21
std::vector< IrrepWedge > irrep_wedges(SymGroup const &head_group, SymGroupRepID id, Eigen::Ref< const Eigen::MatrixXd > const &_subspace)
std::function< Symmetrizer(Eigen::Ref< const Eigen::MatrixXcd > const &f_subspace)> SymmetrizerFunction
Definition: SymRepTools.hh:26
std::vector< SubWedge > symrep_subwedges(SymGroup const &_group, SymGroupRepID id)
Main CASM namespace.
Definition: APICommand.hh:8
bool almost_equal(ClusterInvariants const &A, ClusterInvariants const &B, double tol)
Check if ClusterInvariants are equal.
SymGroupRep permuted_direct_sum_rep(const SymGroupRep &permute_rep, const std::vector< SymGroupRep const * > &sum_reps)
VectorSpaceSymReport vector_space_sym_report(DoFSpace const &dof_space, SupercellSymInfo const &sym_info, std::vector< PermuteIterator > const &group, bool calc_wedges=false)
Make VectorSpaceSymReport.
Definition: DoFSpace.cc:560
Eigen::MatrixXd MatrixXd
OrbitOutputIterator make_orbits(OrbitBranchSpecsIterator begin, OrbitBranchSpecsIterator end, const std::vector< IntegralClusterOrbitGenerator > &custom_generators, OrbitOutputIterator result, std::ostream &status)
Generate orbits of IntegralCluster using OrbitBranchSpecs.
bool is_irrep(SymGroupRep const &_rep, const SymGroup &head_group)
Returns true if _rep is irreducible wrt head_group (does not use character table information)
Definition: SymRepTools.cc:708
Eigen::MatrixXd full_trans_mat(std::vector< SymRepTools::IrrepInfo > const &irreps)
Assumes that irreps are real, and concatenates their individual trans_mats to form larger trans_mat.
Definition: SymRepTools.cc:447
SymRepTools::Symmetrizer irrep_symmetrizer(SymGroupRep const &_rep, const SymGroup &head_group, double vec_compare_tol)
Definition: SymRepTools.cc:420
std::vector< SymRepTools::IrrepInfo > irrep_decomposition(SymGroupRep const &_rep, SymGroup const &head_group, bool allow_complex)
Finds irreducible subspaces that comprise an underlying subspace It does not rely on the character ta...
Definition: SymRepTools.cc:761
SymGroupRep subset_permutation_rep(const SymGroupRep &permute_rep, const std::vector< std::set< Index >> &subsets)
void swap(ConfigDoF &A, ConfigDoF &B)
Definition: ConfigDoF.cc:260
SymGroupRep coord_transformed_copy(SymGroupRep const &_rep, const Eigen::MatrixXd &trans_mat)
Make a copy of representation on vector space 'V' that is transformed into a representation on vector...
Definition: SymGroupRep.cc:175
std::vector< std::vector< Eigen::MatrixXd > > special_subspaces(SymGroupRep const &_rep, const SymGroup &head_group)
finds high-symmetry subspaces within vector space supporting _rep, wrt symmetry of head_group High-sy...
Definition: SymRepTools.cc:610
const double TOL
Definition: definitions.hh:30
SymGroupRep kron_rep(const SymGroupRep &LHS, const SymGroupRep &RHS)
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
multivector< Eigen::VectorXd >::X< 3 > special_total_directions(SymGroupRep const &_rep, const SymGroup &head_group)
finds high-symmetry directions within vector space supporting _rep, wrt symmetry of head_group
Definition: SymRepTools.cc:467
ReturnArray< Index > partition_distinct_values(const Eigen::MatrixBase< Derived > &value, double tol=TOL)
bool contains(const Container &container, const T &value)
Equivalent to container.end() != std::find(container.begin(), container.end(), value)
Definition: algorithm.hh:83
Eigen::MatrixXd irrep_trans_mat(SymGroupRep const &_rep, const SymGroup &head_group)
Finds the transformation matrix that block-diagonalizes this representation of head_group into irrep ...
multivector< Eigen::VectorXcd >::X< 2 > special_irrep_directions(SymGroupRep const &_rep, SymGroup const &head_group, double vec_compare_tol)
Assuming that _rep is an irrep of head_group, find high-symmetry directions throws if _rep is not an ...
Definition: SymRepTools.cc:488
Eigen::MatrixXd::Index EigenIndex
Definition: eigen.hh:7
bool contains_if(const Container &container, UnaryPredicate p)
Equivalent to container.end() != std::find_if(container.begin(), container.end(), p)
Definition: algorithm.hh:98
Index num_blocks(SymGroupRep const &_rep, const SymGroup &head_group)
counts number of nonzero blocks in matrix representation of head_group as specified by _rep Reveals n...
Definition: SymRepTools.cc:402
INDEX_TYPE Index
For long integer indexing:
Definition: definitions.hh:39
Eigen::VectorXd VectorXd
Log & err_log()
Definition: Log.hh:426
bool almost_equal(const Eigen::MatrixBase< Derived1 > &val1, const Eigen::MatrixBase< Derived2 > &val2, double tol=CASM::TOL)
Derived::PlainObject representation_prepare_impl(Eigen::MatrixBase< Derived > const &obj, double _tol)
Prepare an element for comparison.
Definition: stream_io.hh:24
Eigen::Matrix< double, RowsAtCompileTime, ColsAtCompileTime > Type
Definition: SymRepTools.cc:122
bool operator()(SymRepTools::IrrepInfo const &irrep_a, SymRepTools::IrrepInfo const &irrep_b) const
Definition: SymRepTools.cc:59
Eigen::VectorXcd characters
Definition: SymRepTools.hh:67
IrrepInfo(Eigen::MatrixXcd _trans_mat, Eigen::VectorXcd _characters)
Construct an IrrepInfo with transformation matrix and vector of irreducible characters.
Definition: SymRepTools.cc:391
std::vector< std::vector< Eigen::VectorXd > > directions
Definition: SymRepTools.hh:73
bool complex
true if any character has non-zero imaginary component, false otherwise
Definition: SymRepTools.hh:50
Eigen::MatrixXcd trans_mat
Definition: SymRepTools.hh:63
static IrrepInfo make_dummy(Eigen::MatrixBase< Derived > const &_trans_mat)
Named constructor to initialize an IrrepInfo with a user-specified transformtion matrix and character...
Definition: SymRepTools.hh:33
Index irrep_dim() const
Dimension of irreducible vector space (less than or equal to vector_dim())
Definition: SymRepTools.hh:44
An irreducible wedge in an irreducible vector space.
Definition: SymRepTools.hh:77
static IrrepWedge make_dummy(const Eigen::MatrixXd &axes)
std::vector< Index > mult
Symmetric multiplicity of each column of 'axes'.
Definition: SymRepTools.hh:90
Summary of data associated with the action of a symmetry group on a vector space.
Definition: SymRepTools.hh:185
std::vector< Eigen::MatrixXd > symgroup_rep
Matrix representation for each operation in the group – defines action of group on vector space.
Definition: SymRepTools.hh:188
std::vector< std::string > axis_glossary
Names given to individual axes in initial (un-adapted) vector space, corresponding to rows of symmetr...
Definition: SymRepTools.hh:204
Eigen::MatrixXd symmetry_adapted_dof_subspace
Symmetry-oriented subspace of the vector space (columns are the basis vectors)
Definition: SymRepTools.hh:200
std::vector< SymRepTools::SubWedge > irreducible_wedge
Irreducible wedge in the vector space encoded as a vector of symmetrically distinct SubWedges.
Definition: SymRepTools.hh:196
std::vector< SymRepTools::IrrepInfo > irreps
A list of all irreducible representation that make up the full representation.
Definition: SymRepTools.hh:192
Shortcut for multidimensional vector (std::vector< std::vector< ...)
Definition: multivector.hh:26
typename multivector_impl::multivector_tmp< T, N >::type X
Definition: multivector.hh:28