CASM  1.1.0
A Clusters Approach to Statistical Mechanics
HermiteCounter.cc
Go to the documentation of this file.
2 
3 namespace CASM {
4 namespace xtal {
5 HermiteCounter::HermiteCounter(int init_start_determinant, int init_dim)
6  : m_pos(0) {
7  m_diagonal = Eigen::VectorXi::Ones(init_dim);
8  m_diagonal(0) = init_start_determinant;
9 
11 }
12 
14 
15 Eigen::VectorXi HermiteCounter::diagonal() const { return m_diagonal; }
16 
17 Eigen::MatrixXi HermiteCounter::current() const {
19 }
20 
22 
23 Eigen::MatrixXi HermiteCounter::operator()() const { return current(); }
24 
26  return m_diagonal.prod();
27 }
28 
31  return;
32 }
33 
36  return;
37 }
38 
41  return m_pos;
42 }
43 
45  // Vary elements above diagonal
46  m_upper_tri++;
47 
48  // Nothing to permute for identity
49  if (determinant() == 1) {
51  }
52 
53  // Still other matrices available for current diagonal
54  else if (m_upper_tri.valid()) {
55  return *this;
56  }
57 
58  // Find next diagonal, reset elements above diagonal
59  else if (_increment_diagonal() < dim()) {
61  }
62 
63  // Reset diagonal with next determinant value, reset elements above diagonal
64  else {
66  }
67 
68  return *this;
69 }
70 
72  if (new_det < 1) {
73  throw std::runtime_error(
74  "Determinants of Hermite normal form matrices must be greater than 0");
75  }
76 
77  m_diagonal = Eigen::VectorXi::Ones(dim());
78  m_diagonal(0) = new_det;
79 
81  m_pos = 0;
82 
83  return;
84 }
85 
86 namespace HermiteCounter_impl {
87 HermiteCounter::Index _spill_factor(Eigen::VectorXi &diag,
88  HermiteCounter::Index position,
90  // If you fall into these traps you're using this wrong
91  assert(attempt <= diag(position));
92  assert(position < diag.size() - 1);
93  assert(diag(position + 1) == 1);
94  assert(diag(position) > 1);
95  assert(attempt >= 2);
96 
97  // Use the given attempt as a starting guess for factorizing the element at
98  // position, but if that doesn't work, keep increasing the value of the
99  // attempt until it does.
100  while (diag(position) % attempt != 0) {
101  attempt++;
102  }
103 
104  diag(position) = diag(position) / attempt;
105  diag(position + 1) = attempt;
106 
107  position++;
108 
109  return position;
110 }
111 
113  HermiteCounter::Index position) {
114  HermiteCounter::value_type attempt = 2;
115 
116  // If you reached the end of the diagonal, backtrack to nearest non-1 value
117  // and perform the next spill
118  if (position == diag.size() - 1) {
119  do {
120  position--;
121  } while (position >= 0 && diag(position) == 1);
122 
123  // You're at the last spill already. Your diagonal is 1 1 ... 1 1 det. No
124  // more increments are possible.
125  if (position < 0) {
126  return diag.size();
127  }
128 
129  // Flush everything to the right of position with ones, and reset the value
130  // at position with the next attempt for factorization
131  attempt = diag(diag.size() - 1) + 1;
132  diag(position) = diag(position) * diag(diag.size() - 1);
133  diag(diag.size() - 1) = 1;
134  }
135 
136  return _spill_factor(diag, position, attempt);
137 }
138 
140  assert(init_dim > 0);
141 
142  HermiteCounter::Index tritotal = 0;
143 
144  for (int i = 0; i < init_dim; i++) {
145  tritotal += i;
146  }
147 
148  return tritotal;
149 }
150 
151 EigenVectorXiCounter _upper_tri_counter(const Eigen::VectorXi &current_diag) {
152  // Find out how many slots you need for all the elements above the diagonal
153  HermiteCounter::Index uppersize = upper_size(current_diag.size());
154 
155  // Start the counter with zero everywhere
156  Eigen::VectorXi begincount(Eigen::VectorXi::Zero(uppersize));
157 
158  // Increments always go one at a time
159  Eigen::VectorXi stepcount(Eigen::VectorXi::Ones(uppersize));
160 
161  // The m_upper_tri is unrolled left to right, top to bottom
162  Eigen::VectorXi endcount(Eigen::VectorXi::Zero(uppersize));
163  HermiteCounter::Index slot = 0;
164  for (HermiteCounter::Index i = 0; i < current_diag.size(); i++) {
165  for (HermiteCounter::Index j = i + 1; j < current_diag.size(); j++) {
166  // The counter value should always be smaller than the diagonal
167  endcount(slot) = current_diag(i) - 1;
168  slot++;
169  }
170  }
171 
172  return EigenVectorXiCounter(begincount, endcount, stepcount);
173 }
174 
175 Eigen::MatrixXi _zip_matrix(const Eigen::VectorXi &current_diag,
176  const Eigen::VectorXi &current_upper_tri) {
177  assert(current_upper_tri.size() == upper_size(current_diag.size()));
178 
179  Eigen::MatrixXi hmat(current_diag.asDiagonal());
180 
181  Index slot = 0;
182  for (Index i = 0; i < current_diag.size() - 1; i++) {
183  for (Index j = i + 1; j < current_diag.size(); j++) {
184  hmat(i, j) = current_upper_tri(slot);
185  slot++;
186  }
187  }
188 
189  return hmat;
190 }
191 
215 Eigen::MatrixXi _expand_dims_old(const Eigen::MatrixXi &hermit_mat,
216  const Eigen::VectorXi &active_dims) {
217  assert(hermit_mat.rows() == active_dims.sum() &&
218  hermit_mat.cols() == active_dims.sum());
219  assert(active_dims.maxCoeff() == 1 && active_dims.minCoeff() == 0);
220 
221  Eigen::MatrixXi expanded(
222  Eigen::MatrixXi::Identity(active_dims.size(), active_dims.size()));
223 
224  HermiteCounter::Index i, j, si, sj;
225  si = -1;
226  sj = -1;
227 
228  for (i = 0; i < expanded.rows(); i++) {
229  if (active_dims(i) == 0) {
230  continue;
231  }
232 
233  else {
234  si++;
235  }
236 
237  for (j = 0; j < expanded.cols(); j++) {
238  if (active_dims(j) == 0) {
239  continue;
240  }
241 
242  else {
243  sj++;
244  }
245 
246  expanded(i, j) = hermit_mat(si, sj);
247  }
248 
249  j = 0;
250  sj = -1;
251  }
252 
253  return expanded;
254 }
255 
256 /*
257  * This is a generalized way to insert new dimensions into a HNF matrix.
258  * If there is a n x n HNF matrix H, it will be expanded to a new non-HNF
259  * m x m matrix T through a m x m generating matrix G.
260  *
261  * The first n columns of G will be multiplied with the values of H, while
262  * the last m-n columns of G will remain unaffected. For example, if you
263  * are counting over a 2x2 H matrix and currently have
264  *
265  * 2 1
266  * 0 3
267  *
268  * Then you can specify that you want a 3x3 matrix T so that the values of
269  * H work on your lattice vectors a and c, but not b with a G matrix
270  *
271  * 1 0 0
272  * 0 0 1
273  * 0 1 0
274  *
275  * The resulting expanded matrix T is now
276  *
277  * 2 1 0
278  * 0 0 1
279  * 0 3 0
280  *
281  * This is achieved by converting H to a block matrix of dimensions m x m,
282  * which is an identity matrix with the upper left block equal to H. For
283  * the example above B would be
284  *
285  * 2 1 0
286  * 0 3 0
287  * 0 0 1
288  *
289  * This way T=G*B
290  *
291  * You may specify arbitrary combinations of vectors in the columns of G that
292  * H will work on.
293  *
294  * Note that the resulting matrix will probably *NOT* retain it's Hermite normal
295  * form. For use in the SuperlatticeEnumerator class, the order within the first
296  * n vectors and the order within the last m-n vectors will not affect your
297  * enumerations.
298  */
299 
300 Eigen::MatrixXi _expand_dims(const Eigen::MatrixXi &H,
301  const Eigen::MatrixXi &G) {
302  assert(H.rows() == H.cols());
303  assert(G.rows() == G.cols());
304  assert(G.rows() >= H.rows());
305 
306  Index n = H.rows();
307  Index m = G.rows();
308 
309  // First convert H into a block matrix with dimensions m x m, the H block is
310  // on the upper left
311  Eigen::MatrixXi B(m, m);
312  Eigen::MatrixXi I_block(Eigen::MatrixXi::Identity(m - n, m - n));
313  Eigen::MatrixXi Z_block(Eigen::MatrixXi::Zero(n, m - n));
314  B << H, Z_block, Z_block.transpose(), I_block;
315 
316  return G * B;
317 }
318 
337 Eigen::VectorXi _canonical_unroll(const Eigen::MatrixXi &hermit_mat) {
338  assert(hermit_mat.rows() == hermit_mat.cols());
339 
340  int dims = hermit_mat.rows();
341  int unrolldim = 0;
342 
343  for (int i = dims; i > 0; i--) {
344  unrolldim += i;
345  }
346 
347  Eigen::VectorXi unrolled_herm(unrolldim);
348 
349  Index lasti, lastj;
350  lasti = lastj = -1;
351 
352  enum STEP { DOWN, UP, LEFT };
353  STEP curr_step = DOWN;
354 
355  Index unroll_ind = 0;
356 
357  for (Index i = 0; i < dims; i++) {
358  for (Index j = 0; j < dims - i; j++) {
359  switch (curr_step) {
360  case DOWN:
361  lasti = lasti + 1;
362  lastj = lastj + 1;
363  break;
364  case UP:
365  lasti = lasti - 1;
366  break;
367  case LEFT:
368  lastj = lastj - 1;
369  break;
370  }
371 
372  unrolled_herm(unroll_ind) = hermit_mat(lasti, lastj);
373  unroll_ind++;
374  }
375 
376  switch (curr_step) {
377  case DOWN:
378  curr_step = UP;
379  break;
380  case UP:
381  curr_step = LEFT;
382  break;
383  case LEFT:
384  curr_step = DOWN;
385  break;
386  }
387  }
388 
389  return unrolled_herm;
390 }
391 
412 bool _canonical_compare(const Eigen::MatrixXi &H0, const Eigen::MatrixXi &H1) {
413  const Eigen::VectorXi unrolled_H0 = _canonical_unroll(H0);
414  const Eigen::VectorXi unrolled_H1 = _canonical_unroll(H1);
415 
416  assert(unrolled_H0.size() == unrolled_H1.size());
417 
418  for (Index i = 0; i < unrolled_H0.size(); i++) {
419  if (unrolled_H0(i) > unrolled_H1(i)) {
420  return false;
421  }
422 
423  else if (unrolled_H0(i) < unrolled_H1(i)) {
424  return true;
425  }
426  }
427 
428  // Both are equal if you get this far
429  return false;
430 }
431 } // namespace HermiteCounter_impl
432 } // namespace xtal
433 } // namespace CASM
bool valid() const
Definition: BaseCounter.hh:160
const Container & current() const
Definition: BaseCounter.hh:201
A Counter allows looping over many incrementing variables in one loop.
Definition: Counter.hh:109
Index m_pos
Keeps track of the current diagonal element that needs to be factored.
Index _increment_diagonal()
Go to the next values of diagonal elements that keep the same determinant.
HermiteCounter(int init_determinant, int init_dim)
constructor to satisfy iterator requirements. Do not recommend.
HermiteCounter & operator++()
Jump to the next available HNF matrix.
void next_determinant()
Skip the remaining iterations and start at the next determinant value.
void jump_to_determinant(value_type new_det)
Reset the diagonal to the specified determinant and set the other elements to zero.
Eigen::MatrixXi operator()() const
Get the current matrix the counter is on.
EigenVectorXiCounter m_upper_tri
unrolled vector of the upper triangle (does not include diagonal elements)
Eigen::VectorXi::Scalar value_type
Index dim() const
Get the dimensions of *this.
Eigen::VectorXi m_diagonal
Vector holding diagonal element values.
Eigen::VectorXi diagonal() const
value_type determinant() const
Get the current determinant.
Eigen::MatrixXi current() const
Get the current matrix the counter is on.
void reset_current()
reset the counter to the first iteration of the current determinant
EigenCounter< Eigen::VectorXi > EigenVectorXiCounter
Counter for Eigen::VectorXi.
Definition: Counter.hh:231
IdentitySymRepBuilder Identity()
EigenVectorXiCounter _upper_tri_counter(const Eigen::VectorXi &current_diag)
Create a counter for the elements above the diagonal based on the current diagonal value.
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....
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.
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<H1.
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.
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...
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)
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...
HermiteCounter::Index upper_size(HermiteCounter::Index init_dim)
Determine the number of elements in the upper triangular matrix (excluding diagonal)
Main CASM namespace.
Definition: APICommand.hh:8
INDEX_TYPE Index
For long integer indexing:
Definition: definitions.hh:39