AFLOW
 
Loading...
Searching...
No Matches
aurostd_xcombos.cpp
Go to the documentation of this file.
1// ***************************************************************************
2// * *
3// * Aflow STEFANO CURTAROLO - Duke University 2003-2024 *
4// * *
5// ***************************************************************************
6
7#ifndef _AUROSTD_XCOMBOS_CPP_
8#define _AUROSTD_XCOMBOS_CPP_
9
10#include "aurostd_xcombos.h"
11
12#include <algorithm>
13#include <cstddef>
14#include <iostream>
15#include <vector>
16
17#include "aurostd.h"
19#include "aurostd_xerror.h"
20
21#include "aflow_xhost.h" // todo required for XHOST.DEBUG use
22
23#define _DEBUG_XCOMBOS_ false
24
25using aurostd::xerror;
26using std::cerr;
27using std::endl;
28
29namespace aurostd {
30 //--------------------------------------------------------------------------------
31 // class xcombos
32 //
33 // A class to create permutations, combinations, and enumerations. xcombos uses
34 // different "modes" to dinstinguish between these different types. A mode can
35 // be called using the char 'P', 'C', or 'E'.
36 //
37 // Permutations (mode 'P') are called using a vector that contains the elements
38 // to permute.
39 //
40 // Combinations (mode 'C') of m_choice out of n_choose elements are called using
41 // two integers. They may be created with or without repetitions.
42 //
43 // Enumerations (mode 'E') are combinations with repetitions that also include all
44 // permutations. For example, the combinations with repetitions of the numbers
45 // 0 and 1 of length 3 are {0, 0, 0}, {0, 0, 1}, {0, 1, 1}, and {1, 1, 1}.
46 // The enumerations with the same parameters are {0, 0, 0}, {0, 0, 1}, {0, 1, 0},
47 // {0, 1, 1}, {1, 0, 0}, {1, 0, 1}, {1, 1, 0}, {1, 1, 1}.
48 // It is not necessary for all dimension in an enumeration to have the same size.
49 // For example, the first dimension may just contain the number 0, which results
50 // in the enumerations {0, 0, 0}, {0, 0, 1}, {0, 1, 0}, {0, 1, 1}.
51 // If all dimensions are the same size, they are called like combinations but with
52 // mode 'E'. If the dimensions have different sizes, they are called with a vector.
53 //
54 //--------------------------------------------------------------------------------
55
56 //--------------------------------------------------------------------------------
57 // Permutation algorithms
58 // Depending on which algorithm is used, the swapping order for permutations can
59 // be different, see http://combos.org/perm
60 //
61 // Two algorithms are currently supported:
62 // 1) Shen - lexicographical order (default)
63 // reference: doi:10.1007/BF01940170
64 // first swap: 1234 -> 1243
65 // example use case(s): POSCARs and POCC algorithm
66 // 2) Heap - swap left-most first (fastest, minimum number of swaps)
67 // reference: https://en.wikipedia.org/wiki/Heap%27s_algorithm
68 // first swap: 1234 -> 2134
69 // example use case(s): finding representative atom decorations/permutations for prototypes
70 //
71 // If new algorithms are added, update the algorithms_xcombos enum.
72
73 //------------------------------------------------------------------------------
74 // constructor
75 //------------------------------------------------------------------------------
77 free();
78 }
79 xcombos::xcombos(const std::vector<int>& vec, bool sort, char mode, algorithm_xcombos algorithm) {
80 reset(vec, sort, mode, algorithm);
81 } // DX20201222 - added algorithm
82 xcombos::xcombos(int choice_count, int choose_count, char mode, bool rpt) {
83 reset(choice_count, choose_count, mode, rpt);
84 }
85 xcombos::xcombos(const std::vector<int>& vec, char mode) {
86 reset(vec, mode);
87 }
89 copy(b);
90 } // copy PUBLIC
92 free();
93 }
94
96 m_initialized = false;
97 m_input.clear();
98 n_choices = 0;
99 m_choose = 0;
100 m_mode = '\0'; // ME20180529
101 m_algorithm = shen_alg_xcombos; // DX20201222 - use Shen algorithm by default
102 m_sets.clear(); // ME20180529
103 m_sort = false;
104 m_started = false;
105 m_exhausted = false;
106 m_current.clear();
107 m_indices.clear();
108 m_p.clear();
109 m_x = 0;
110 m_y = 0;
111 m_repeat = false; // ME20180509
112 }
113
114 void xcombos::copy(const xcombos& b) { // copy PRIVATE
116 m_input = b.m_input;
118 m_choose = b.m_choose;
119 m_indices = b.m_indices; // ME20180620
120 m_mode = b.m_mode; // ME20180529
121 m_algorithm = b.m_algorithm; // DX20201222
122 m_sets = b.m_sets;
123 m_sort = b.m_sort;
127 m_p = b.m_p;
128 m_x = b.m_x;
129 m_y = b.m_y;
130 m_repeat = b.m_repeat; // ME20180509
131 }
132
133 const xcombos& xcombos::operator=(const xcombos& other) {
134 if (this != &other) {
135 free();
136 copy(other);
137 }
138 return *this;
139 }
140
141 xcombos& xcombos::operator++() { // remember, this is PREFIX (faster than POSTFIX)
142 if (!m_initialized) {
143 throw xerror(__AFLOW_FILE__, "xcombos::operator++()", "Cannot increment uninitialized xcombos class.", _RUNTIME_INIT_);
144 }
145 if (m_mode == 'P') {
147 } else {
148 if ((m_mode == 'C') && (!m_repeat)) {
150 } else {
152 }
153 }
154 return *this;
155 }
156
157 void xcombos::reset() { // clear PUBLIC
158 if (m_mode == 'P') {
160 } else {
161 if (!m_sets.empty()) {
163 } else {
165 }
166 }
167 }
168
169 void xcombos::reset(std::vector<int> vec, bool sort, char mode, algorithm_xcombos algorithm) { // do NOT make input vec a const &, this will screw up reset()
170 free();
171 m_input = vec;
172 m_mode = mode;
173 m_algorithm = algorithm; // DX20201222
174 if (m_mode == 'p') {
175 m_mode = 'P';
176 }
177 if (m_mode != 'P') {
178 m_exhausted = true;
179 std::cerr << "xcombos::reset: Unrecognized mode " << m_mode << " for permutations." << std::endl;
180 return;
181 }
182 m_sort = sort;
183 m_started = false;
184 if (m_input.empty()) {
185 m_exhausted = true;
186 return;
187 } // safe
188 initialize();
189 }
190
191 // ME20180509 - Implemented combinations with repetitions
192 void xcombos::reset(int choice_count, int choose_count, char mode, bool rpt) {
193 free();
194 m_mode = mode;
195 if (choice_count <= 0 || choose_count <= 0 || ((!rpt) && (choice_count < choose_count))) {
196 m_exhausted = true;
197 return;
198 } // safe
199 if (m_mode == 'e') {
200 m_mode = 'E';
201 }
202 if (m_mode == 'E') {
203 const std::vector<int> vec(choose_count, choice_count);
204 reset(vec, m_mode);
205 } else {
206 n_choices = choice_count;
207 m_choose = choose_count;
208 m_started = false;
209 m_repeat = rpt;
210 if (m_mode == 'c') {
211 m_mode = 'C';
212 }
213 if (m_mode != 'C') {
214 m_exhausted = true;
215 std::cerr << "Unrecognized mode " << m_mode << " for combinations." << std::endl;
216 return;
217 }
218 initialize();
219 }
220 }
221
222 // ME20180529 - Enumerations
223 void xcombos::reset(std::vector<int> vec, char mode) {
224 free();
225 m_mode = mode;
226 if (m_mode == 'e') {
227 m_mode = 'E';
228 }
229 if (m_mode != 'E') {
230 m_exhausted = true;
231 std::cerr << "Unrecognized mode " << m_mode << " for enumerations." << std::endl;
232 return;
233 }
234 m_choose = (int) vec.size();
235 n_choices = vec[vec.size() - 1];
236 m_sets = vec;
237 bool all_zero = true;
238 for (size_t i = 0; i < m_sets.size(); i++) {
239 if (m_sets[i] < 0) {
240 std::cerr << "xcombos::reset: m_sets cannot contain negative numbers." << std::endl;
241 m_exhausted = true;
242 return;
243 } else if (m_sets[i] > 0) {
244 all_zero = false;
245 }
246 }
247 if (all_zero) {
248 m_exhausted = true;
249 return;
250 }
251 initialize();
252 }
253
254 // ME20180509 - Implemented combinations with repetitions
256 const bool LDEBUG = (false || XHOST.DEBUG || _DEBUG_XCOMBOS_);
257 if (m_mode == 'P') {
259 if (m_sort) {
260 std::sort(m_current.begin(), m_current.end());
261 }
262 // ME20180620
263 m_indices.resize(m_input.size());
264 for (size_t i = 0; i < m_indices.size(); i++) {
265 m_indices[i] = i;
266 }
267 // for Heap's algorithm //DX20201222
268 m_p.clear();
269 m_p.resize(m_input.size(), 0);
270 n_choices = (int) m_input.size(); // DX+ME20210111
271 m_choose = (int) m_input.size(); // DX+ME20210111
272 } else {
273 if (LDEBUG) {
274 if (m_mode == 'C') {
275 std::cerr << __AFLOW_FUNC__ << " combinations: " << n_choices << " choose " << m_choose << ((m_repeat) ? " with " : " without ") << "repetitions."; // CO20200404
276 } else {
277 std::cerr << __AFLOW_FUNC__ << " enumerations of length " << m_choose << std::endl; // CO20200404
278 }
279 }
280 if ((m_repeat) || (m_mode == 'E')) {
281 m_current.resize(m_choose, 0);
282 } else {
283 m_current.resize(n_choices);
284 for (size_t i = 0; i < m_current.size(); i++) {
285 if ((int) i < m_choose) {
286 m_current[i] = 1;
287 } else {
288 m_current[i] = 0;
289 }
290 }
292 }
293 }
294 m_initialized = true;
295 }
296
297 const std::vector<int>& xcombos::getCombo() const {
298 return m_current;
299 } // ME20190703 - use const & (faster)
300 int xcombos::getN() const {
301 return n_choices;
302 }
303 int xcombos::getM() const {
304 return m_choose;
305 }
306
307 std::vector<int> xcombos::getIndices() const {
308 std::vector<int> vout;
309 if (m_mode == 'C') { // only works for combinations, return nothing otherwise
310 for (int i = 0; i < (int) m_current.size(); i++) {
311 if (m_current[i] == 1) {
312 vout.push_back(i);
313 }
314 }
315 }
316 return vout;
317 }
318
319 template <class utype> std::vector<utype> xcombos::applyCombo(const std::vector<utype>& v_items) const {
320 std::vector<utype> v_items_new;
321 if (!(m_mode == 'P' || m_mode == 'C')) {
322 return v_items_new;
323 }
324 // if permutations, then m_current contains indices
325 // otherwise, we need to get them
326 // ME says combinations with repetitions is same structure as permutations
327 std::vector<int> v_indices = m_current;
328 if ((m_mode == 'C') && !(m_repeat)) {
329 v_indices.clear();
330 v_indices = getIndices();
331 } // combo indices
332 for (size_t i = 0; i < v_indices.size(); i++) {
333 if (v_indices[i] >= (int) v_items.size()) {
334 throw xerror(__AFLOW_FILE__, __AFLOW_FUNC__, "Invalid index", _INDEX_MISMATCH_);
335 }
336 v_items_new.push_back(v_items[v_indices[i]]);
337 }
338 return v_items_new;
339 }
340#define AST_TEMPLATE(utype) template std::vector<utype> xcombos::applyCombo(const std::vector<utype>&) const;
343#undef AST_TEMPLATE
344
345 template <class utype> std::vector<utype> xcombos::applyCombo(const std::vector<std::vector<utype>>& v_items) const {
346 std::vector<utype> v_items_new;
347 if (m_mode != 'E') {
348 return v_items_new;
349 } // only applies to enumerations
350 std::vector<int> v_indices = m_current;
351 for (size_t i = 0; i < v_indices.size(); i++) {
352 if (v_indices[i] >= (int) v_items[i].size()) {
353 throw xerror(__AFLOW_FILE__, __AFLOW_FUNC__, "Invalid index", _INDEX_MISMATCH_);
354 }
355 v_items_new.push_back(v_items[i][v_indices[i]]);
356 }
357 return v_items_new;
358 }
359#define AST_TEMPLATE(utype) template std::vector<utype> xcombos::applyCombo(const std::vector<std::vector<utype>>&) const;
362#undef AST_TEMPLATE
363
365 if (!m_initialized) {
366 return false;
367 } // safety
368 if (m_exhausted == true) {
369 return false;
370 } // safety
371 if (!m_started) {
372 m_started = true;
373 return true;
374 } // allows us to use increment() for first grab too!
375 ++(*this);
376 if (m_exhausted == true) {
377 return false;
378 }
379 return true;
380 }
381
383 const bool LDEBUG = (false || XHOST.DEBUG || _DEBUG_XCOMBOS_);
384 if (m_exhausted) {
385 return;
386 }
388 // Shen, MK. BIT (1962) 2(228). doi:10.1007/BF01940170
389 // note this will generate next permutation in lexicographical order (left to right), e.g., first swap 1234 -> 1243
390 int _i = -1;
391 int _j = -1;
392 for (int i = 1; i < m_choose; i++) {
393 if (m_current[i - 1] < m_current[i] && (i > _i)) {
394 _i = i;
395 }
396 }
397 if (_i == -1) {
398 m_exhausted = true;
399 return;
400 } // stop condition
401 for (int j = 0; j < m_choose; j++) {
402 if (m_current[_i - 1] < m_current[j] && (j > _j)) {
403 _j = j;
404 }
405 }
406 if (LDEBUG) {
407 cerr << "xcombos::incrementPermutation(): i=" << _i << " j=" << _j << " " << endl;
408 }
409 std::swap(m_current[_i - 1], m_current[_j]);
410 std::swap(m_indices[_i - 1], m_indices[_j]); // ME20180620
411 for (int i = 0; i < (m_choose - _i + 1) / 2; i++) {
412 std::swap(m_current[_i + i], m_current[m_current.size() - i - 1]);
413 }
414 } else if (m_algorithm == heap_alg_xcombos) { // DX20201222 - added this algorithm
415 // Heap, B.R. (1963): https://en.wikipedia.org/wiki/Heap%27s_algorithm
416 // note this will generate permutation by swapping lowest position index (left-most position), e.g., first swap 1234 -> 2134
417 bool found_permutation = false;
418 uint count = 0;
419 const uint safety = m_choose; // DX20210111 - smarter saftey; each increment should swap no more than m_choose times
420 while (!found_permutation && count <= safety) {
421 count++;
422 if (m_x >= m_choose) {
423 m_exhausted = true;
424 return;
425 } // stop condition
426 if (m_p[m_x] < m_x) {
427 if (m_x % 2 == 0) {
428 std::swap(m_current[0], m_current[m_x]);
429 } else {
430 std::swap(m_current[m_p[m_x]], m_current[m_x]);
431 }
432 m_p[m_x] += 1;
433 m_x = 0;
434 found_permutation = true;
435 if (LDEBUG) {
436 cerr << "xcombos::incrementPermutation(): [HEAP] m_current: " << aurostd::joinWDelimiter(m_current, ",") << endl;
437 }
438 } else {
439 m_p[m_x] = 0;
440 m_x += 1;
441 }
442 }
443 if (count > safety) {
444 throw xerror(__AFLOW_FILE__, "xcombos::incrementPermutation()", "[HEAP] Uncontrolled while loop. Algorithm is not working as expected.", _RUNTIME_ERROR_);
445 }
446 } else { // DX2020111
447 throw xerror(__AFLOW_FILE__, "xcombos::incrementPermutation()", "Invalide algorithm type, only Shen (shen_alg_xcombos) and Heap's (heap_alg_xcombos) algorithms are available.", _INPUT_ERROR_);
448 }
449 }
450
451 // ME20180509 - Implemented combinations with repetitions
453 if (m_exhausted) {
454 return;
455 }
457 m_current[m_x] = 1;
458 m_current[m_y] = 0;
459 }
460
462 if (m_exhausted) {
463 return;
464 }
466 }
467
468 void xcombos::initializeCombinationsP() { // combinations only
469 const bool LDEBUG = (false || XHOST.DEBUG || _DEBUG_XCOMBOS_);
470 int i = 0;
471 m_p.resize(n_choices + 2);
472 m_p[i++] = -2;
473 while (m_choose - i + 1 > 0) {
474 m_p[i] = m_choose - i + 1;
475 i++;
476 }
477 while (i < n_choices + 1) {
478 m_p[i++] = 0;
479 }
480 m_p[n_choices + 1] = n_choices + 1;
481 if (m_choose == 0) {
482 m_p[n_choices] = 1;
483 }
484 if (LDEBUG) {
485 cerr << "xcombos::initializeCombinationsP(): p(0): ";
486 for (size_t l = 0; l < m_p.size(); l++) {
487 cerr << m_p[l] << " ";
488 }
489 cerr << endl;
490 }
491 }
492
494 if (!m_initialized) {
495 return;
496 }
497 if (m_exhausted) {
498 return;
499 }
500 const bool LDEBUG = (false || XHOST.DEBUG || _DEBUG_XCOMBOS_);
501 // Chase, PJ. ACM (1970) 13(6). doi:10.1145/362384.362502
502 // implementation modified from twiddle.c by Matthew Belmonte
503 // http://www.netlib.no/netlib/toms/382
504 // note this will generate next permutation in lexicographical order (left to right)
505 // originally, Belmonte programmed right to left
506 int i;
507 int j;
508 int k;
509 i = j = k = -1;
510 j = n_choices;
511 if (LDEBUG) {
512 cerr << "xcombos::setCombinationsIncrementParameters(): j(0)=" << j << endl;
513 }
514 while (m_p[j] <= 0) {
515 j--;
516 }
517 if (LDEBUG) {
518 cerr << "xcombos::setCombinationsIncrementParameters(): j(1)=" << j << endl;
519 }
520 if (m_p[j + 1] == 0) {
521 for (i = j + 1; i != n_choices; i++) {
522 m_p[i] = -1;
523 }
524 m_p[j] = 0;
525 m_x = n_choices - 1;
526 m_p[n_choices] = 1;
527 m_y = j - 1;
528 if (LDEBUG) {
529 cerr << "xcombos::setCombinationsIncrementParameters(): p(0): ";
530 for (size_t l = 0; l < m_p.size(); l++) {
531 cerr << m_p[l] << " ";
532 }
533 cerr << endl;
534 cerr << "xcombos::setCombinationsIncrementParameters(): x=" << m_x << "(1)" << endl;
535 cerr << "xcombos::setCombinationsIncrementParameters(): y=" << m_y << "(0)" << endl;
536 }
537 } else {
538 if (j < n_choices) {
539 m_p[j + 1] = 0;
540 }
541 j--;
542 while (m_p[j] > 0) {
543 j--;
544 }
545 if (LDEBUG) {
546 cerr << "xcombos::setCombinationsIncrementParameters(): j(2)=" << j << endl;
547 }
548 k = j + 1;
549 i = j;
550 if (LDEBUG) {
551 cerr << "xcombos::setCombinationsIncrementParameters(): i(0)=" << i << endl;
552 cerr << "xcombos::setCombinationsIncrementParameters(): k(0)=" << k << endl;
553 }
554 while (m_p[i] == 0) {
555 m_p[i--] = -1;
556 }
557 if (LDEBUG) {
558 cerr << "xcombos::setCombinationsIncrementParameters(): i(1)=" << i << endl;
559 }
560 if (m_p[i] == -1) {
561 m_p[i] = m_p[k];
562 m_x = i - 1;
563 m_y = k - 1;
564 m_p[k] = -1;
565 if (LDEBUG) {
566 cerr << "xcombos::setCombinationsIncrementParameters(): p(1): ";
567 for (size_t l = 0; l < m_p.size(); l++) {
568 cerr << m_p[l] << " ";
569 }
570 cerr << endl;
571 cerr << "x=" << m_x << "(1)" << endl;
572 cerr << "y=" << m_y << "(0)" << endl;
573 }
574 } else {
575 if (LDEBUG) {
576 cerr << "xcombos::setCombinationsIncrementParameters(): i=" << i << endl;
577 cerr << "xcombos::setCombinationsIncrementParameters(): p(2): ";
578 for (size_t l = 0; l < m_p.size(); l++) {
579 cerr << m_p[l] << " ";
580 }
581 cerr << endl;
582 }
583 if (i == 0) {
584 m_exhausted = true;
585 return;
586 }
587 // if(i==m_p[n_choices+1]) {m_exhausted=true; cerr << "HERE" << endl;return;}
588 else {
589 m_p[j] = m_p[i];
590 m_p[i] = 0;
591 m_x = j - 1;
592 m_y = i - 1;
593 if (LDEBUG) {
594 cerr << "xcombos::setCombinationsIncrementParameters(): p(3): ";
595 for (size_t l = 0; l < m_p.size(); l++) {
596 cerr << m_p[l] << " ";
597 }
598 cerr << endl;
599 cerr << "x=" << m_x << "(1)" << endl;
600 cerr << "y=" << m_y << "(0)" << endl;
601 }
602 }
603 }
604 }
605 return;
606 }
607
608 // ME20180529
609 // Creates the next enumeration in lexicographical order
611 const bool LDEBUG = (false || XHOST.DEBUG || _DEBUG_XCOMBOS_);
612 if (m_current[m_choose - 1] == n_choices - 1) {
613 int p = m_choose - 2;
614 if (m_mode == 'E') {
615 while ((p >= 0) && (m_current[p] == m_sets[p] - 1)) {
616 p--;
617 }
618 } else {
619 while ((p >= 0) && (m_current[p] == n_choices - 1)) {
620 p--;
621 }
622 }
623 if (LDEBUG) {
624 cerr << "xcombos::getNextEnumeration: p(0) = " << p << endl;
625 }
626 if (p == -1) {
627 m_exhausted = true;
628 return;
629 } else {
630 m_current[p]++;
631 for (int i = p + 1; i < m_choose; i++) {
632 if (m_mode == 'E') {
633 m_current[i] = 0;
634 } else {
635 m_current[i] = m_current[p];
636 }
637 }
638 }
639 } else {
640 m_current[m_choose - 1]++;
641 }
642 if (LDEBUG) {
643 cerr << "xcombos::getNextEnumeration: m_current(0): ";
644 for (size_t i = 0; i < m_current.size(); i++) {
645 cerr << m_current[i] << " ";
646 }
647 cerr << endl;
648 }
649 return;
650 }
651} // namespace aurostd
652
653#endif // _AUROSTD_XCOMBOS_CPP_
654// ***************************************************************************
655// * *
656// * Aflow STEFANO CURTAROLO - Duke University 2003-2024 *
657// * *
658// ***************************************************************************
_XHOST XHOST
unsigned uint
Definition aurostd.h:39
#define __AFLOW_FILE__
Definition aurostd.h:44
#define __AFLOW_FUNC__
Definition aurostd.h:43
This file contains the preprocessor macros to ensure a proper instantiation of all aurostd functions.
#define AST_GEN_1(type_selection)
autogenerate 1D code based on AST_TEMPLATE
#define AST_UTYPE_TEXT
#define AST_UTYPE_NUM
#define _DEBUG_XCOMBOS_
algorithm_xcombos
@ heap_alg_xcombos
@ shen_alg_xcombos
#define _RUNTIME_INIT_
#define _RUNTIME_ERROR_
#define _INPUT_ERROR_
#define _INDEX_MISMATCH_
void setCombinationsIncrementParameters()
std::vector< int > m_p
const std::vector< int > & getCombo() const
std::vector< int > m_sets
algorithm_xcombos m_algorithm
std::vector< int > getIndices() const
std::vector< int > m_indices
void copy(const xcombos &b)
const xcombos & operator=(const xcombos &other)
std::vector< int > m_input
std::vector< int > m_current
std::vector< utype > applyCombo(const std::vector< utype > &v_items) const
utype mode(const xvector< utype > &a)
void sort(vector< utype1 > &arr)
string joinWDelimiter(const xvector< utype > &ientries, const char delimiter)