CASM
AClustersApproachtoStatisticalMechanics
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules
SupercellEnumerator.cc
Go to the documentation of this file.
2 
3 #include "casm/external/boost.hh"
4 #include "casm/external/Eigen/Dense"
5 
7 #include "casm/clex/PrimClex.hh"
8 
9 namespace CASM {
10 
28  Eigen::Matrix3i make_unit_cell(PrimClex &primclex, const jsonParser &input) {
29 
30  // read generating matrix (unit cell)
31  Eigen::Matrix3i generating_matrix;
32  if(input.is_null() || !input.contains("unit_cell")) {
33  generating_matrix = Eigen::Matrix3i::Identity();
34  }
35  else if(input["unit_cell"].is_array()) {
36  from_json(generating_matrix, input["unit_cell"]);
37  }
38  else if(input["unit_cell"].is_string()) {
39  generating_matrix = primclex.get_supercell(input["unit_cell"].get<std::string>()).get_transf_mat();
40  }
41  else {
42  throw std::invalid_argument(
43  "Error reading unit cell from JSON input: 'unit_cell' must be a 3x3 integer matrix or supercell name");
44  }
45  return generating_matrix;
46  }
47 
69 
70  // read volume range
73 
74  if(!input.contains("min")) {
75  min_vol = 1;
76  }
77  else {
78  from_json(min_vol, input["min"]);
79  }
80  if(!(min_vol > 0)) {
81  throw std::invalid_argument(
82  "Error in ScelEnumProps JSON input: 'min_volume' must be >0");
83  }
84 
85  // read "max" scel size, or by default use largest existing supercell
86  ScelEnumProps::size_type max_scel_size = 1;
87  for(const auto &scel : primclex.get_supercell_list()) {
88  if(scel.volume() > max_scel_size) {
89  max_scel_size = scel.volume();
90  }
91  }
92 
93  input.get_else(max_vol, "max", max_scel_size);
94  if(!(max_vol >= min_vol)) {
95  throw std::invalid_argument(
96  "Error in ScelEnumProps JSON input: 'max' must be greater than or equal to 'min'");
97  }
98 
99  // read generating matrix (unit cell)
100  Eigen::Matrix3i generating_matrix = make_unit_cell(primclex, input);
101 
102  std::string dirs;
103  input.get_else<std::string>(dirs, "dirs", "abc");
104 
105  return ScelEnumProps(min_vol, max_vol + 1, dirs, generating_matrix);
106  }
107 
113  HermiteCounter::HermiteCounter(int init_start_determinant, int init_dim):
114  m_pos(0) {
115  m_diagonal = Eigen::VectorXi::Ones(init_dim);
116  m_diagonal(0) = init_start_determinant;
117 
119  }
120 
122  return m_pos;
123  }
124 
125  Eigen::VectorXi HermiteCounter::diagonal() const {
126  return m_diagonal;
127  }
128 
129  Eigen::MatrixXi HermiteCounter::current() const {
131  }
132 
134  return m_diagonal.size();
135  }
136 
137  Eigen::MatrixXi HermiteCounter::operator()() const {
138  return current();
139  }
140 
142  return m_diagonal.prod();
143  }
144 
147  return;
148  }
149 
152  return;
153  }
154 
157  return m_pos;
158  }
159 
161  //Vary elements above diagonal
162  m_upper_tri++;
163 
164  //Nothing to permute for identity
165  if(determinant() == 1) {
167  }
168 
169  //Still other matrices available for current diagonal
170  else if(m_upper_tri.valid()) {
171  return *this;
172  }
173 
174  //Find next diagonal, reset elements above diagonal
175  else if(_increment_diagonal() < dim()) {
177  }
178 
179  //Reset diagonal with next determinant value, reset elements above diagonal
180  else {
182  }
183 
184  return *this;
185  }
186 
188  if(new_det < 1) {
189  throw std::runtime_error("Determinants of Hermite normal form matrices must be greater than 0");
190  }
191 
192  m_diagonal = Eigen::VectorXi::Ones(dim());
193  m_diagonal(0) = new_det;
194 
196  m_pos = 0;
197 
198  return;
199  }
200 
201  namespace HermiteCounter_impl {
203  //If you fall into these traps you're using this wrong
204  assert(attempt <= diag(position));
205  assert(position < diag.size() - 1);
206  assert(diag(position + 1) == 1);
207  assert(diag(position) > 1);
208  assert(attempt >= 2);
209 
210 
211  //Use the given attempt as a starting guess for factorizing the element at position,
212  //but if that doesn't work, keep increasing the value of the attempt until it does.
213  while(diag(position) % attempt != 0) {
214  attempt++;
215  }
216 
217  diag(position) = diag(position) / attempt;
218  diag(position + 1) = attempt;
219 
220  position++;
221 
222  return position;
223  }
224 
226  HermiteCounter::value_type attempt = 2;
227 
228  //If you reached the end of the diagonal, backtrack to nearest non-1 value
229  //and perform the next spill
230  if(position == diag.size() - 1) {
231  do {
232  position--;
233  }
234  while(position >= 0 && diag(position) == 1);
235 
236  //You're at the last spill already. Your diagonal is 1 1 ... 1 1 det. No more increments are possible.
237  if(position < 0) {
238  return diag.size();
239  }
240 
241  //Flush everything to the right of position with ones, and reset the value at position
242  //with the next attempt for factorization
243  attempt = diag(diag.size() - 1) + 1;
244  diag(position) = diag(position) * diag(diag.size() - 1);
245  diag(diag.size() - 1) = 1;
246  }
247 
248  return _spill_factor(diag, position, attempt);
249  }
250 
252  assert(init_dim > 0);
253 
254  HermiteCounter::Index tritotal = 0;
255 
256  for(int i = 0; i < init_dim; i++) {
257  tritotal += i;
258  }
259 
260  return tritotal;
261  }
262 
263  EigenVectorXiCounter _upper_tri_counter(const Eigen::VectorXi &current_diag) {
264  //Find out how many slots you need for all the elements above the diagonal
265  HermiteCounter::Index uppersize = upper_size(current_diag.size());
266 
267  //Start the counter with zero everywhere
268  Eigen::VectorXi begincount(Eigen::VectorXi::Zero(uppersize));
269 
270  //Increments always go one at a time
271  Eigen::VectorXi stepcount(Eigen::VectorXi::Ones(uppersize));
272 
273  //The m_upper_tri is unrolled left to right, top to bottom
274  Eigen::VectorXi endcount(Eigen::VectorXi::Zero(uppersize));
275  HermiteCounter::Index slot = 0;
276  for(HermiteCounter::Index i = 0; i < current_diag.size(); i++) {
277  for(HermiteCounter::Index j = i + 1; j < current_diag.size(); j++) {
278  //The counter value should always be smaller than the diagonal
279  endcount(slot) = current_diag(i) - 1;
280  slot++;
281  }
282  }
283 
284  return EigenVectorXiCounter(begincount, endcount, stepcount);
285  }
286 
287  Eigen::MatrixXi _zip_matrix(const Eigen::VectorXi &current_diag, const Eigen::VectorXi &current_upper_tri) {
288  assert(current_upper_tri.size() == upper_size(current_diag.size()));
289 
290  Eigen::MatrixXi hmat(current_diag.asDiagonal());
291 
292  Index slot = 0;
293  for(Index i = 0; i < current_diag.size() - 1; i++) {
294  for(Index j = i + 1; j < current_diag.size(); j++) {
295  hmat(i, j) = current_upper_tri(slot);
296  slot++;
297  }
298  }
299 
300  return hmat;
301  }
302 
325  Eigen::MatrixXi _expand_dims_old(const Eigen::MatrixXi &hermit_mat, const Eigen::VectorXi &active_dims) {
326  assert(hermit_mat.rows() == active_dims.sum() && hermit_mat.cols() == active_dims.sum());
327  assert(active_dims.maxCoeff() == 1 && active_dims.minCoeff() == 0);
328 
329  Eigen::MatrixXi expanded(Eigen::MatrixXi::Identity(active_dims.size(), active_dims.size()));
330 
331  HermiteCounter::Index i, j, si, sj;
332  si = -1;
333  sj = -1;
334 
335  for(i = 0; i < expanded.rows(); i++) {
336  if(active_dims(i) == 0) {
337  continue;
338  }
339 
340  else {
341  si++;
342  }
343 
344 
345  for(j = 0; j < expanded.cols(); j++) {
346  if(active_dims(j) == 0) {
347  continue;
348  }
349 
350  else {
351  sj++;
352  }
353 
354  expanded(i, j) = hermit_mat(si, sj);
355  }
356 
357  j = 0;
358  sj = -1;
359  }
360 
361  return expanded;
362  }
363 
364  /*
365  * This is a generalized way to insert new dimensions into a HNF matrix.
366  * If there is a n x n HNF matrix H, it will be expanded to a new non-HNF
367  * m x m matrix T through a m x m generating matrix G.
368  *
369  * The first n columns of G will be multiplied with the values of H, while
370  * the last m-n columns of G will remain unaffected. For example, if you
371  * are counting over a 2x2 H matrix and currently have
372  *
373  * 2 1
374  * 0 3
375  *
376  * Then you can specify that you want a 3x3 matrix T so that the values of
377  * H work on your lattice vectors a and c, but not b with a G matrix
378  *
379  * 1 0 0
380  * 0 0 1
381  * 0 1 0
382  *
383  * The resulting expanded matrix T is now
384  *
385  * 2 1 0
386  * 0 0 1
387  * 0 3 0
388  *
389  * This is achieved by converting H to a block matrix of dimensions m x m,
390  * which is an identity matrix with the upper left block equal to H. For
391  * the example above B would be
392  *
393  * 2 1 0
394  * 0 3 0
395  * 0 0 1
396  *
397  * This way T=G*B
398  *
399  * You may specify arbitrary combinations of vectors in the columns of G that
400  * H will work on.
401  *
402  * Note that the resulting matrix will probably *NOT* retain it's Hermite normal
403  * form. For use in the SupercellEnumerator class, the order within the first n
404  * vectors and the order within the last m-n vectors will not affect your enumerations.
405  */
406 
407  Eigen::MatrixXi _expand_dims(const Eigen::MatrixXi &H, const Eigen::MatrixXi &G) {
408  assert(H.rows() == H.cols());
409  assert(G.rows() == G.cols());
410  assert(G.rows() >= H.rows());
411 
412  Index n = H.rows();
413  Index m = G.rows();
414 
415  //First convert H into a block matrix with dimensions m x m, the H block is on the upper left
416  Eigen::MatrixXi B(m, m);
417  Eigen::MatrixXi I_block(Eigen::MatrixXi::Identity(m - n, m - n));
418  Eigen::MatrixXi Z_block(Eigen::MatrixXi::Zero(n, m - n));
419  B << H, Z_block, Z_block.transpose(), I_block;
420 
421  return G * B;
422  }
423 
424 
443  Eigen::VectorXi _canonical_unroll(const Eigen::MatrixXi &hermit_mat) {
444  assert(hermit_mat.rows() == hermit_mat.cols());
445 
446  int dims = hermit_mat.rows();
447  int unrolldim = 0;
448 
449  for(int i = dims; i > 0; i--) {
450  unrolldim += i;
451  }
452 
453  Eigen::VectorXi unrolled_herm(unrolldim);
454 
455  Index lasti, lastj;
456  lasti = lastj = -1;
457 
458  enum STEP {DOWN, UP, LEFT};
459  STEP curr_step = DOWN;
460 
461  Index unroll_ind = 0;
462 
463  for(Index i = 0; i < dims; i++) {
464  for(Index j = 0; j < dims - i; j++) {
465  switch(curr_step) {
466  case DOWN:
467  lasti = lasti + 1;
468  lastj = lastj + 1;
469  break;
470  case UP:
471  lasti = lasti - 1;
472  break;
473  case LEFT:
474  lastj = lastj - 1;
475  break;
476  }
477 
478  unrolled_herm(unroll_ind) = hermit_mat(lasti, lastj);
479  unroll_ind++;
480  }
481 
482  switch(curr_step) {
483  case DOWN:
484  curr_step = UP;
485  break;
486  case UP:
487  curr_step = LEFT;
488  break;
489  case LEFT:
490  curr_step = DOWN;
491  break;
492  }
493  }
494 
495  return unrolled_herm;
496  }
497 
518  bool _canonical_compare(const Eigen::MatrixXi &H0, const Eigen::MatrixXi &H1) {
519  const Eigen::VectorXi unrolled_H0 = _canonical_unroll(H0);
520  const Eigen::VectorXi unrolled_H1 = _canonical_unroll(H1);
521 
522  assert(unrolled_H0.size() == unrolled_H1.size());
523 
524  for(Index i = 0; i < unrolled_H0.size(); i++) {
525  if(unrolled_H0(i) > unrolled_H1(i)) {
526  return false;
527  }
528 
529  else if(unrolled_H0(i) < unrolled_H1(i)) {
530  return true;
531  }
532  }
533 
534  //Both are equal if you get this far
535  return false;
536  }
537  }
538 
539 
540  //******************************************************************************************************************//
541 
542 
543  template<>
545  const ScelEnumProps &enum_props,
546  double tol) :
547  m_unit(unit),
548  m_lat(unit),
549  m_begin_volume(enum_props.begin_volume()),
550  m_end_volume(enum_props.end_volume()),
551  m_gen_mat(enum_props.generating_matrix()),
552  m_dims(enum_props.dims()) {
553 
554  if(m_gen_mat.determinant() < 1) {
555  throw std::runtime_error("The transformation matrix to expand into a 3x3 matrix must have a positive determinant!");
556  }
557 
559 
560  }
561 
562  template<>
564  const SymGroup &point_grp,
565  const ScelEnumProps &enum_props) :
566  m_unit(unit),
567  m_lat(unit),
568  m_point_group(point_grp),
569  m_begin_volume(enum_props.begin_volume()),
570  m_end_volume(enum_props.end_volume()),
571  m_gen_mat(enum_props.generating_matrix()),
572  m_dims(enum_props.dims()) {
573 
574  if(m_gen_mat.determinant() < 1) {
575  throw std::runtime_error("The transformation matrix to expand into a 3x3 matrix must have a positive determinant!");
576  }
577  }
578 
579 
580 
581  template<>
583  const Lattice &unit,
584  const Eigen::Matrix3i &T,
585  const SymGroup &point_grp,
586  Index volume,
587  bool fix_shape) {
588 
589  if(fix_shape) {
590  auto init_vol = T.determinant();
591  Index m = 1;
592  while(m * m * m * init_vol < volume) {
593  ++m;
594  }
595 
596  return m * Eigen::Matrix3i::Identity();
597  }
598  else {
599  auto init_vol = T.determinant();
600  Index m = 1;
601  while(m * init_vol < volume) {
602  ++m;
603  }
604 
605  auto compactness = [](const Lattice & lat) {
606  Eigen::Matrix3d L = lat.lat_column_mat();
607  return (L.transpose() * L).trace();
608  };
609 
610  auto compare = [&](const Lattice & A, const Lattice & B) {
611  return compactness(A) < compactness(B);
612  };
613 
614  SupercellEnumerator<Lattice> scel(unit, point_grp, ScelEnumProps(m, m + 1));
615  auto best_it = std::min_element(scel.begin(), scel.end(), compare);
616  return best_it.matrix();
617  }
618 
619  }
620 
621 
632  // that has a lattice ref_lat (not to be confused with the point group of ref_lat itself).
653  Eigen::Matrix3i canonical_hnf(const Eigen::Matrix3i &T, const SymGroup &effective_pg, const Lattice &ref_lattice) {
654  Eigen::Matrix3d lat = ref_lattice.lat_column_mat();
655 
656  //get T in hermite normal form
657  //H is the canonical form of the initial T matrix
658  const Eigen::Matrix3i H = hermite_normal_form(T).first;
659 
660  //H_best will be the most canonical version and is returned
661  Eigen::Matrix3i H_best;
662  H_best = H;
663 
664  for(Index i = 0; i < effective_pg.size(); i++) {
665  Eigen::Matrix3i transformed = iround(lat.inverse() * effective_pg[i].matrix() * lat) * H;
666  Eigen::Matrix3i H_transformed = hermite_normal_form(transformed).first;
667 
668  //If you fall in here then transformed was greater than H
669  if(HermiteCounter_impl::_canonical_compare(H_best, H_transformed) == 1) {
670  H_best = H_transformed;
671  }
672  }
673 
674  return H_best;
675  //return std::make_pair<Eigen::Matrix3i, Eigen::MatrixXd>(H_best, effective_pg[i_canon].matrix());
676  }
677 
678 }
Index _increment_diagonal()
Go to the next values of diagonal elements that keep the same determinant.
HermiteCounter::Index _spill_factor(Eigen::VectorXi &diag, HermiteCounter::Index position, HermiteCounter::value_type attempt)
Find the next factor of the specified position and share with next element. Use attempt as starting p...
value_type determinant() const
Get the current determinant.
const Container & current() const
Definition: BaseCounter.hh:195
boost::container::stable_vector< Supercell > & get_supercell_list()
Access entire supercell_list.
Definition: PrimClex.cc:299
Lattice m_lat
The lattice of the unit cell.
Eigen::MatrixXi _expand_dims(const Eigen::MatrixXi &H, const Eigen::MatrixXi &G)
Expand a n x n Hermite normal matrix (H) into a m x m one through a m x m generating matrix (G) (e...
void from_json(ClexDescription &desc, const jsonParser &json)
A Counter allows looping over many incrementing variables in one loop.
Definition: Counter.hh:71
Index size() const
Definition: Array.hh:145
Eigen::VectorXi diagonal() const
void generate_point_group(SymGroup &point_group, double pg_tol=TOL) const
Populate.
Definition: Lattice.cc:304
bool compare(const T &A, const T &B, double tol)
Floating point comparison with tol, return A < B.
Definition: CASM_math.hh:89
Data structure for holding supercell enumeration properties.
Eigen::Matrix3i canonical_hnf(const Eigen::Matrix3i &T, const SymGroup &effective_pg, const Lattice &ref_lattice)
Return canonical hermite normal form of the supercell matrix, and op used to find it...
PrimClex * primclex
Definition: settings.cc:101
Index m_pos
Keeps track of the current diagonal element that needs to be factored.
bool _canonical_compare(const Eigen::MatrixXi &H0, const Eigen::MatrixXi &H1)
Compare two integer matrices and see which one is lexicographically greatest. Returns true if H0
Eigen::MatrixXi _zip_matrix(const Eigen::VectorXi &current_diag, const Eigen::VectorXi &current_upper_tri)
Assemble a matrix diagonal and unrolled upper triangle values into a matrix.
const_iterator end() const
A const iterator to the past-the-last volume.
Eigen::MatrixXi _expand_dims_old(const Eigen::MatrixXi &hermit_mat, const Eigen::VectorXi &active_dims)
Expand a n x n Hermite normal matrix into a m x m one (e.g. for 2D supercells)
A fake container of supercell matrices.
Definition: Enumerator.hh:407
void jump_to_determinant(value_type new_det)
Reset the diagonal to the specified determinant and set the other elements to zero.
Main CASM namespace.
Definition: complete.cpp:8
Eigen::Matrix3i enforce_min_volume< Lattice >(const Lattice &unit, const Eigen::Matrix3i &T, const SymGroup &point_grp, Index volume, bool fix_shape)
const Eigen::Matrix3d & lat_column_mat() const
3x3 matrix with lattice vectors as its columne
Definition: Lattice.hh:104
Index dim() const
Get the dimensions of *this.
Eigen::MatrixXi operator()() const
Get the current matrix the counter is on.
SymGroup is a collection of symmetry operations that satisfy the group property The symmetry operatio...
Definition: SymGroup.hh:33
double tol
EigenCounter< Eigen::VectorXi > EigenVectorXiCounter
Counter for Eigen::VectorXi.
Definition: Counter.hh:218
const_iterator begin() const
A const iterator to the beginning volume, specify here how the iterator should jump through the enume...
bool is_null() const
Check if null type.
Definition: jsonParser.cc:251
Eigen::VectorXi _canonical_unroll(const Eigen::MatrixXi &hermit_mat)
Unroll a Hermit normal form square matrix into a vector such that it's canonical form is easy to comp...
ScelEnumProps make_scel_enum_props(PrimClex &primclex, const jsonParser &input)
Make a ScelEnumProps object from JSON input.
SymGroup m_point_group
The point group of the unit cell.
EigenIndex Index
For long integer indexing:
HermiteCounter & operator++()
Jump to the next available HNF matrix.
bool get_else(T &t, const std::string &key, const T &default_value, Args...args) const
Definition: jsonParser.hh:749
PrimClex is the top-level data structure for a CASM project.
Definition: PrimClex.hh:52
HermiteCounter(int init_determinant, int init_dim)
constructor to satisfy iterator requirements. Do not recommend.
std::pair< Eigen::MatrixXi, Eigen::MatrixXi > hermite_normal_form(const Eigen::MatrixXi &M)
Return the hermite normal form, M == H*V.
EigenVectorXiCounter m_upper_tri
unrolled vector of the upper triangle (does not include diagonal elements)
void reset_current()
reset the counter to the first iteration of the current determinant
Eigen::Matrix3d Matrix3d
void next_determinant()
Skip the remaining iterations and start at the next determinant value.
Eigen::VectorXi m_diagonal
Vector holding diagonal element values.
const Supercell & get_supercell(Index i) const
const Access supercell by index
Definition: PrimClex.cc:311
bool contains(const std::string &name) const
Return true if JSON object contains 'name'.
Definition: jsonParser.cc:500
HermiteCounter::Index upper_size(HermiteCounter::Index init_dim)
Determine the number of elements in the upper triangular matrix (excluding diagonal) ...
double volume(const Lattice &lat)
Returns the volume of a Lattice.
Definition: Lattice.hh:372
Eigen::CwiseUnaryOp< decltype(std::ptr_fun(boost::math::iround< typename Derived::Scalar >)), const Derived > iround(const Eigen::MatrixBase< Derived > &val)
Round Eigen::MatrixXd to Eigen::MatrixXi.
HermiteCounter::Index next_spill_position(Eigen::VectorXi &diag, HermiteCounter::Index position)
Spill the next factor of the specified element with its neighbor, and return new position.
const Eigen::Matrix3i & get_transf_mat() const
Definition: Supercell.hh:263
EigenVectorXiCounter _upper_tri_counter(const Eigen::VectorXi &current_diag)
Create a counter for the elements above the diagonal based on the current diagonal value...
bool valid() const
Definition: BaseCounter.hh:146
Eigen::VectorXi::Scalar value_type
const Eigen::Matrix3i m_gen_mat
This matrix (G) specifies new lattice vectors to enumerate over column-wise, such that the resulting ...
Eigen::MatrixXi current() const
Get the current matrix the counter is on.
SupercellEnumerator(UnitType unit, const ScelEnumProps &enum_props, double tol)
Construct a SupercellEnumerator.
Eigen::Matrix3i make_unit_cell(PrimClex &primclex, const jsonParser &json)
Read unit cell transformation matrix from JSON input.