1 // 2 // libsemigroups - C++ library for semigroups and monoids 3 // Copyright (C) 2019 James D. Mitchell 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU General Public License for more details. 14 // 15 // You should have received a copy of the GNU General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 // 18 19 // This file contains implementations of the member functions for the 20 // FroidurePin class. 21 22 #include "libsemigroups-debug.hpp" // for LIBSEMIGROUPS_ASSERT 23 #include "libsemigroups-exception.hpp" // for LIBSEMIGROUPS_EXCEPTION 24 #include "report.hpp" // for REPORT 25 #include "timer.hpp" // for detail::Timer 26 27 #ifndef LIBSEMIGROUPS_INCLUDE_FROIDURE_PIN_IMPL_HPP_ 28 #define LIBSEMIGROUPS_INCLUDE_FROIDURE_PIN_IMPL_HPP_ 29 30 #define TEMPLATE template <typename TElementType, typename TTraits> 31 #define FROIDURE_PIN FroidurePin<TElementType, TTraits> 32 33 #define VOID TEMPLATE void 34 #define INLINE_VOID TEMPLATE inline void 35 #define SIZE_T TEMPLATE size_t 36 #define BOOL TEMPLATE bool 37 #define TRIL TEMPLATE tril 38 #define ELEMENT_INDEX_TYPE TEMPLATE element_index_type 39 #define LETTER_TYPE TEMPLATE letter_type 40 #define CONST_REFERENCE TEMPLATE typename FROIDURE_PIN::const_reference 41 #define ELEMENT_TYPE TEMPLATE typename FROIDURE_PIN::element_type 42 #define CAYLEY_GRAPH_TYPE TEMPLATE typename FROIDURE_PIN::cayley_graph_type 43 #define WORD_TYPE TEMPLATE word_type 44 45 namespace libsemigroups { 46 // using enumerate_index_type = FroidurePinBase::size_type; 47 using element_index_type = FroidurePinBase::element_index_type; 48 49 //////////////////////////////////////////////////////////////////////// 50 // FroidurePin - constructors + destructor - public 51 //////////////////////////////////////////////////////////////////////// 52 53 TEMPLATE FroidurePin(std::vector<element_type> const * gens)54 FROIDURE_PIN::FroidurePin(std::vector<element_type> const* gens) 55 : detail::BruidhinnTraits<TElementType>(), 56 FroidurePinBase(), 57 _degree(UNDEFINED), 58 _duplicate_gens(), 59 _elements(), 60 _enumerate_order(), 61 _final(), 62 _first(), 63 _found_one(false), 64 _gens(), 65 _id(), 66 _idempotents(), 67 _idempotents_found(false), 68 _is_idempotent(), 69 _left(gens->size()), 70 _length(), 71 _lenindex(), 72 _letter_to_pos(), 73 _map(), 74 _mtx(), 75 _nr(0), 76 _nrgens(gens->size()), 77 _nr_rules(0), 78 _pos(0), 79 _pos_one(0), 80 _prefix(), 81 _reduced(gens->size()), 82 _relation_gen(0), 83 _relation_pos(UNDEFINED), 84 _right(gens->size()), 85 _sorted(), 86 _suffix(), 87 _tmp_product(), 88 _wordlen(0) { // (length of the current word) - 1 89 if (_nrgens == 0) { 90 LIBSEMIGROUPS_EXCEPTION("no generators given"); 91 } 92 #ifdef LIBSEMIGROUPS_VERBOSE 93 _nr_products = 0; 94 #endif 95 _right.set_default_value(UNDEFINED); 96 // FIXME(later) inclusion of the next line makes test FroidurePin of BMats 97 // 01 extremely slow (~50ms to ~10s!!!!) 98 // reserve(_nrgens); 99 _degree = Degree()((*gens)[0]); 100 101 for (size_t i = 0; i < _nrgens; ++i) { 102 size_t degree = Degree()((*gens)[i]); 103 if (degree != _degree) { 104 LIBSEMIGROUPS_EXCEPTION( 105 "generator %d has degree %d but should have degree %d", 106 i, 107 degree, 108 _degree); 109 } 110 } 111 for (const_reference x : *gens) { 112 _gens.push_back(this->internal_copy(this->to_internal_const(x))); 113 } 114 115 _tmp_product = this->to_internal(One()(this->to_external_const(_gens[0]))); 116 _id = this->to_internal(One()(this->to_external_const(_gens[0]))); 117 _lenindex.push_back(0); 118 119 // #ifdef LIBSEMIGROUPS_DENSEHASHMAP 120 // _map.set_empty_key(this->empty_key(_id)); 121 // #endif 122 123 // add the generators 124 for (letter_type i = 0; i < _nrgens; i++) { 125 auto it = _map.find(_gens[i]); 126 if (it != _map.end()) { // duplicate generator 127 _letter_to_pos.push_back(it->second); 128 _nr_rules++; 129 _duplicate_gens.emplace_back(i, _first[it->second]); 130 // i.e. _gens[i] = _gens[_first[it->second]] 131 // _first maps from element_index_type -> letter_type :) 132 } else { 133 is_one(_gens[i], _nr); 134 _elements.push_back(_gens[i]); 135 // Note that every non-duplicate generator is *really* stored in 136 // _elements, and so must be deleted from _elements but not _gens. 137 _first.push_back(i); 138 _final.push_back(i); 139 _enumerate_order.push_back(_nr); 140 _letter_to_pos.push_back(_nr); 141 _length.push_back(1); 142 _map.emplace(_elements.back(), _nr); 143 _prefix.push_back(UNDEFINED); 144 // TODO(later) _prefix.push_back(_nr) and get rid of _letter_to_pos, and 145 // the extra clause in the run member function! 146 _suffix.push_back(UNDEFINED); 147 _nr++; 148 } 149 } 150 expand(_nr); 151 _lenindex.push_back(_enumerate_order.size()); 152 } 153 154 TEMPLATE FroidurePin(std::vector<element_type> const & gens)155 FROIDURE_PIN::FroidurePin(std::vector<element_type> const& gens) 156 : FroidurePin(&gens) {} 157 158 TEMPLATE FroidurePin(std::initializer_list<element_type> gens)159 FROIDURE_PIN::FroidurePin(std::initializer_list<element_type> gens) 160 : FroidurePin(std::vector<element_type>(gens)) {} 161 162 TEMPLATE FroidurePin(FroidurePin const & S)163 FROIDURE_PIN::FroidurePin(FroidurePin const& S) 164 : detail::BruidhinnTraits<TElementType>(), 165 FroidurePinBase(S), 166 _degree(S._degree), 167 _duplicate_gens(S._duplicate_gens), 168 _elements(), 169 _enumerate_order(S._enumerate_order), 170 _final(S._final), 171 _first(S._first), 172 _found_one(S._found_one), 173 _gens(), 174 _id(this->internal_copy(S._id)), 175 _idempotents(S._idempotents), 176 _idempotents_found(S._idempotents_found), 177 _is_idempotent(S._is_idempotent), 178 _left(S._left), 179 _length(S._length), 180 _lenindex(S._lenindex), 181 _letter_to_pos(S._letter_to_pos), 182 _nr(S._nr), 183 _nrgens(S._nrgens), 184 _nr_rules(S._nr_rules), 185 _pos(S._pos), 186 _pos_one(S._pos_one), 187 _prefix(S._prefix), 188 _reduced(S._reduced), 189 _relation_gen(S._relation_gen), 190 _relation_pos(S._relation_pos), 191 _right(S._right), 192 _sorted(), // TODO(later) S this if set 193 _suffix(S._suffix), 194 _wordlen(S._wordlen) { 195 #ifdef LIBSEMIGROUPS_VERBOSE 196 _nr_products = 0; 197 #endif 198 _elements.reserve(_nr); 199 200 // #ifdef LIBSEMIGROUPS_DENSEHASHMAP 201 // _map.set_empty_key(this->empty_key(_id)); 202 // _map.resize(_nr); 203 // #else 204 // // _map.reserve(_nr); // FIXME(later) uncommenting this line causes 205 // huge 206 // // slow down in FroidurePin 000 207 // #endif 208 _tmp_product = this->internal_copy(S._id); 209 210 element_index_type i = 0; 211 for (internal_const_reference x : S._elements) { 212 auto y = this->internal_copy(x); 213 _elements.push_back(y); 214 _map.emplace(y, i++); 215 } 216 copy_gens(); 217 } 218 219 TEMPLATE ~FroidurePin()220 FROIDURE_PIN::~FroidurePin() { 221 this->internal_free(_tmp_product); 222 this->internal_free(_id); 223 224 // delete those generators not in _elements, i.e. the duplicate ones 225 for (auto& x : _duplicate_gens) { 226 this->internal_free(_gens[x.first]); 227 } 228 for (auto& x : _elements) { 229 this->internal_free(x); 230 } 231 } 232 233 //////////////////////////////////////////////////////////////////////// 234 // FroidurePin - constructor - private 235 //////////////////////////////////////////////////////////////////////// 236 237 // Partial copy. 238 // \p copy a semigroup 239 // \p coll a collection of additional generators 240 // 241 // This is a constructor for a semigroup generated by the generators of the 242 // FroidurePin copy and the (possibly) additional generators coll. 243 // 244 // The relevant parts of the data structure of copy are copied and 245 // \c this will be corrupt unless add_generators or closure is called 246 // subsequently. This is why this member function is private. 247 // 248 // The same effect can be obtained by copying copy using the copy 249 // constructor and then calling add_generators or closure. However, 250 // this constructor avoids copying those parts of the data structure of 251 // copy that add_generators invalidates anyway. If copy has not been 252 // enumerated at all, then these two routes for adding more generators are 253 // equivalent. 254 // 255 // <add_generators> or <closure> should usually be called after this. 256 TEMPLATE FroidurePin(FroidurePin const & S,std::vector<element_type> const * coll)257 FROIDURE_PIN::FroidurePin(FroidurePin const& S, 258 std::vector<element_type> const* coll) 259 : _degree(S._degree), // copy for comparison in add_generators 260 _duplicate_gens(S._duplicate_gens), 261 _elements(), 262 _found_one(S._found_one), // copy in case degree doesn't change in 263 // add_generators 264 _gens(), 265 _idempotents(S._idempotents), 266 _idempotents_found(S._idempotents_found), 267 _is_idempotent(S._is_idempotent), 268 _left(S._left), 269 _letter_to_pos(S._letter_to_pos), 270 _nr(S._nr), 271 _nrgens(S._nrgens), 272 _nr_rules(0), 273 _pos(S._pos), 274 _pos_one(S._pos_one), // copy in case degree doesn't change in 275 // add_generators 276 _reduced(S._reduced), 277 _relation_gen(0), 278 _relation_pos(UNDEFINED), 279 _right(S._right), 280 _sorted(), 281 _wordlen(0) { 282 LIBSEMIGROUPS_ASSERT(!coll->empty()); 283 LIBSEMIGROUPS_ASSERT(Degree()(coll->at(0)) >= S.degree()); 284 285 #ifdef LIBSEMIGROUPS_DEBUG 286 for (const_reference x : *coll) { 287 LIBSEMIGROUPS_ASSERT(Degree()(x) == Degree()((*coll)[0])); 288 } 289 #endif 290 #ifdef LIBSEMIGROUPS_VERBOSE 291 _nr_products = 0; 292 #endif 293 _elements.reserve(S._nr); 294 295 // the following are required for assignment to specific positions in 296 // add_generators 297 _final.resize(S._nr, 0); 298 _first.resize(S._nr, 0); 299 _length.resize(S._nr, 0); 300 _prefix.resize(S._nr, 0); 301 _suffix.resize(S._nr, 0); 302 303 size_t deg_plus = Degree()(coll->at(0)) - S.degree(); 304 305 if (deg_plus != 0) { 306 _degree += deg_plus; 307 _found_one = false; 308 _pos_one = 0; 309 } 310 311 _lenindex.push_back(0); 312 _lenindex.push_back(S._lenindex[1]); 313 _enumerate_order.reserve(S._nr); 314 315 // add the distinct old generators to new _enumerate_order 316 for (enumerate_index_type i = 0; i < S._lenindex[1]; i++) { 317 _enumerate_order.push_back(S._enumerate_order[i]); 318 _final[_enumerate_order[i]] = S._final[S._enumerate_order[i]]; 319 _first[_enumerate_order[i]] = S._first[S._enumerate_order[i]]; 320 _prefix[_enumerate_order[i]] = UNDEFINED; 321 _suffix[_enumerate_order[i]] = UNDEFINED; 322 _length[_enumerate_order[i]] = 1; 323 } 324 325 _id = One()(this->to_internal(coll->at(0))); 326 _tmp_product = this->internal_copy(_id); 327 328 // #ifdef LIBSEMIGROUPS_DENSEHASHMAP 329 // _map.set_empty_key(this->empty_key(_id)); 330 // _map.resize(S._nr); 331 // #else 332 _map.reserve(S._nr); 333 // #endif 334 335 element_index_type i = 0; 336 for (internal_const_reference x : S._elements) { 337 auto y = this->internal_copy(x); 338 IncreaseDegree()(y, deg_plus); 339 _elements.push_back(y); 340 _map.emplace(y, i); 341 is_one(y, i++); 342 } 343 copy_gens(); // copy the old generators 344 // Now this is ready to have add_generators or closure called on it 345 } 346 347 //////////////////////////////////////////////////////////////////////// 348 // FroidurePin - member functions - public 349 //////////////////////////////////////////////////////////////////////// 350 word_to_pos(word_type const & w) const351 ELEMENT_INDEX_TYPE FROIDURE_PIN::word_to_pos(word_type const& w) const { 352 // w is a word in the generators (i.e. a vector of letter_type's) 353 if (w.size() == 0) { 354 LIBSEMIGROUPS_EXCEPTION("the given word has length 0"); 355 } 356 for (auto x : w) { 357 validate_letter_index(x); 358 } 359 element_index_type out = _letter_to_pos[w[0]]; 360 for (auto it = w.cbegin() + 1; it < w.cend() && out != UNDEFINED; ++it) { 361 out = _right.get(out, _letter_to_pos[*it]); 362 } 363 return out; 364 } 365 word_to_element(word_type const & w) const366 ELEMENT_TYPE FROIDURE_PIN::word_to_element(word_type const& w) const { 367 element_index_type pos = word_to_pos(w); 368 if (pos != UNDEFINED) { 369 // Return a copy 370 return this->external_copy(this->to_external_const(_elements[pos])); 371 } 372 // word_to_pos is always known for generators (i.e. when w.size() == 1), 373 // and word_to_pos verifies that w is valid. 374 LIBSEMIGROUPS_ASSERT(w.size() > 1); 375 LIBSEMIGROUPS_ASSERT(w[0] < nr_generators() && w[1] < nr_generators()); 376 element_type prod 377 = this->external_copy(this->to_external_const(_tmp_product)); 378 Product()(prod, 379 this->to_external_const(_gens[w[0]]), 380 this->to_external_const(_gens[w[1]])); 381 for (auto it = w.begin() + 2; it < w.end(); ++it) { 382 LIBSEMIGROUPS_ASSERT(*it < nr_generators()); 383 Swap()(this->to_external(_tmp_product), prod); 384 Product()(prod, 385 this->to_external_const(_tmp_product), 386 this->to_external_const(_gens[*it])); 387 } 388 return prod; 389 } 390 equal_to(word_type const & u,word_type const & v) const391 BOOL FROIDURE_PIN::equal_to(word_type const& u, word_type const& v) const { 392 element_index_type u_pos = word_to_pos(u); // validates u 393 element_index_type v_pos = word_to_pos(v); // validates v 394 if (finished() || (u_pos != UNDEFINED && v_pos != UNDEFINED)) { 395 LIBSEMIGROUPS_ASSERT(u_pos != UNDEFINED); 396 LIBSEMIGROUPS_ASSERT(v_pos != UNDEFINED); 397 return u_pos == v_pos; 398 } else { 399 element_type uu = word_to_element(u); 400 element_type vv = word_to_element(v); 401 auto res = (uu == vv); 402 this->external_free(uu); 403 this->external_free(vv); 404 return res; 405 } 406 } 407 current_max_word_length() const408 SIZE_T FROIDURE_PIN::current_max_word_length() const noexcept { 409 return _length[_enumerate_order.back()]; 410 } 411 degree() const412 SIZE_T FROIDURE_PIN::degree() const noexcept { 413 return _degree; 414 } 415 nr_generators() const416 SIZE_T FROIDURE_PIN::nr_generators() const noexcept { 417 return _gens.size(); 418 } 419 generator(letter_type pos) const420 CONST_REFERENCE FROIDURE_PIN::generator(letter_type pos) const { 421 validate_letter_index(pos); 422 return this->to_external_const(_gens[pos]); 423 } 424 current_position(const_reference x) const425 ELEMENT_INDEX_TYPE FROIDURE_PIN::current_position(const_reference x) const { 426 if (Degree()(this->to_internal_const(x)) != _degree) { 427 return UNDEFINED; 428 } 429 430 auto it = _map.find(this->to_internal_const(x)); 431 return (it == _map.end() ? UNDEFINED : it->second); 432 } 433 current_size() const434 SIZE_T FROIDURE_PIN::current_size() const noexcept { 435 return _elements.size(); 436 } 437 current_nr_rules() const438 SIZE_T FROIDURE_PIN::current_nr_rules() const noexcept { 439 return _nr_rules; 440 } 441 442 ELEMENT_INDEX_TYPE prefix(element_index_type pos) const443 FROIDURE_PIN::prefix(element_index_type pos) const { 444 validate_element_index(pos); 445 return _prefix[pos]; 446 } 447 448 ELEMENT_INDEX_TYPE suffix(element_index_type pos) const449 FROIDURE_PIN::suffix(element_index_type pos) const { 450 validate_element_index(pos); 451 return _suffix[pos]; 452 } 453 454 LETTER_TYPE first_letter(element_index_type pos) const455 FROIDURE_PIN::first_letter(element_index_type pos) const { 456 validate_element_index(pos); 457 return _first[pos]; 458 } 459 460 LETTER_TYPE final_letter(element_index_type pos) const461 FROIDURE_PIN::final_letter(element_index_type pos) const { 462 validate_element_index(pos); 463 return _final[pos]; 464 } 465 length_const(element_index_type pos) const466 SIZE_T FROIDURE_PIN::length_const(element_index_type pos) const { 467 validate_element_index(pos); 468 return _length[pos]; 469 } 470 length_non_const(element_index_type pos)471 SIZE_T FROIDURE_PIN::length_non_const(element_index_type pos) { 472 if (pos >= _nr) { 473 run(); 474 } 475 return length_const(pos); 476 } 477 478 ELEMENT_INDEX_TYPE product_by_reduction(element_index_type i,element_index_type j) const479 FROIDURE_PIN::product_by_reduction(element_index_type i, 480 element_index_type j) const { 481 validate_element_index(i); 482 validate_element_index(j); 483 484 if (length_const(i) <= length_const(j)) { 485 while (i != UNDEFINED) { 486 j = _left.get(j, _final[i]); 487 i = _prefix[i]; 488 } 489 return j; 490 } else { 491 while (j != UNDEFINED) { 492 i = _right.get(i, _first[j]); 493 j = _suffix[j]; 494 } 495 return i; 496 } 497 } 498 499 ELEMENT_INDEX_TYPE fast_product(element_index_type i,element_index_type j) const500 FROIDURE_PIN::fast_product(element_index_type i, element_index_type j) const { 501 validate_element_index(i); 502 validate_element_index(j); 503 if (length_const(i) 504 < 2 * Complexity()(this->to_external_const(_tmp_product)) 505 || length_const(j) 506 < 2 * Complexity()(this->to_external_const(_tmp_product))) { 507 return product_by_reduction(i, j); 508 } else { 509 Product()(this->to_external(_tmp_product), 510 this->to_external_const(_elements[i]), 511 this->to_external_const(_elements[j])); 512 return _map.find(_tmp_product)->second; 513 } 514 } 515 letter_to_pos(letter_type i) const516 ELEMENT_INDEX_TYPE FROIDURE_PIN::letter_to_pos(letter_type i) const { 517 validate_letter_index(i); 518 return _letter_to_pos[i]; 519 } 520 nr_idempotents()521 SIZE_T FROIDURE_PIN::nr_idempotents() { 522 init_idempotents(); 523 return _idempotents.size(); 524 } 525 is_idempotent(element_index_type pos)526 BOOL FROIDURE_PIN::is_idempotent(element_index_type pos) { 527 validate_element_index(pos); 528 init_idempotents(); 529 return _is_idempotent[pos]; 530 } 531 nr_rules()532 SIZE_T FROIDURE_PIN::nr_rules() { 533 run(); 534 return _nr_rules; 535 } 536 reserve(size_t n)537 VOID FROIDURE_PIN::reserve(size_t n) { 538 // Since the FroidurePin we are enumerating is bounded in size by the 539 // maximum value of an element_index_t, we cast the argument here to this 540 // integer type. 541 element_index_type nn = static_cast<element_index_type>(n); 542 _elements.reserve(nn); 543 _final.reserve(nn); 544 _first.reserve(nn); 545 _enumerate_order.reserve(nn); 546 _left.reserve(nn); 547 _length.reserve(nn); 548 549 // #ifdef LIBSEMIGROUPS_DENSEHASHMAP 550 // _map.resize(nn); 551 // #else 552 _map.reserve(nn); 553 // #endif 554 555 _prefix.reserve(nn); 556 _reduced.reserve(nn); 557 _right.reserve(nn); 558 _suffix.reserve(nn); 559 } 560 size()561 SIZE_T FROIDURE_PIN::size() { 562 run(); 563 return _elements.size(); 564 } 565 contains(const_reference x)566 BOOL FROIDURE_PIN::contains(const_reference x) { 567 return (position(x) != UNDEFINED); 568 } 569 position(const_reference x)570 ELEMENT_INDEX_TYPE FROIDURE_PIN::position(const_reference x) { 571 if (Degree()(x) != _degree) { 572 return UNDEFINED; 573 } 574 575 while (true) { 576 auto it = _map.find(this->to_internal_const(x)); 577 if (it != _map.end()) { 578 return it->second; 579 } 580 if (finished()) { 581 return UNDEFINED; 582 } 583 enumerate(_nr + 1); 584 // _nr + 1 means we run batch_size() more elements 585 } 586 } 587 sorted_position(const_reference x)588 ELEMENT_INDEX_TYPE FROIDURE_PIN::sorted_position(const_reference x) { 589 return position_to_sorted_position(position(x)); 590 } 591 592 ELEMENT_INDEX_TYPE position_to_sorted_position(element_index_type pos)593 FROIDURE_PIN::position_to_sorted_position(element_index_type pos) { 594 run(); 595 if (pos >= _nr) { 596 return UNDEFINED; 597 } 598 init_sorted(); 599 return _sorted[pos].second; 600 } 601 at(element_index_type pos)602 CONST_REFERENCE FROIDURE_PIN::at(element_index_type pos) { 603 enumerate(pos + 1); 604 return this->to_external_const(_elements.at(pos)); 605 } 606 operator [](element_index_type pos) const607 CONST_REFERENCE FROIDURE_PIN::operator[](element_index_type pos) const { 608 LIBSEMIGROUPS_ASSERT(pos < _elements.size()); 609 return this->to_external_const(_elements[pos]); 610 } 611 sorted_at(element_index_type pos)612 CONST_REFERENCE FROIDURE_PIN::sorted_at(element_index_type pos) { 613 init_sorted(); 614 return this->to_external_const(_sorted.at(pos).first); 615 } 616 right(element_index_type i,letter_type j)617 ELEMENT_INDEX_TYPE FROIDURE_PIN::right(element_index_type i, letter_type j) { 618 run(); 619 return _right.get(i, j); 620 } 621 right_cayley_graph()622 CAYLEY_GRAPH_TYPE const& FROIDURE_PIN::right_cayley_graph() { 623 run(); 624 _right.shrink_rows_to(size()); 625 return _right; 626 } 627 left(element_index_type i,letter_type j)628 ELEMENT_INDEX_TYPE FROIDURE_PIN::left(element_index_type i, letter_type j) { 629 run(); 630 return _left.get(i, j); 631 } 632 left_cayley_graph()633 CAYLEY_GRAPH_TYPE const& FROIDURE_PIN::left_cayley_graph() { 634 run(); 635 _left.shrink_rows_to(size()); 636 return _left; 637 } 638 minimal_factorisation(word_type & word,element_index_type pos)639 VOID FROIDURE_PIN::minimal_factorisation(word_type& word, 640 element_index_type pos) { 641 if (pos >= _nr && !finished()) { 642 enumerate(pos + 1); 643 } 644 validate_element_index(pos); 645 word.clear(); 646 while (pos != UNDEFINED) { 647 word.push_back(_first[pos]); 648 pos = _suffix[pos]; 649 } 650 } 651 652 WORD_TYPE minimal_factorisation(element_index_type pos)653 FROIDURE_PIN::minimal_factorisation(element_index_type pos) { 654 word_type word; 655 minimal_factorisation(word, pos); 656 return word; 657 } 658 minimal_factorisation(const_reference x)659 WORD_TYPE FROIDURE_PIN::minimal_factorisation(const_reference x) { 660 element_index_type pos = this->position(x); 661 if (pos == UNDEFINED) { 662 LIBSEMIGROUPS_EXCEPTION( 663 "the argument is not an element of the semigroup"); 664 } 665 return minimal_factorisation(pos); 666 } 667 factorisation(word_type & word,element_index_type pos)668 VOID FROIDURE_PIN::factorisation(word_type& word, element_index_type pos) { 669 minimal_factorisation(word, pos); 670 } 671 factorisation(element_index_type pos)672 WORD_TYPE FROIDURE_PIN::factorisation(element_index_type pos) { 673 return minimal_factorisation(pos); 674 } 675 factorisation(const_reference x)676 WORD_TYPE FROIDURE_PIN::factorisation(const_reference x) { 677 return minimal_factorisation(x); 678 } 679 reset_next_relation()680 VOID FROIDURE_PIN::reset_next_relation() noexcept { 681 _relation_pos = UNDEFINED; 682 _relation_gen = 0; 683 } 684 next_relation(word_type & relation)685 VOID FROIDURE_PIN::next_relation(word_type& relation) { 686 if (!finished()) { 687 run(); 688 } 689 690 relation.clear(); 691 692 if (_relation_pos == _nr) { // no more relations 693 return; 694 } 695 696 if (_relation_pos != UNDEFINED) { 697 while (_relation_pos < _nr) { 698 while (_relation_gen < _nrgens) { 699 if (!_reduced.get(_enumerate_order[_relation_pos], _relation_gen) 700 && (_relation_pos < _lenindex[1] 701 || _reduced.get(_suffix[_enumerate_order[_relation_pos]], 702 _relation_gen))) { 703 relation.push_back(_enumerate_order[_relation_pos]); 704 relation.push_back(_relation_gen); 705 relation.push_back( 706 _right.get(_enumerate_order[_relation_pos], _relation_gen)); 707 break; 708 } 709 _relation_gen++; 710 } 711 if (_relation_gen == _nrgens) { // then relation is empty 712 _relation_gen = 0; 713 _relation_pos++; 714 } else { 715 break; 716 } 717 } 718 _relation_gen++; 719 } else { 720 // duplicate generators 721 if (_relation_gen < _duplicate_gens.size()) { 722 relation.push_back(_duplicate_gens[_relation_gen].first); 723 relation.push_back(_duplicate_gens[_relation_gen].second); 724 _relation_gen++; 725 } else { 726 _relation_gen = 0; 727 _relation_pos++; 728 next_relation(relation); 729 } 730 } 731 } 732 enumerate(size_t limit)733 VOID FROIDURE_PIN::enumerate(size_t limit) { 734 if (finished() || limit <= current_size()) { 735 return; 736 } else if (LIMIT_MAX - batch_size() > current_size()) { 737 limit = std::max(limit, current_size() + batch_size()); 738 } else { // batch_size() is very big for some reason 739 limit = batch_size(); 740 } 741 REPORT_DEFAULT("limit = %llu (%s)\n", limit, __func__); 742 run_until([this, &limit]() -> bool { return current_size() >= limit; }); 743 } 744 run_impl()745 VOID FROIDURE_PIN::run_impl() { 746 std::lock_guard<std::mutex> lg(_mtx); 747 if (_pos >= _nr) { 748 return; 749 } 750 751 detail::Timer timer; 752 size_t tid = THREAD_ID_MANAGER.tid(std::this_thread::get_id()); 753 754 // product the generators by every generator 755 if (_pos < _lenindex[1]) { 756 size_type nr_shorter_elements = _nr; 757 while (_pos < _lenindex[1]) { 758 element_index_type i = _enumerate_order[_pos]; 759 for (letter_type j = 0; j != _nrgens; ++j) { 760 Product()(this->to_external(_tmp_product), 761 this->to_external_const(_elements[i]), 762 this->to_external_const(_gens[j]), 763 tid); 764 765 #ifdef LIBSEMIGROUPS_VERBOSE 766 _nr_products++; 767 #endif 768 auto it = _map.find(_tmp_product); 769 770 if (it != _map.end()) { 771 _right.set(i, j, it->second); 772 _nr_rules++; 773 } else { 774 is_one(_tmp_product, _nr); 775 _elements.push_back(this->internal_copy(_tmp_product)); 776 _first.push_back(_first[i]); 777 _final.push_back(j); 778 _enumerate_order.push_back(_nr); 779 _length.push_back(2); 780 _map.emplace(_elements.back(), _nr); 781 _prefix.push_back(i); 782 _reduced.set(i, j, true); 783 _right.set(i, j, _nr); 784 _suffix.push_back(_letter_to_pos[j]); 785 _nr++; 786 } 787 } 788 _pos++; 789 } 790 for (enumerate_index_type i = 0; i != _pos; ++i) { 791 letter_type b = _final[_enumerate_order[i]]; 792 for (letter_type j = 0; j != _nrgens; ++j) { 793 _left.set(_enumerate_order[i], j, _right.get(_letter_to_pos[j], b)); 794 } 795 } 796 _wordlen++; 797 expand(_nr - nr_shorter_elements); 798 _lenindex.push_back(_enumerate_order.size()); 799 } 800 801 // Multiply the words of length > 1 by every generator 802 while (_pos != _nr && !stopped()) { 803 size_type nr_shorter_elements = _nr; 804 while (_pos != _lenindex[_wordlen + 1] && !stopped()) { 805 element_index_type i = _enumerate_order[_pos]; 806 letter_type b = _first[i]; 807 element_index_type s = _suffix[i]; 808 for (letter_type j = 0; j != _nrgens; ++j) { 809 if (!_reduced.get(s, j)) { 810 element_index_type r = _right.get(s, j); 811 if (_found_one && r == _pos_one) { 812 _right.set(i, j, _letter_to_pos[b]); 813 } else if (_prefix[r] != UNDEFINED) { // r is not a generator 814 _right.set(i, j, _right.get(_left.get(_prefix[r], b), _final[r])); 815 } else { 816 _right.set(i, j, _right.get(_letter_to_pos[b], _final[r])); 817 } 818 } else { 819 Product()(this->to_external(_tmp_product), 820 this->to_external_const(_elements[i]), 821 this->to_external_const(_gens[j]), 822 tid); 823 #ifdef LIBSEMIGROUPS_VERBOSE 824 _nr_products++; 825 #endif 826 auto it = _map.find(_tmp_product); 827 828 if (it != _map.end()) { 829 _right.set(i, j, it->second); 830 _nr_rules++; 831 } else { 832 is_one(_tmp_product, _nr); 833 _elements.push_back(this->internal_copy(_tmp_product)); 834 _first.push_back(b); 835 _final.push_back(j); 836 _length.push_back(_wordlen + 2); 837 _map.emplace(_elements.back(), _nr); 838 _prefix.push_back(i); 839 _reduced.set(i, j, true); 840 _right.set(i, j, _nr); 841 _suffix.push_back(_right.get(s, j)); 842 _enumerate_order.push_back(_nr); 843 _nr++; 844 } 845 } 846 } // finished applying gens to <_elements.at(_pos)> 847 _pos++; 848 } // finished words of length <wordlen> + 1 849 expand(_nr - nr_shorter_elements); 850 851 if (_pos > _nr || _pos == _lenindex[_wordlen + 1]) { 852 for (enumerate_index_type i = _lenindex[_wordlen]; i != _pos; ++i) { 853 element_index_type p = _prefix[_enumerate_order[i]]; 854 letter_type b = _final[_enumerate_order[i]]; 855 for (letter_type j = 0; j != _nrgens; ++j) { 856 _left.set(_enumerate_order[i], j, _right.get(_left.get(p, j), b)); 857 } 858 } 859 _wordlen++; 860 _lenindex.push_back(_enumerate_order.size()); 861 } 862 REPORT_DEFAULT("found %d elements, %d rules, %d max word length\n", 863 _nr, 864 _nr_rules, 865 current_max_word_length()); 866 } 867 REPORT_TIME(timer); 868 report_why_we_stopped(); 869 #ifdef LIBSEMIGROUPS_VERBOSE 870 REPORT_DEFAULT("number of products = %llu\n", _nr_products); 871 #endif 872 } 873 finished_impl() const874 BOOL FROIDURE_PIN::finished_impl() const { 875 return _pos >= _nr; 876 } 877 add_generator(element_type const & x)878 VOID FROIDURE_PIN::add_generator(element_type const& x) { 879 add_generators({x}); 880 } 881 882 TEMPLATE 883 template <typename TCollection> add_generators(TCollection const & coll)884 void FROIDURE_PIN::add_generators(TCollection const& coll) { 885 static_assert(!std::is_pointer<TCollection>::value, 886 "TCollection should not be a pointer"); 887 if (immutable()) { 888 LIBSEMIGROUPS_EXCEPTION("cannot add generators, the FroidurePin instance " 889 "has been set to immutable"); 890 } else if (coll.size() == 0) { 891 return; 892 } 893 for (auto it = coll.begin(); it < coll.end(); ++it) { 894 element_index_type degree = Degree()(*it); 895 if (degree != _degree) { 896 LIBSEMIGROUPS_EXCEPTION( 897 "new generator %llu has degree %llu but should have degree %llu", 898 it - coll.begin(), 899 degree, 900 _degree); 901 } 902 } 903 detail::Timer timer; 904 size_t tid = THREAD_ID_MANAGER.tid(std::this_thread::get_id()); 905 906 // get some parameters from the old semigroup 907 letter_type old_nrgens = _nrgens; 908 size_type old_nr = _nr; 909 size_type nr_old_left = _pos; 910 911 // erase the old index 912 _enumerate_order.erase(_enumerate_order.begin() + _lenindex[1], 913 _enumerate_order.end()); 914 915 // old_new[i] indicates if we have seen _elements.at(i) yet in new. 916 std::vector<bool> old_new; 917 old_new.clear(); 918 old_new.resize(old_nr, false); 919 for (letter_type i = 0; i < _letter_to_pos.size(); i++) { 920 old_new[_letter_to_pos[i]] = true; 921 } 922 923 // add the new generators to new _gens, _elements, and _enumerate_order 924 for (const_reference x : coll) { 925 auto it = _map.find(this->to_internal_const(x)); 926 if (it == _map.end()) { // new generator 927 _gens.push_back(this->internal_copy(this->to_internal_const(x))); 928 _elements.push_back(_gens.back()); 929 _map.emplace(_gens.back(), _nr); 930 931 _first.push_back(_gens.size() - 1); 932 _final.push_back(_gens.size() - 1); 933 934 _letter_to_pos.push_back(_nr); 935 _enumerate_order.push_back(_nr); 936 937 is_one(this->to_internal_const(x), _nr); 938 _prefix.push_back(UNDEFINED); 939 _suffix.push_back(UNDEFINED); 940 _length.push_back(1); 941 _nr++; 942 } else if (_letter_to_pos[_first[it->second]] == it->second) { 943 _gens.push_back(this->internal_copy(this->to_internal_const(x))); 944 // x is one of the existing generators 945 _duplicate_gens.push_back( 946 std::make_pair(_gens.size() - 1, _first[it->second])); 947 // _gens[_gens.size() - 1] = _gens[_first[it->second])] 948 // since _first maps element_index_type -> letter_type 949 _letter_to_pos.push_back(it->second); 950 } else { 951 // x is an old element that will now be a generator 952 _gens.push_back(_elements[it->second]); 953 _letter_to_pos.push_back(it->second); 954 _enumerate_order.push_back(it->second); 955 956 _first[it->second] = _gens.size() - 1; 957 _final[it->second] = _gens.size() - 1; 958 _prefix[it->second] = UNDEFINED; 959 _suffix[it->second] = UNDEFINED; 960 _length[it->second] = UNDEFINED; 961 962 old_new[it->second] = true; 963 } 964 } 965 966 // reset the data structure 967 _idempotents_found = false; 968 _nr_rules = _duplicate_gens.size(); 969 _pos = 0; 970 _wordlen = 0; 971 _nrgens = _gens.size(); 972 _lenindex.clear(); 973 _lenindex.push_back(0); 974 _lenindex.push_back(_nrgens - _duplicate_gens.size()); 975 976 // Add columns for new generators 977 _reduced = detail::DynamicArray2<bool>( 978 _nrgens, _reduced.nr_rows() + _nrgens - old_nrgens); 979 _left.add_cols(_nrgens - _left.nr_cols()); 980 _right.add_cols(_nrgens - _right.nr_cols()); 981 982 // Add rows in for newly added generators 983 _left.add_rows(_nrgens - old_nrgens); 984 _right.add_rows(_nrgens - old_nrgens); 985 986 size_type nr_shorter_elements; 987 988 // Repeat until we have multiplied all of the elements of <old> up to the 989 // old value of _pos by all of the (new and old) generators 990 991 while (nr_old_left > 0) { 992 nr_shorter_elements = _nr; 993 while (_pos < _lenindex[_wordlen + 1] && nr_old_left > 0) { 994 element_index_type i = _enumerate_order[_pos]; // position in _elements 995 letter_type b = _first[i]; 996 element_index_type s = _suffix[i]; 997 if (_right.get(i, 0) != UNDEFINED) { 998 nr_old_left--; 999 // _elements[i] is in old semigroup, and its descendants are 1000 // known 1001 for (letter_type j = 0; j < old_nrgens; j++) { 1002 element_index_type k = _right.get(i, j); 1003 if (!old_new[k]) { // it's new! 1004 is_one(_elements[k], k); 1005 _first[k] = _first[i]; 1006 _final[k] = j; 1007 _length[k] = _wordlen + 2; 1008 _prefix[k] = i; 1009 _reduced.set(i, j, true); 1010 if (_wordlen == 0) { 1011 _suffix[k] = _letter_to_pos[j]; 1012 } else { 1013 _suffix[k] = _right.get(s, j); 1014 } 1015 _enumerate_order.push_back(k); 1016 old_new[k] = true; 1017 } else if (s == UNDEFINED || _reduced.get(s, j)) { 1018 // this clause could be removed if _nr_rules wasn't necessary 1019 _nr_rules++; 1020 } 1021 } 1022 for (letter_type j = old_nrgens; j < _nrgens; j++) { 1023 closure_update(i, j, b, s, old_nr, tid, old_new); 1024 } 1025 } else { 1026 // _elements[i] is either not in old, or it is in old but its 1027 // descendants are not known 1028 for (letter_type j = 0; j < _nrgens; j++) { 1029 closure_update(i, j, b, s, old_nr, tid, old_new); 1030 } 1031 } 1032 _pos++; 1033 } // finished words of length <wordlen> + 1 1034 1035 expand(_nr - nr_shorter_elements); 1036 if (_pos > _nr || _pos == _lenindex[_wordlen + 1]) { 1037 if (_wordlen == 0) { 1038 for (enumerate_index_type i = 0; i < _pos; i++) { 1039 size_t b = _final[_enumerate_order[i]]; 1040 for (letter_type j = 0; j < _nrgens; j++) { 1041 // TODO(JDM) reuse old info here! 1042 _left.set( 1043 _enumerate_order[i], j, _right.get(_letter_to_pos[j], b)); 1044 } 1045 } 1046 } else { 1047 for (enumerate_index_type i = _lenindex[_wordlen]; i < _pos; i++) { 1048 element_index_type p = _prefix[_enumerate_order[i]]; 1049 letter_type b = _final[_enumerate_order[i]]; 1050 for (letter_type j = 0; j < _nrgens; j++) { 1051 // TODO(JDM) reuse old info here! 1052 _left.set(_enumerate_order[i], j, _right.get(_left.get(p, j), b)); 1053 } 1054 } 1055 } 1056 _lenindex.push_back(_enumerate_order.size()); 1057 _wordlen++; 1058 } 1059 REPORT_DEFAULT("found %d elements, %d rules, %d max word length\n", 1060 _nr, 1061 _nr_rules, 1062 current_max_word_length()); 1063 } 1064 if (started()) { 1065 REPORT_TIME(timer); 1066 } 1067 report_why_we_stopped(); 1068 } 1069 1070 VOID add_generators(std::initializer_list<const_element_type> coll)1071 FROIDURE_PIN::add_generators(std::initializer_list<const_element_type> coll) { 1072 add_generators<std::initializer_list<const_element_type>>(coll); 1073 } 1074 1075 TEMPLATE 1076 template <typename TCollection> 1077 FROIDURE_PIN* copy_add_generators(TCollection const & coll) const1078 FROIDURE_PIN::copy_add_generators(TCollection const& coll) const { 1079 static_assert(!std::is_pointer<TCollection>::value, 1080 "TCollection should not be a pointer"); 1081 if (coll.size() == 0) { 1082 return new FroidurePin(*this); 1083 } else { 1084 // Partially copy 1085 FroidurePin* out = new FroidurePin(*this, &coll); 1086 out->add_generators(coll); 1087 return out; 1088 } 1089 } 1090 1091 TEMPLATE 1092 template <typename TCollection> closure(TCollection const & coll)1093 void FROIDURE_PIN::closure(TCollection const& coll) { 1094 static_assert(!std::is_pointer<TCollection>::value, 1095 "TCollection should not be a pointer"); 1096 if (coll.size() == 0) { 1097 return; 1098 } else { 1099 for (const_reference x : coll) { 1100 if (!contains(x)) { 1101 add_generators({x}); 1102 } 1103 } 1104 } 1105 } 1106 closure(std::initializer_list<const_element_type> coll)1107 VOID FROIDURE_PIN::closure(std::initializer_list<const_element_type> coll) { 1108 closure<std::initializer_list<const_element_type>>(coll); 1109 } 1110 1111 TEMPLATE 1112 template <typename TCollection> copy_closure(TCollection const & coll)1113 FROIDURE_PIN* FROIDURE_PIN::copy_closure(TCollection const& coll) { 1114 static_assert(!std::is_pointer<TCollection>::value, 1115 "TCollection should not be a pointer"); 1116 if (coll.size() == 0) { 1117 return new FroidurePin(*this); 1118 } else { 1119 // The next line is required so that when we call the closure member 1120 // function on out, the partial copy contains enough information to all 1121 // membership testing without a call to run (which will fail because 1122 // the partial copy does not contain enough data to run run). 1123 this->run(); 1124 // Partially copy 1125 FroidurePin* out = new FroidurePin(*this, &coll); 1126 out->closure(coll); 1127 return out; 1128 } 1129 } 1130 is_monoid()1131 BOOL FROIDURE_PIN::is_monoid() { 1132 run(); 1133 return _found_one; 1134 } 1135 1136 // More specialisations below in kbe.*pp and element.*pp is_finite()1137 TRIL FROIDURE_PIN::is_finite() { 1138 return tril::TRUE; 1139 } 1140 1141 //////////////////////////////////////////////////////////////////////// 1142 // FroidurePin - validation member functions - private 1143 //////////////////////////////////////////////////////////////////////// 1144 validate_element_index(element_index_type i) const1145 VOID FROIDURE_PIN::validate_element_index(element_index_type i) const { 1146 if (i >= _nr) { 1147 LIBSEMIGROUPS_EXCEPTION( 1148 "element index out of bounds, expected value in [0, %d), got %d", 1149 _nr, 1150 i); 1151 } 1152 } 1153 validate_letter_index(letter_type i) const1154 VOID FROIDURE_PIN::validate_letter_index(letter_type i) const { 1155 if (i >= nr_generators()) { 1156 LIBSEMIGROUPS_EXCEPTION( 1157 "generator index out of bounds, expected value in [0, %d), got %d", 1158 nr_generators(), 1159 i); 1160 } 1161 } 1162 1163 //////////////////////////////////////////////////////////////////////// 1164 // FroidurePin - enumeration member functions - private 1165 //////////////////////////////////////////////////////////////////////// 1166 1167 // Expand the data structures in the semigroup with space for nr elements expand(size_type nr)1168 INLINE_VOID FROIDURE_PIN::expand(size_type nr) { 1169 _left.add_rows(nr); 1170 _reduced.add_rows(nr); 1171 _right.add_rows(nr); 1172 } 1173 1174 // Check if an element is the identity, x should be in the position pos 1175 // of _elements. is_one(internal_const_element_type x,element_index_type pos)1176 INLINE_VOID FROIDURE_PIN::is_one( 1177 internal_const_element_type x, 1178 element_index_type 1179 pos) noexcept(std::is_nothrow_default_constructible<InternalEqualTo>:: 1180 value&& noexcept( 1181 std::declval<InternalEqualTo>()(x, x))) { 1182 if (!_found_one && InternalEqualTo()(x, _id)) { 1183 _pos_one = pos; 1184 _found_one = true; 1185 } 1186 } 1187 1188 // _nrgens, _duplicates_gens, _letter_to_pos, and _elements must all be 1189 // initialised for this to work, and _gens must point to an empty vector. copy_gens()1190 VOID FROIDURE_PIN::copy_gens() { 1191 LIBSEMIGROUPS_ASSERT(_gens.empty()); 1192 _gens.resize(_nrgens); 1193 std::vector<bool> seen(_nrgens, false); 1194 // really copy duplicate gens from _elements 1195 for (std::pair<letter_type, letter_type> const& x : _duplicate_gens) { 1196 // The degree of everything in _elements has already been increased (if 1197 // it needs to be at all), and so we do not need to increase the degree 1198 // in the copy below. 1199 _gens[x.first] = this->internal_copy(_elements[_letter_to_pos[x.second]]); 1200 seen[x.first] = true; 1201 } 1202 // the non-duplicate gens are already in _elements, so don't really copy 1203 for (letter_type i = 0; i < _nrgens; i++) { 1204 if (!seen[i]) { 1205 _gens[i] = _elements[_letter_to_pos[i]]; 1206 } 1207 } 1208 } 1209 closure_update(element_index_type i,letter_type j,letter_type b,element_index_type s,size_type old_nr,size_t const & tid,std::vector<bool> & old_new)1210 VOID FROIDURE_PIN::closure_update(element_index_type i, 1211 letter_type j, 1212 letter_type b, 1213 element_index_type s, 1214 size_type old_nr, 1215 size_t const& tid, 1216 std::vector<bool>& old_new) { 1217 if (_wordlen != 0 && !_reduced.get(s, j)) { 1218 element_index_type r = _right.get(s, j); 1219 if (_found_one && r == _pos_one) { 1220 _right.set(i, j, _letter_to_pos[b]); 1221 } else if (_prefix[r] != UNDEFINED) { 1222 _right.set(i, j, _right.get(_left.get(_prefix[r], b), _final[r])); 1223 } else { 1224 _right.set(i, j, _right.get(_letter_to_pos[b], _final[r])); 1225 } 1226 } else { 1227 Product()(this->to_external(_tmp_product), 1228 this->to_external_const(_elements[i]), 1229 this->to_external_const(_gens[j]), 1230 tid); 1231 auto it = _map.find(_tmp_product); 1232 if (it == _map.end()) { // it's new! 1233 is_one(_tmp_product, _nr); 1234 _elements.push_back(this->internal_copy(_tmp_product)); 1235 _first.push_back(b); 1236 _final.push_back(j); 1237 _length.push_back(_wordlen + 2); 1238 _map.emplace(_elements.back(), _nr); 1239 _prefix.push_back(i); 1240 _reduced.set(i, j, true); 1241 _right.set(i, j, _nr); 1242 if (_wordlen == 0) { 1243 _suffix.push_back(_letter_to_pos[j]); 1244 } else { 1245 _suffix.push_back(_right.get(s, j)); 1246 } 1247 _enumerate_order.push_back(_nr); 1248 _nr++; 1249 } else if (it->second < old_nr && !old_new[it->second]) { 1250 // we didn't process it yet! 1251 is_one(_tmp_product, it->second); 1252 _first[it->second] = b; 1253 _final[it->second] = j; 1254 _length[it->second] = _wordlen + 2; 1255 _prefix[it->second] = i; 1256 _reduced.set(i, j, true); 1257 _right.set(i, j, it->second); 1258 if (_wordlen == 0) { 1259 _suffix[it->second] = _letter_to_pos[j]; 1260 } else { 1261 _suffix[it->second] = _right.get(s, j); 1262 } 1263 _enumerate_order.push_back(it->second); 1264 old_new[it->second] = true; 1265 } else { // it->second >= old->_nr || old_new[it->second] 1266 // it's old 1267 _right.set(i, j, it->second); 1268 _nr_rules++; 1269 } 1270 } 1271 } 1272 1273 //////////////////////////////////////////////////////////////////////// 1274 // FroidurePin - initialisation member functions - private 1275 //////////////////////////////////////////////////////////////////////// 1276 1277 // Initialise the data member _sorted. We store a list of pairs consisting 1278 // of an internal_element_type and element_index_type which is sorted on the 1279 // first entry using the operator< of the Element class. The second 1280 // component is then inverted (as a permutation) so that we can then find 1281 // the position of an element in the sorted list of elements. init_sorted()1282 VOID FROIDURE_PIN::init_sorted() { 1283 if (_sorted.size() == size()) { 1284 return; 1285 } 1286 size_t n = size(); 1287 _sorted.reserve(n); 1288 for (element_index_type i = 0; i < n; i++) { 1289 _sorted.emplace_back(_elements[i], i); 1290 } 1291 std::sort( 1292 _sorted.begin(), 1293 _sorted.end(), 1294 [this](std::pair<internal_element_type, element_index_type> const& x, 1295 std::pair<internal_element_type, element_index_type> const& y) 1296 -> bool { 1297 return Less()(this->to_external_const(x.first), 1298 this->to_external_const(y.first)); 1299 }); 1300 1301 // Invert the permutation in _sorted[*].second 1302 std::vector<element_index_type> tmp_inverter; 1303 tmp_inverter.resize(n); 1304 for (element_index_type i = 0; i < n; i++) { 1305 tmp_inverter[_sorted[i].second] = i; 1306 } 1307 for (element_index_type i = 0; i < n; i++) { 1308 _sorted[i].second = tmp_inverter[i]; 1309 } 1310 } 1311 1312 // Find the idempotents and store their pointers and positions in a 1313 // std::pair of type internal_idempotent_pair. init_idempotents()1314 VOID FROIDURE_PIN::init_idempotents() { 1315 if (_idempotents_found) { 1316 return; 1317 } 1318 _idempotents_found = true; 1319 run(); 1320 _is_idempotent.resize(_nr, false); 1321 1322 detail::Timer timer; 1323 1324 // Find the threshold beyond which it is quicker to simply product 1325 // elements rather than follow a path in the Cayley graph. This is the 1326 // enumerate_index_t i for which length(i) >= complexity. 1327 size_t cmplxty = std::max( 1328 size_t{Complexity()(this->to_external_const(_tmp_product)) / 2}, 1329 size_t{1}); 1330 LIBSEMIGROUPS_ASSERT(_lenindex.size() > 1); 1331 // threshold_length = the min. length of a word which is >= complexity. 1332 // if a word has length strictly greater than threshold_length, then we 1333 // multiply, otherwise we trace in the Cayley graph. 1334 size_t threshold_length = std::min(cmplxty, current_max_word_length()); 1335 LIBSEMIGROUPS_ASSERT(threshold_length < _lenindex.size()); 1336 1337 enumerate_index_type threshold_index = _lenindex.at(threshold_length); 1338 1339 size_t total_load = 0; 1340 for (size_t i = 1; i <= threshold_length; ++i) { 1341 // _lenindex[i - 1] is the element_index_t where words of length i begin 1342 // so _lenindex[i] - _lenindex[i - 1]) is the number of words of length 1343 // i. 1344 total_load += i * (_lenindex[i] - _lenindex[i - 1]); 1345 } 1346 1347 REPORT_VERBOSE_DEFAULT("When finding the number of idempotents . . ."); 1348 REPORT_VERBOSE_DEFAULT( 1349 "complexity of multiplication %*s = %llu\n", 11, " ", cmplxty); 1350 REPORT_VERBOSE_DEFAULT( 1351 "multiple words longer than %*s = %llu\n", 13, " ", threshold_length); 1352 REPORT_VERBOSE_DEFAULT( 1353 "number of paths traced in Cayley graph %*s = %llu\n", 1354 0, 1355 " ", 1356 threshold_index); 1357 REPORT_VERBOSE_DEFAULT( 1358 "mean path length %*s = %llu\n", 23, " ", total_load / threshold_index); 1359 REPORT_VERBOSE_DEFAULT( 1360 "number of products %*s = %llu\n", 21, " ", _nr - threshold_index); 1361 1362 // _lenindex.at(threshold_length) is the element_index_type where words of 1363 // length (threshold_length + 1) begin 1364 LIBSEMIGROUPS_ASSERT(_nr >= _lenindex.at(threshold_length)); 1365 total_load += cmplxty * (_nr - _lenindex.at(threshold_length)); 1366 size_t const N = max_threads(); 1367 LIBSEMIGROUPS_ASSERT(N != 0); 1368 1369 if (N == 1 || size() < concurrency_threshold()) { 1370 // Use only 1 thread 1371 idempotents(0, _nr, threshold_index, _idempotents); 1372 } else { 1373 // Use > 1 threads 1374 size_t mean_load = total_load / N; 1375 size_t len = 1; 1376 std::vector<enumerate_index_type> first(N, 0); 1377 std::vector<enumerate_index_type> last(N, _nr); 1378 std::vector<std::vector<internal_idempotent_pair>> tmp( 1379 N, std::vector<internal_idempotent_pair>()); 1380 std::vector<std::thread> threads; 1381 THREAD_ID_MANAGER.reset(); 1382 1383 for (size_t i = 0; i < N - 1; i++) { 1384 size_t thread_load = 0; 1385 last[i] = first[i]; 1386 while (thread_load < mean_load && last[i] < threshold_index) { 1387 if (last[i] >= _lenindex[len]) { 1388 ++len; 1389 } 1390 thread_load += len; 1391 ++last[i]; 1392 } 1393 while (thread_load < mean_load) { 1394 thread_load += cmplxty; 1395 ++last[i]; 1396 } 1397 total_load -= thread_load; 1398 REPORT_DEFAULT("thread %d has load %d\n", i + 1, thread_load); 1399 first[i + 1] = last[i]; 1400 1401 threads.emplace_back(&FroidurePin::idempotents, 1402 this, 1403 first[i], 1404 last[i], 1405 threshold_index, 1406 std::ref(tmp[i])); 1407 } 1408 1409 REPORT_DEFAULT("thread %d has load %d\n", N, total_load); 1410 threads.emplace_back(&FroidurePin::idempotents, 1411 this, 1412 first[N - 1], 1413 last[N - 1], 1414 threshold_index, 1415 std::ref(tmp[N - 1])); 1416 1417 size_t nr_idempotents = 0; 1418 for (size_t i = 0; i < N; i++) { 1419 threads[i].join(); 1420 nr_idempotents += tmp[i].size(); 1421 } 1422 _idempotents.reserve(nr_idempotents); 1423 for (size_t i = 0; i < N; i++) { 1424 std::copy( 1425 tmp[i].begin(), tmp[i].end(), std::back_inserter(_idempotents)); 1426 } 1427 } 1428 REPORT_TIME(timer); 1429 } 1430 1431 // Find the idempotents in the range [first, last) and store 1432 // the corresponding std::pair of type internal_idempotent_pair in the 4th 1433 // parameter. The parameter threshold is the point, calculated in 1434 // init_idempotents, at which it is better to simply product elements 1435 // rather than trace in the left/right Cayley graph. idempotents(enumerate_index_type const first,enumerate_index_type const last,enumerate_index_type const threshold,std::vector<internal_idempotent_pair> & idempotents)1436 VOID FROIDURE_PIN::idempotents( 1437 enumerate_index_type const first, 1438 enumerate_index_type const last, 1439 enumerate_index_type const threshold, 1440 std::vector<internal_idempotent_pair>& idempotents) { 1441 REPORT_DEFAULT( 1442 "first = %d, last = %d, diff = %d\n", first, last, last - first); 1443 detail::Timer timer; 1444 1445 enumerate_index_type pos = first; 1446 1447 for (; pos < std::min(threshold, last); pos++) { 1448 element_index_type k = _enumerate_order[pos]; 1449 if (!_is_idempotent[k]) { 1450 // The following is product_by_reduction, don't have to consider 1451 // lengths because they are equal!! 1452 element_index_type i = k, j = k; 1453 while (j != UNDEFINED) { 1454 i = _right.get(i, _first[j]); 1455 // TODO(later) improve this if R/L-classes are known to stop 1456 // performing the product if we fall out of the R/L-class of the 1457 // initial element. 1458 j = _suffix[j]; 1459 } 1460 if (i == k) { 1461 idempotents.emplace_back(_elements[k], k); 1462 _is_idempotent[k] = true; 1463 } 1464 } 1465 } 1466 1467 if (pos >= last) { 1468 REPORT_TIME(timer); 1469 return; 1470 } 1471 1472 // Cannot use _tmp_product itself since there are multiple threads here! 1473 internal_element_type tmp_product = this->internal_copy(_tmp_product); 1474 size_t tid = THREAD_ID_MANAGER.tid(std::this_thread::get_id()); 1475 1476 for (; pos < last; pos++) { 1477 element_index_type k = _enumerate_order[pos]; 1478 if (!_is_idempotent[k]) { 1479 Product()(this->to_external(tmp_product), 1480 this->to_external(_elements[k]), 1481 this->to_external(_elements[k]), 1482 tid); 1483 if (InternalEqualTo()(tmp_product, _elements[k])) { 1484 idempotents.emplace_back(_elements[k], k); 1485 _is_idempotent[k] = true; 1486 } 1487 } 1488 } 1489 this->internal_free(tmp_product); 1490 REPORT_TIME(timer); 1491 } 1492 1493 //////////////////////////////////////////////////////////////////////// 1494 // FroidurePin - iterators - public 1495 //////////////////////////////////////////////////////////////////////// 1496 1497 TEMPLATE cbegin() const1498 typename FROIDURE_PIN::const_iterator FROIDURE_PIN::cbegin() const { 1499 return const_iterator(_elements.cbegin()); 1500 } 1501 1502 TEMPLATE begin() const1503 typename FROIDURE_PIN::const_iterator FROIDURE_PIN::begin() const { 1504 return cbegin(); 1505 } 1506 1507 TEMPLATE cend() const1508 typename FROIDURE_PIN::const_iterator FROIDURE_PIN::cend() const { 1509 return const_iterator(_elements.cend()); 1510 } 1511 1512 TEMPLATE end() const1513 typename FROIDURE_PIN::const_iterator FROIDURE_PIN::end() const { 1514 return cend(); 1515 } 1516 1517 TEMPLATE crbegin() const1518 typename FROIDURE_PIN::const_reverse_iterator FROIDURE_PIN::crbegin() const { 1519 return const_reverse_iterator(cend()); 1520 } 1521 1522 TEMPLATE crend() const1523 typename FROIDURE_PIN::const_reverse_iterator FROIDURE_PIN::crend() const { 1524 return const_reverse_iterator(cbegin()); 1525 } 1526 1527 TEMPLATE cbegin_sorted()1528 typename FROIDURE_PIN::const_iterator_sorted FROIDURE_PIN::cbegin_sorted() { 1529 init_sorted(); 1530 return const_iterator_pair_first(_sorted.cbegin()); 1531 } 1532 1533 TEMPLATE cend_sorted()1534 typename FROIDURE_PIN::const_iterator_sorted FROIDURE_PIN::cend_sorted() { 1535 init_sorted(); 1536 return const_iterator_pair_first(_sorted.cend()); 1537 } 1538 1539 TEMPLATE 1540 typename FROIDURE_PIN::const_reverse_iterator_sorted crbegin_sorted()1541 FROIDURE_PIN::crbegin_sorted() { 1542 init_sorted(); 1543 return const_reverse_iterator_pair_first(cend_sorted()); 1544 } 1545 1546 TEMPLATE 1547 typename FROIDURE_PIN::const_reverse_iterator_sorted crend_sorted()1548 FROIDURE_PIN::crend_sorted() { 1549 init_sorted(); 1550 return const_reverse_iterator_pair_first(cbegin_sorted()); 1551 } 1552 1553 TEMPLATE 1554 typename FROIDURE_PIN::const_iterator_idempotents cbegin_idempotents()1555 FROIDURE_PIN::cbegin_idempotents() { 1556 init_idempotents(); 1557 return const_iterator_pair_first(_idempotents.cbegin()); 1558 } 1559 1560 TEMPLATE 1561 typename FROIDURE_PIN::const_iterator_idempotents cend_idempotents()1562 FROIDURE_PIN::cend_idempotents() { 1563 init_idempotents(); 1564 return const_iterator_pair_first(_idempotents.cend()); 1565 } 1566 1567 //////////////////////////////////////////////////////////////////////// 1568 // FroidurePin - iterators - private 1569 //////////////////////////////////////////////////////////////////////// 1570 1571 TEMPLATE 1572 struct FROIDURE_PIN::DerefPairFirst : detail::BruidhinnTraits<TElementType> { 1573 const_reference operator ()libsemigroups::FROIDURE_PIN::DerefPairFirst1574 operator()(typename std::vector< 1575 std::pair<internal_element_type, 1576 element_index_type>>::const_iterator const& it) const { 1577 return this->to_external_const((*it).first); 1578 } 1579 }; 1580 1581 TEMPLATE 1582 struct FROIDURE_PIN::AddressOfPairFirst 1583 : detail::BruidhinnTraits<TElementType> { 1584 const_pointer operator ()libsemigroups::FROIDURE_PIN::AddressOfPairFirst1585 operator()(typename std::vector< 1586 std::pair<internal_element_type, 1587 element_index_type>>::const_iterator const& it) const { 1588 return &(this->to_external_const((*it).first)); 1589 } 1590 }; 1591 1592 TEMPLATE 1593 struct FROIDURE_PIN::IteratorPairFirstTraits 1594 : detail::ConstIteratorTraits< 1595 std::vector<std::pair<internal_element_type, element_index_type>>> { 1596 using value_type = 1597 typename FroidurePin<TElementType, TTraits>::element_type; 1598 using const_reference = 1599 typename FroidurePin<TElementType, TTraits>::const_reference; 1600 using const_pointer = 1601 typename FroidurePin<TElementType, TTraits>::const_pointer; 1602 1603 using Deref = DerefPairFirst; 1604 using AddressOf = AddressOfPairFirst; 1605 }; 1606 1607 } // namespace libsemigroups 1608 #endif // LIBSEMIGROUPS_INCLUDE_FROIDURE_PIN_IMPL_HPP_ 1609