// // libsemigroups - C++ library for semigroups and monoids // Copyright (C) 2019 James D. Mitchell // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // #include "libsemigroups/froidure-pin-base.hpp" #include #include "libsemigroups/libsemigroups-exception.hpp" #include "libsemigroups/report.hpp" namespace libsemigroups { using element_index_type = FroidurePinBase::element_index_type; //////////////////////////////////////////////////////////////////////// // FroidurePinBase - constructors and destructor - public //////////////////////////////////////////////////////////////////////// FroidurePinBase::FroidurePinBase() : Runner(), _degree(UNDEFINED), _duplicate_gens(), _enumerate_order(), _final(), _first(), _found_one(false), _idempotents_found(false), _is_idempotent(), _left(), _length(), _lenindex({0, 0}), _letter_to_pos(), _nr(0), _nr_rules(0), _pos(0), _pos_one(0), _prefix(), _reduced(), _right(), _suffix(), _wordlen(0), // (length of the current word) - 1 // Deprecated, remove in v2 _relation_gen(0), _relation_pos(UNDEFINED) { #ifdef LIBSEMIGROUPS_VERBOSE _nr_products = 0; #endif _right.set_default_value(UNDEFINED); } FroidurePinBase::FroidurePinBase(FroidurePinBase const& S) : Runner(S), _degree(UNDEFINED), // _degree must be UNDEFINED until !_gens.empty() _duplicate_gens(S._duplicate_gens), _enumerate_order(S._enumerate_order), _final(S._final), _first(S._first), _found_one(S._found_one), _idempotents_found(S._idempotents_found), _is_idempotent(S._is_idempotent), _left(S._left), _length(S._length), _lenindex(S._lenindex), _letter_to_pos(S._letter_to_pos), _nr(S._nr), _nr_rules(S._nr_rules), _pos(S._pos), _pos_one(S._pos_one), _prefix(S._prefix), _reduced(S._reduced), _right(S._right), _suffix(S._suffix), _wordlen(S._wordlen), _relation_gen(S._relation_gen), _relation_pos(S._relation_pos) { #ifdef LIBSEMIGROUPS_VERBOSE _nr_products = 0; #endif } FroidurePinBase::~FroidurePinBase() = default; //////////////////////////////////////////////////////////////////////// // FroidurePinBase - constructors - private //////////////////////////////////////////////////////////////////////// // Partial copy. // \p copy a semigroup // \p coll a collection of additional generators // // This is a constructor for a semigroup generated by the generators of the // FroidurePin copy and the (possibly) additional generators coll. // // The relevant parts of the data structure of copy are copied and // \c this will be corrupt unless add_generators or closure is called // subsequently. This is why this member function is private. // // The same effect can be obtained by copying copy using the copy // constructor and then calling add_generators or closure. However, // this constructor avoids copying those parts of the data structure of // copy that add_generators invalidates anyway. If copy has not been // enumerated at all, then these two routes for adding more generators are // equivalent. // // or should usually be called after this. void FroidurePinBase::partial_copy(FroidurePinBase const& S) { _degree = S._degree; // copy for comparison in add_generators _duplicate_gens = S._duplicate_gens; _found_one = S._found_one; // copy in case degree doesn't change in // add_generators _idempotents_found = S._idempotents_found; _is_idempotent = S._is_idempotent; _left = S._left; _lenindex = {0, S._lenindex[1]}; _letter_to_pos = S._letter_to_pos; _nr = S._nr; _nr_rules = 0; _pos = S._pos; _pos_one = S._pos_one; // copy in case degree doesn't change in // add_generators _reduced = S._reduced; _right = S._right; _wordlen = 0; _relation_gen = 0; _relation_pos = UNDEFINED; LIBSEMIGROUPS_ASSERT(S._lenindex.size() > 1); #ifdef LIBSEMIGROUPS_VERBOSE _nr_products = 0; #endif // the following are required for assignment to specific positions in // add_generators _final.resize(S._nr, 0); _first.resize(S._nr, 0); _length.resize(S._nr, 0); _prefix.resize(S._nr, 0); _suffix.resize(S._nr, 0); _enumerate_order.reserve(S._nr); // add the distinct old generators to new _enumerate_order LIBSEMIGROUPS_ASSERT(S._lenindex.size() > 1); for (enumerate_index_type i = 0; i < S._lenindex[1]; i++) { _enumerate_order.push_back(S._enumerate_order[i]); _final[_enumerate_order[i]] = S._final[S._enumerate_order[i]]; _first[_enumerate_order[i]] = S._first[S._enumerate_order[i]]; _prefix[_enumerate_order[i]] = UNDEFINED; _suffix[_enumerate_order[i]] = UNDEFINED; _length[_enumerate_order[i]] = 1; } } //////////////////////////////////////////////////////////////////////// // FroidurePinBase - member functions - public //////////////////////////////////////////////////////////////////////// element_index_type FroidurePinBase::word_to_pos(word_type const& w) const { // w is a word in the generators (i.e. a vector of letter_type's) if (w.size() == 0) { LIBSEMIGROUPS_EXCEPTION("the given word has length 0"); } for (auto x : w) { validate_letter_index(x); } element_index_type out = _letter_to_pos[w[0]]; for (auto it = w.cbegin() + 1; it < w.cend() && out != UNDEFINED; ++it) { out = _right.get(out, *it); } return out; } element_index_type FroidurePinBase::product_by_reduction(element_index_type i, element_index_type j) const { validate_element_index(i); validate_element_index(j); if (length_const(i) <= length_const(j)) { while (i != UNDEFINED) { j = _left.get(j, _final[i]); i = _prefix[i]; } return j; } else { while (j != UNDEFINED) { i = _right.get(i, _first[j]); j = _suffix[j]; } return i; } } // Deprecated, remove in v2 void FroidurePinBase::next_relation(word_type& relation) { if (!finished()) { run(); } relation.clear(); if (_relation_pos == _nr) { // no more relations return; } if (_relation_pos != UNDEFINED) { while (_relation_pos < _nr) { while (_relation_gen < nr_generators()) { if (!_reduced.get(_enumerate_order[_relation_pos], _relation_gen) && (_relation_pos < _lenindex[1] || _reduced.get(_suffix[_enumerate_order[_relation_pos]], _relation_gen))) { relation.push_back(_enumerate_order[_relation_pos]); relation.push_back(_relation_gen); relation.push_back( _right.get(_enumerate_order[_relation_pos], _relation_gen)); break; } _relation_gen++; } if (_relation_gen == nr_generators()) { // then relation is empty _relation_gen = 0; _relation_pos++; } else { break; } } _relation_gen++; } else { // duplicate generators if (_relation_gen < _duplicate_gens.size()) { relation.push_back(_duplicate_gens[_relation_gen].first); relation.push_back(_duplicate_gens[_relation_gen].second); _relation_gen++; } else { _relation_gen = 0; _relation_pos++; next_relation(relation); } } } void FroidurePinBase::enumerate(size_t limit) { if (finished() || limit <= current_size()) { return; } else if (LIMIT_MAX - batch_size() > current_size()) { limit = std::max(limit, current_size() + batch_size()); } else { // batch_size() is very big for some reason limit = batch_size(); } REPORT_DEFAULT( "limit = %llu (%s)\n", static_cast(limit), __func__); run_until([this, &limit]() -> bool { return current_size() >= limit; }); } //////////////////////////////////////////////////////////////////////// // FroidurePinBase - settings - public //////////////////////////////////////////////////////////////////////// FroidurePinBase& FroidurePinBase::batch_size(size_t batch_size) noexcept { _settings._batch_size = batch_size; return *this; } size_t FroidurePinBase::batch_size() const noexcept { return _settings._batch_size; } FroidurePinBase& FroidurePinBase::max_threads(size_t nr_threads) noexcept { unsigned int n = static_cast(nr_threads == 0 ? 1 : nr_threads); _settings._max_threads = std::min(n, std::thread::hardware_concurrency()); return *this; } size_t FroidurePinBase::max_threads() const noexcept { return _settings._max_threads; } FroidurePinBase& FroidurePinBase::concurrency_threshold(size_t thrshld) noexcept { _settings._concurrency_threshold = thrshld; return *this; } size_t FroidurePinBase::concurrency_threshold() const noexcept { return _settings._concurrency_threshold; } FroidurePinBase& FroidurePinBase::immutable(bool val) noexcept { _settings._immutable = val; return *this; } bool FroidurePinBase::immutable() const noexcept { return _settings._immutable; } //////////////////////////////////////////////////////////////////////// // FroidurePinBase - helper non-member functions //////////////////////////////////////////////////////////////////////// void relations(FroidurePinBase& S, std::function&& hook) { S.run(); std::vector relation; // a triple S.reset_next_relation(); S.next_relation(relation); while (relation.size() == 2 && !relation.empty()) { hook(word_type({relation[0]}), word_type({relation[1]})); S.next_relation(relation); } word_type lhs, rhs; // changed in-place by factorisation while (!relation.empty()) { S.factorisation(lhs, relation[0]); S.factorisation(rhs, relation[2]); lhs.push_back(relation[1]); hook(lhs, rhs); S.next_relation(relation); } } void relations(FroidurePinBase& S, std::function&& hook) { S.run(); std::vector relation; // a triple S.reset_next_relation(); S.next_relation(relation); while (relation.size() == 2 && !relation.empty()) { hook(word_type({relation[0]})); hook(word_type({relation[1]})); S.next_relation(relation); } word_type word; // changed in-place by factorisation while (!relation.empty()) { S.factorisation(word, relation[0]); word.push_back(relation[1]); hook(word); S.factorisation(word, relation[2]); hook(word); S.next_relation(relation); } } } // namespace libsemigroups