From 7a74d26fbdab2612e9030246d35ef9a17eae2bdc Mon Sep 17 00:00:00 2001 From: "James D. Mitchell" Date: Thu, 19 Sep 2024 14:52:20 +0100 Subject: [PATCH] Update obvinf.hpp for v1/3 --- docs/DoxygenLayout.xml | 15 +- include/libsemigroups/obvinf.hpp | 547 ++++++++++++++++++++++++------- src/obvinf.cpp | 270 +++++++-------- tests/test-obvinf.cpp | 334 +++++++++---------- 4 files changed, 726 insertions(+), 440 deletions(-) diff --git a/docs/DoxygenLayout.xml b/docs/DoxygenLayout.xml index a8b7f0eca..e9e6c3323 100644 --- a/docs/DoxygenLayout.xml +++ b/docs/DoxygenLayout.xml @@ -79,6 +79,14 @@ title="Partitioned binary relations (PBRs)" /> + + + + + + + + - - - - - - - diff --git a/include/libsemigroups/obvinf.hpp b/include/libsemigroups/obvinf.hpp index 84573a1c8..89b195ef3 100644 --- a/include/libsemigroups/obvinf.hpp +++ b/include/libsemigroups/obvinf.hpp @@ -18,42 +18,8 @@ // // This file contains a helper class for checking whether or not a congruence -// defined by generating pairs or finitely presented semigroup is obviously -// infinite. Currently, all that is checked is that: -// -// 1. For every generator there is at least one side of one relation that -// consists solely of that generator. If this condition is not met, then -// there is a generator of infinite order. -// -// 2. The number of occurrences of every generator is not preserved by the -// relations. Otherwise, it is not possible to use the relations to reduce -// the number of occurrences of a generator in a word, and so there are -// infinitely many distinct words. -// -// 3. The number of generators on the left hand side of a relation is not the -// same as the number of generators on the right hand side for at least -// one generator. Otherwise the relations preserve the length of any word -// and so there are infinitely many distinct words. -// -// 4. There are at least as many relations as there are generators. Otherwise -// we can find a surjective homomorphism onto an infinite subsemigroup of -// the rationals under addition. -// -// 5. The checks 2., 3. and 4. are a special case of a more general matrix based -// condition. We construct a matrix whose columns correspond to generators -// and rows correspond to relations. The (i, j)-th entry is the number of -// occurrences of the j-th generator in the left hand side of the i-th -// relation minus the number of occurrences of it on the right hand side. -// If this matrix has a non-trivial kernel, then we can construct a -// surjective homomorphism onto an infinite subsemigroup of the rationals -// under addition. So we check that the matrix is full rank. -// -// 6. The presentation is not that of a free product. To do this we consider -// a graph whose vertices are generators and an edge connects two generators -// if they occur on either side of the same relation. If this graph is -// disconnected then the presentation is a free product and is therefore -// infinite. Note that we currently do not consider the case where the -// identity occurs in the presentation. +// defined by generating pairs or finitely presented semigroup or monoid is +// obviously infinite. #ifndef LIBSEMIGROUPS_OBVINF_HPP_ #define LIBSEMIGROUPS_OBVINF_HPP_ @@ -65,13 +31,12 @@ #include // for vector #include "config.hpp" // for LIBSEMIGROUPS_EIGEN_ENABLED -#include "ranges.hpp" // for word_type etc +#include "ranges.hpp" // for rx/ranges #include "types.hpp" // for word_type etc #include "word-graph.hpp" // for is_acyclic -#include "detail/eigen.hpp" -#include "detail/rewriters.hpp" // for RewriteTrie -#include "detail/uf.hpp" // for Duf +#include "detail/eigen.hpp" // for eigen +#include "detail/uf.hpp" // for Duf namespace libsemigroups { #ifndef PARSED_BY_DOXYGEN @@ -93,106 +58,336 @@ namespace libsemigroups { void change_alphabet(Presentation&, Word const&); } - namespace detail { - class IsObviouslyInfinite { - using const_iterator_word_type = - typename std::vector::const_iterator; - using const_iterator_pair_string = typename std::vector< - std::pair>::const_iterator; - - using const_iterator_string = - typename std::vector::const_iterator; - - public: - explicit IsObviouslyInfinite(size_t); - - explicit IsObviouslyInfinite(std::string const& lphbt) - : IsObviouslyInfinite(lphbt.size()) {} - - IsObviouslyInfinite(IsObviouslyInfinite const&) = delete; - IsObviouslyInfinite(IsObviouslyInfinite&&) = delete; - IsObviouslyInfinite& operator=(IsObviouslyInfinite const&) = delete; - IsObviouslyInfinite& operator=(IsObviouslyInfinite&&) = delete; - - ~IsObviouslyInfinite(); - - void add_rules(const_iterator_word_type first, - const_iterator_word_type last); - - void add_rules(std::string const&, - const_iterator_string first, - const_iterator_string last); - - void add_rules(std::string const& lphbt, - const_iterator_pair_string first, - const_iterator_pair_string last); - - bool result() const; - - private: - void private_add_rule(size_t const, word_type const&, word_type const&); + //! \defgroup obvinf_group Obviously infinite + //! \ingroup misc_group + //! + //! \brief Functions for checking if a finitely presented semigroup or monoid + //! is obviously infinite. + //! + //! This page collects the documentation for the functionality in + //! ``libsemigroups`` for checking if a finitely presented semigroup or monoid + //! is obviously infinite. + + //! \ingroup obvinf_group + //! + //! \brief Class for checking if a finitely presented semigroup or monoid is + //! obviously infinite. + //! + //! This class implements a number of checks whether or not a finitely + //! presented semigroup or monoid is infinite. These checks are all decidable, + //! and always return an answer within an amount of time that is linear in the + //! size of the input. + //! + //! These checks are: + //! + //! 1. For every generator there is at least one side of one relation that + //! consists solely of that generator. If this condition is not met, then + //! there is a generator of infinite order. + //! + //! 2. The number of occurrences of every generator is not preserved by the + //! relations. Otherwise, it is not possible to use the relations to + //! reduce the number of occurrences of a generator in a word, and so + //! there are infinitely many distinct words. + //! + //! 3. The number of generators on the left hand side of a relation is not + //! the same as the number of generators on the right hand side for at + //! least one generator. Otherwise the relations preserve the length of + //! any word and so there are infinitely many distinct words. + //! + //! 4. There are at least as many relations as there are generators. + //! Otherwise we can find a surjective homomorphism onto an infinite + //! subsemigroup of the rationals under addition. + //! + //! 5. The checks 2., 3. and 4. are a special case of a more general matrix + //! based condition. We construct a matrix whose columns correspond to + //! generators and rows correspond to relations. The (i, j)-th entry is + //! the number of occurrences of the j-th generator in the left hand side + //! of the i-th relation minus the number of occurrences of it on the + //! right hand side. If this matrix has a non-trivial kernel, then we can + //! construct a surjective homomorphism onto an infinite subsemigroup of + //! the rationals under addition. So we check that the matrix is full + //! rank. + //! + //! 6. The presentation is not that of a free product. To do this we consider + //! a graph whose vertices are generators and an edge connects two + //! generators if they occur on either side of the same relation. If this + //! graph is disconnected then the presentation is a free product and is + //! therefore infinite. Note that we currently do not consider the case + //! where the identity occurs in the presentation. + //! + //! \sa \ref is_obviously_infinite. + // TODO(1) there are definitely some assumptions about the calls to the member + // functions of an IsObviouslyInfinite (see for example the function + // is_obviously_infinite for a Presentation). These should be documented. + class IsObviouslyInfinite { + // The default constructor is private since an object that is default + // constructed isn't usable with the current public API. + IsObviouslyInfinite() = default; + + public: + //! Alias for std::vector::const_iterator. + using const_iterator_word_type = + typename std::vector::const_iterator; + + //! \brief Alias for std::vector< std::pair>::const_iterator. + //! + //! Alias for std::vector< std::pair>::const_iterator. + using const_iterator_pair_string = typename std::vector< + std::pair>::const_iterator; + + //! Alias for std::vector::const_iterator. + using const_iterator_string = + typename std::vector::const_iterator; + + //! \brief Construct from alphabet size. + //! + //! Constructs an empty IsObviouslyInfinite object representing a finitely + //! presented semigroup or monoid with \p n generators. + //! + //! \param n the number of generators. + //! + //! \exceptions + //! \no_libsemigroups_except + explicit IsObviouslyInfinite(size_t n); + + //! Re-initialize the object as if it had just been constructed. + //! + //! Calling this function puts it into the same state that it would have + //! been in if it had just been newly constructed with the same parameter \p + //! n. + //! + //! This function exists to allow re-use of the memory allocated within the + //! object. + //! + //! \param n the number of generators. + //! + //! \returns A reference to `*this`. + //! + //! \exceptions + //! \no_libsemigroups_except + IsObviouslyInfinite& init(size_t n); + + //! \brief Construct from alphabet. + //! + //! Constructs an empty IsObviouslyInfinite object representing a finitely + //! presented semigroup or monoid with alphabet \p lphbt. + //! + //! \param lphbt the alphabet to use. + //! + //! \exceptions + //! \no_libsemigroups_except + explicit IsObviouslyInfinite(std::string const& lphbt) + : IsObviouslyInfinite(lphbt.size()) {} + + //! Re-initialize the object as if it had just been constructed. + //! + //! Calling this function puts it into the same state that it would have + //! been in if it had just been newly constructed with the same parameter \p + //! n. + //! + //! This function exists to allow re-use of the memory allocated within the + //! object. + //! + //! \param lphbt the alphabet to use. + //! + //! \returns A reference to `*this`. + //! + //! \exceptions + //! \no_libsemigroups_except + IsObviouslyInfinite& init(std::string const& lphbt) { + return init(lphbt.size()); + } - inline void letters_in_word(size_t row, word_type const& w, int64_t adv) { - for (size_t const& x : w) { - matrix(row, x) += adv; - _seen[x] = true; - } + //! Deleted + IsObviouslyInfinite(IsObviouslyInfinite const&) = delete; + + //! Deleted + IsObviouslyInfinite(IsObviouslyInfinite&&) = delete; + + //! Deleted + IsObviouslyInfinite& operator=(IsObviouslyInfinite const&) = delete; + + //! Deleted + IsObviouslyInfinite& operator=(IsObviouslyInfinite&&) = delete; + + ~IsObviouslyInfinite(); + + //! \brief Add rules from iterators to \ref word_type. + //! + //! This function adds the rules described by the iterators \p first and \p + //! last. + //! + //! \param first iterator pointing at the left-hand-side of the first rule + //! to add. + //! + //! \param last iterator pointing one beyond the right-hand-side of the last + //! rule to add. + //! + //! \returns A reference to `*this`. + //! + //! \exceptions + //! \no_libsemigroups_except + //! + //! \warning + //! This function does not check its arguments. + // TODO(0) does this check its args? Throw? + IsObviouslyInfinite& add_rules_no_checks(const_iterator_word_type first, + const_iterator_word_type last); + + //! \brief Add rules from iterators to std::string. + //! + //! This function adds the rules described by the iterators \p first and \p + //! last. The rules are translated to \ref word_type objects using the + //! position of each character in the 1st argument \p lphbt. + //! + //! \param lphbt the alphabet to use. + //! + //! \param first iterator pointing at the left-hand-side of the first rule + //! to add. + //! + //! \param last iterator pointing one beyond the right-hand-side of the last + //! rule to add. + //! + //! \returns A reference to `*this`. + //! + //! \exceptions + //! \no_libsemigroups_except + //! + //! \warning + //! This function does not check its arguments. + IsObviouslyInfinite& add_rules_no_checks(std::string const& lphbt, + const_iterator_string first, + const_iterator_string last); + + //! \brief Add rules from iterators to std::pair of std::string. + //! + //! This function adds the rules described by the iterators \p first and \p + //! last. The rules are translated to \ref word_type objects using the + //! position of each character in the 1st argument \p lphbt. + //! + //! \param lphbt the alphabet to use. + //! + //! \param first iterator pointing at the left-hand-side of the first rule + //! to add. + //! + //! \param last iterator pointing one beyond the right-hand-side of the last + //! rule to add. + //! + //! \returns A reference to `*this`. + //! + //! \exceptions + //! \no_libsemigroups_except + //! + //! \warning + //! This function does not check its arguments. + IsObviouslyInfinite& add_rules_no_checks(std::string const& lphbt, + const_iterator_pair_string first, + const_iterator_pair_string last); + + //! \brief Returns whether or not the finitely presented semigroup or monoid + //! is obviously infinite. + //! + //! This function returns \c true if the finitely presented semigroup or + //! monoid defined using the alphabet used to construct an + //! IsObviouslyInfinite object, and with relations added to the + //! IsObviouslyInfinite object by \ref add_rules_no_checks, is obviously + //! infinite. + //! + //! \returns Whether or not the finitely presented semigroup or monoid is + //! obviously infinite. + bool result() const; + + // TODO(1) certificate() returning why the thing is obviously infinite + + private: + void private_add_rule(size_t const, word_type const&, word_type const&); + + inline void letters_in_word(size_t row, word_type const& w, int64_t adv) { + for (size_t const& x : w) { + matrix(row, x) += adv; + _seen[x] = true; } + } - inline void plus_letters_in_word(size_t row, word_type const& w) { - letters_in_word(row, w, 1); - } + inline void plus_letters_in_word(size_t row, word_type const& w) { + letters_in_word(row, w, 1); + } - inline void minus_letters_in_word(size_t row, word_type const& w) { - letters_in_word(row, w, -1); - } + inline void minus_letters_in_word(size_t row, word_type const& w) { + letters_in_word(row, w, -1); + } - inline int64_t& matrix(size_t row, size_t col) { + inline int64_t& matrix(size_t row, size_t col) { #ifdef LIBSEMIGROUPS_EIGEN_ENABLED - return _matrix(row, col); + return _matrix(row, col); #else - (void) row; - return _matrix[col]; + (void) row; + return _matrix[col]; #endif - } + } - inline bool matrix_row_sums_to_0(size_t row) { + inline bool matrix_row_sums_to_0(size_t row) { #ifdef LIBSEMIGROUPS_EIGEN_ENABLED - return _matrix.row(row).sum() == 0; + return _matrix.row(row).sum() == 0; #else - (void) row; - return std::accumulate(_matrix.cbegin(), _matrix.cend(), 0) == 0; + (void) row; + return std::accumulate(_matrix.cbegin(), _matrix.cend(), 0) == 0; #endif - } + } - // letter_type i belongs to "preserve" if there exists a relation where - // the number of occurrences of i is not the same on both sides of the - // relation letter_type i belongs to "unique" if there is a relation - // where one side consists solely of i. - bool _empty_word; - detail::Duf<> _letter_components; - size_t _nr_gens; - size_t _nr_letter_components; - size_t _nr_relations; - bool _preserve_length; - std::vector _preserve; - std::vector _seen; - std::vector _unique; + // letter_type i belongs to "preserve" if there exists a relation where + // the number of occurrences of i is not the same on both sides of the + // relation letter_type i belongs to "unique" if there is a relation + // where one side consists solely of i. + bool _empty_word; + detail::Duf<> _letter_components; + size_t _nr_gens; + size_t _nr_letter_components; + size_t _nr_relations; + bool _preserve_length; + std::vector _preserve; + std::vector _seen; + std::vector _unique; #ifdef LIBSEMIGROUPS_EIGEN_ENABLED - Eigen::Matrix _matrix; + Eigen::Matrix _matrix; #else - std::vector _matrix; + std::vector _matrix; #endif - }; - } // namespace detail - + }; + + //! \ingroup obvinf_group + //! + //! \brief Function for checking if the finitely presented semigroup or monoid + //! defined by a Presentation object is obviously infinite or not. + //! + //! This function returns \c true if the finitely presented semigroup or + //! monoid defined by the Presentation object \p p is obviously infinite. This + //! function exists to make it simpler to call an IsObviouslyInfinite + //! object a single time. + //! + //! \tparam Word the type of the words in the presentation \p p. + //! + //! \param p the presentation. + //! + //! \returns Whether or not the presentation defines an obviously infinite + //! semigroup or monoid. + //! + //! \throws LibsemigroupsException If the presentation \p p is not valid. + //! + //! \note If this function returns \c false, it is still possible that + //! semigroup or monoid defined by \p p is infinite. template bool is_obviously_infinite(Presentation const& p) { + p.validate(); if (p.alphabet().empty()) { return false; } // FIXME! This returns wrong answers if the p.alphabet is not contiguous + // At time of writing this (some time after writing the FIXME in the + // previous line, I'm not sure there is anything to fix. Maybe there is. auto it = std::max_element(std::begin(p.alphabet()), std::end(p.alphabet())); @@ -202,22 +397,102 @@ namespace libsemigroups { copy_p, rx::seq::letter_type>(0) | rx::take(p.alphabet().size()) | rx::to_vector()); - detail::IsObviouslyInfinite ioi(copy_p.alphabet().size()); - ioi.add_rules(copy_p.rules.cbegin(), copy_p.rules.cend()); + IsObviouslyInfinite ioi(copy_p.alphabet().size()); + ioi.add_rules_no_checks(copy_p.rules.cbegin(), copy_p.rules.cend()); return ioi.result(); } - detail::IsObviouslyInfinite ioi(p.alphabet().size()); - ioi.add_rules(p.rules.cbegin(), p.rules.cend()); + IsObviouslyInfinite ioi(p.alphabet().size()); + ioi.add_rules_no_checks(p.rules.cbegin(), p.rules.cend()); return ioi.result(); } + //! \ingroup obvinf_group + //! + //! \brief Function for checking if the finitely presented semigroup or monoid + //! defined by a Presentation object is obviously infinite or not. + //! + //! This function returns \c true if the finitely presented semigroup or + //! monoid defined by the Presentation object \p p is obviously infinite. This + //! function exists to make it simpler to call an IsObviouslyInfinite + //! object a single time. + //! + //! \param p the presentation. + //! + //! \returns Whether or not the presentation defines an obviously infinite + //! semigroup or monoid. + //! + //! \throws LibsemigroupsException If the presentation \p p is not valid. template <> bool is_obviously_infinite(Presentation const& p); + //! \ingroup obvinf_group + //! + //! \brief Function for checking if the quotient of a finitely presented + //! semigroup or monoid defined by a ToddCoxeter object is obviously infinite + //! or not. + //! + //! This function returns \c true if the quotient of the finitely presented + //! semigroup or monoid defined by the ToddCoxeter object \p tc is obviously + //! infinite; \c false is returned if it is not. + //! + //! This function exists to make it simpler to call an + //! IsObviouslyInfinite object a single time, and uses some information from + //! the (possible incomplete) ToddCoxeter object to assist in this + //! determination. + //! + //! \param tc the ToddCoxeter instance. + //! + //! \returns Whether or not the quotient defined by a ToddCoxeter instance is + //! obviously infinite. + //! + //! \note If this function returns \c false, it is still possible that the + //! quotient defined by the ToddCoxeter object \p tc is infinite. bool is_obviously_infinite(ToddCoxeter const& tc); - bool is_obviously_infinite(Congruence& kb); + //! \ingroup obvinf_group + //! + //! \brief Function for checking if a congruence obviously has infinite many + //! classes. + //! + //! This function returns \c true if the quotient of the finitely presented + //! semigroup or monoid defined by the Congruence object \p c is obviously + //! infinite; \c false is returned if it is not. + //! + //! This function exists to make it simpler to call an IsObviouslyInfinite + //! object a single time, and uses some information from the (possible + //! incomplete) Congruence object to assist in this determination. + //! + //! \param c the Congruence instance. + //! + //! \returns Whether or not the congruence obviously has infinitely many + //! classes. + //! + //! \note If this function returns \c false, it is still possible that the + //! congruence has infinitely many classes. + bool is_obviously_infinite(Congruence& c); + + //! \ingroup obvinf_group + //! + //! \brief Function for checking if the finitely presented semigroup or + //! monoid defined by a Kambites object obviously has infinite many + //! classes. + //! + //! This function returns \c true if the finitely presented semigroup or + //! monoid defined by a Kambites object is obviously infinite; \c false is + //! returned if it is not. + //! + //! This function exists to make it simpler to call an IsObviouslyInfinite + //! object a single time, and uses some information from the (possible + //! incomplete) Kambites object to assist in this determination. + //! + //! \param k the Kambites instance. + //! + //! \returns Whether or not the finitely presented semigroup or + //! monoid defined by a Kambites object is obviously infinite. + //! + //! \note If this function returns \c false, it is still possible that the + //! finitely presented semigroup or monoid defined by \p k$ is infinite. template bool is_obviously_infinite(Kambites& k) { if (k.finished() && k.small_overlap_class() >= 3) { @@ -229,6 +504,27 @@ namespace libsemigroups { return k.small_overlap_class() >= 3; } + //! \ingroup obvinf_group + //! + //! \brief Function for checking if the quotient of a finitely presented + //! semigroup or monoid defined by a KnuthBendix object is obviously infinite + //! or not. + //! + //! This function returns \c true if the quotient of the finitely presented + //! semigroup or monoid defined by the KnuthBendix object \p kb is obviously + //! infinite; \c false is returned if it is not. + //! + //! This function exists to make it simpler to call an IsObviouslyInfinite + //! object a single time, and uses some information from the (possible + //! incomplete) KnuthBendix object to assist in this determination. + //! + //! \param kb the KnuthBendix instance. + //! + //! \returns Whether or not the quotient defined by a KnuthBendix instance is + //! obviously infinite. + //! + //! \note If this function returns \c false, it is still possible that the + //! quotient defined by the KnuthBendix object \p kb is infinite. template bool is_obviously_infinite(KnuthBendix& kb) { if (kb.finished()) { @@ -238,9 +534,10 @@ namespace libsemigroups { if (p.alphabet().empty()) { return false; } - detail::IsObviouslyInfinite ioi(p.alphabet().size()); - ioi.add_rules(p.alphabet(), p.rules.cbegin(), p.rules.cend()); - ioi.add_rules(kb.generating_pairs().cbegin(), kb.generating_pairs().cend()); + IsObviouslyInfinite ioi(p.alphabet().size()); + ioi.add_rules_no_checks(p.alphabet(), p.rules.cbegin(), p.rules.cend()); + ioi.add_rules_no_checks(kb.generating_pairs().cbegin(), + kb.generating_pairs().cend()); return ioi.result(); } diff --git a/src/obvinf.cpp b/src/obvinf.cpp index b65f7ac73..8a0cab160 100644 --- a/src/obvinf.cpp +++ b/src/obvinf.cpp @@ -38,163 +38,172 @@ #include "libsemigroups/detail/eigen.hpp" namespace libsemigroups { - namespace detail { - - using const_iterator_word_type = - typename std::vector::const_iterator; - using const_iterator_pair_string = typename std::vector< - std::pair>::const_iterator; - - IsObviouslyInfinite::IsObviouslyInfinite(size_t n) - : _empty_word(false), - _letter_components(n), - _nr_gens(n), - _nr_relations(0), - _preserve_length(true), - _preserve(n, false), - _seen(n, false), - _unique(n, false), + + using const_iterator_word_type = + typename std::vector::const_iterator; + using const_iterator_pair_string = + typename std::vector>::const_iterator; + + IsObviouslyInfinite::IsObviouslyInfinite(size_t n) : IsObviouslyInfinite() { + init(n); + } + + IsObviouslyInfinite& IsObviouslyInfinite::init(size_t n) { + _empty_word = false; + _letter_components.init(n); + _nr_gens = n; + _nr_relations = 0; + _preserve_length = true; + _preserve.clear(); + _preserve.resize(n, false); + _seen.clear(); + _seen.resize(n, false); + _unique.resize(n, false); #ifdef LIBSEMIGROUPS_EIGEN_ENABLED - _matrix(0, n) { - } + _matrix = decltype(_matrix)(0, n); #else - _matrix(n, 0) { - } + _matrix = decltype(_matrix)(n, 0); #endif + return *this; + } - IsObviouslyInfinite::~IsObviouslyInfinite() = default; + IsObviouslyInfinite::~IsObviouslyInfinite() = default; - void IsObviouslyInfinite::add_rules(const_iterator_word_type first, - const_iterator_word_type last) { + IsObviouslyInfinite& + IsObviouslyInfinite::add_rules_no_checks(const_iterator_word_type first, + const_iterator_word_type last) { #ifdef LIBSEMIGROUPS_EIGEN_ENABLED - auto matrix_start = _matrix.rows(); - _matrix.conservativeResize(matrix_start + (last - first) / 2, - Eigen::NoChange); - _matrix.block(matrix_start, 0, (last - first) / 2, _matrix.cols()) - .setZero(); + auto matrix_start = _matrix.rows(); + _matrix.conservativeResize(matrix_start + (last - first) / 2, + Eigen::NoChange); + _matrix.block(matrix_start, 0, (last - first) / 2, _matrix.cols()) + .setZero(); #else - auto matrix_start = 0; - std::fill(_matrix.begin(), _matrix.end(), 0); + auto matrix_start = 0; + std::fill(_matrix.begin(), _matrix.end(), 0); #endif - for (auto it = first; it < last; it += 2) { - private_add_rule(matrix_start + (it - first) / 2, *it, *(it + 1)); - } - _nr_letter_components = _letter_components.number_of_blocks(); + for (auto it = first; it < last; it += 2) { + private_add_rule(matrix_start + (it - first) / 2, *it, *(it + 1)); } + _nr_letter_components = _letter_components.number_of_blocks(); + return *this; + } - void IsObviouslyInfinite::add_rules(std::string const& lphbt, - const_iterator_pair_string first, - const_iterator_pair_string last) { + IsObviouslyInfinite& + IsObviouslyInfinite::add_rules_no_checks(std::string const& lphbt, + const_iterator_pair_string first, + const_iterator_pair_string last) { #ifdef LIBSEMIGROUPS_EIGEN_ENABLED - auto matrix_start = _matrix.rows(); - _matrix.conservativeResize(matrix_start + (last - first), - Eigen::NoChange); - _matrix.block(matrix_start, 0, (last - first), _matrix.cols()).setZero(); + auto matrix_start = _matrix.rows(); + _matrix.conservativeResize(matrix_start + (last - first), Eigen::NoChange); + _matrix.block(matrix_start, 0, (last - first), _matrix.cols()).setZero(); #else - auto matrix_start = 0; - std::fill(_matrix.begin(), _matrix.end(), 0); + auto matrix_start = 0; + std::fill(_matrix.begin(), _matrix.end(), 0); #endif - ToWord stw(lphbt); - word_type lhs; - word_type rhs; - for (auto it = first; it < last; ++it) { - stw(lhs, it->first); // lhs changed in-place - stw(rhs, it->second); // rhs changed in-place - private_add_rule(matrix_start + (it - first), lhs, rhs); - } - _nr_letter_components = _letter_components.number_of_blocks(); + ToWord stw(lphbt); + word_type lhs; + word_type rhs; + for (auto it = first; it < last; ++it) { + stw(lhs, it->first); // lhs changed in-place + stw(rhs, it->second); // rhs changed in-place + private_add_rule(matrix_start + (it - first), lhs, rhs); } + _nr_letter_components = _letter_components.number_of_blocks(); + return *this; + } - void IsObviouslyInfinite::add_rules(std::string const& lphbt, - const_iterator_string first, - const_iterator_string last) { + IsObviouslyInfinite& + IsObviouslyInfinite::add_rules_no_checks(std::string const& lphbt, + const_iterator_string first, + const_iterator_string last) { #ifdef LIBSEMIGROUPS_EIGEN_ENABLED - auto matrix_start = _matrix.rows(); - _matrix.conservativeResize(matrix_start + (last - first) / 2, - Eigen::NoChange); - _matrix.block(matrix_start, 0, (last - first) / 2, _matrix.cols()) - .setZero(); + auto matrix_start = _matrix.rows(); + _matrix.conservativeResize(matrix_start + (last - first) / 2, + Eigen::NoChange); + _matrix.block(matrix_start, 0, (last - first) / 2, _matrix.cols()) + .setZero(); #else - auto matrix_start = 0; - std::fill(_matrix.begin(), _matrix.end(), 0); + auto matrix_start = 0; + std::fill(_matrix.begin(), _matrix.end(), 0); #endif - ToWord stw(lphbt); - word_type lhs, rhs; - for (auto it = first; it < last; ++it) { - stw(lhs, *it++); // lhs changed in-place - stw(rhs, *it); // rhs changed in-place - private_add_rule(matrix_start + (it - first) / 2, lhs, rhs); - } - _nr_letter_components = _letter_components.number_of_blocks(); + ToWord stw(lphbt); + word_type lhs, rhs; + for (auto it = first; it < last; ++it) { + stw(lhs, *it++); // lhs changed in-place + stw(rhs, *it); // rhs changed in-place + private_add_rule(matrix_start + (it - first) / 2, lhs, rhs); } + _nr_letter_components = _letter_components.number_of_blocks(); + return *this; + } - bool IsObviouslyInfinite::result() const { + bool IsObviouslyInfinite::result() const { #ifdef LIBSEMIGROUPS_EIGEN_ENABLED - LIBSEMIGROUPS_ASSERT(_matrix.rows() >= 0); - LIBSEMIGROUPS_ASSERT(_matrix.cast().colPivHouseholderQr().rank() - >= 0); + LIBSEMIGROUPS_ASSERT(_matrix.rows() >= 0); + LIBSEMIGROUPS_ASSERT(_matrix.cast().colPivHouseholderQr().rank() + >= 0); #endif - return (_preserve_length - || (!_empty_word - && !std::all_of(_unique.begin(), - _unique.end(), - [](bool v) -> bool { return v; })) - || !std::all_of(_preserve.begin(), - _preserve.end(), - [](bool v) -> bool { return v; }) - || (!_empty_word && _nr_letter_components > 1) + return (_preserve_length + || (!_empty_word + && !std::all_of(_unique.begin(), + _unique.end(), + [](bool v) -> bool { return v; })) + || !std::all_of(_preserve.begin(), + _preserve.end(), + [](bool v) -> bool { return v; }) + || (!_empty_word && _nr_letter_components > 1) #ifdef LIBSEMIGROUPS_EIGEN_ENABLED - || _nr_relations < _nr_gens - || size_t(_matrix.cast().colPivHouseholderQr().rank()) - != _nr_gens); + || _nr_relations < _nr_gens + || size_t(_matrix.cast().colPivHouseholderQr().rank()) + != _nr_gens); #else || _nr_relations < _nr_gens); #endif - } + } - void IsObviouslyInfinite::private_add_rule(size_t row_index, - word_type const& u, - word_type const& v) { - _nr_relations++; - if (u.empty() || v.empty()) { - _empty_word = true; - } - std::fill(_seen.begin(), _seen.end(), false); - plus_letters_in_word(row_index, u); - if (!_empty_word - && std::all_of(u.cbegin() + 1, u.cend(), [&u](letter_type i) -> bool { - return i == u[0]; - })) { - _unique[u[0]] = true; - } - minus_letters_in_word(row_index, v); - if (!_empty_word && !v.empty() - && std::all_of(v.cbegin() + 1, v.cend(), [&v](letter_type i) -> bool { - return i == v[0]; - })) { - _unique[v[0]] = true; - } - for (size_t x = 0; x < _nr_gens; x++) { - if (matrix(row_index, x) != 0) { - _preserve[x] = true; - } - } - if (_preserve_length && !matrix_row_sums_to_0(row_index)) { - _preserve_length = false; + void IsObviouslyInfinite::private_add_rule(size_t row_index, + word_type const& u, + word_type const& v) { + _nr_relations++; + if (u.empty() || v.empty()) { + _empty_word = true; + } + std::fill(_seen.begin(), _seen.end(), false); + plus_letters_in_word(row_index, u); + if (!_empty_word + && std::all_of(u.cbegin() + 1, u.cend(), [&u](letter_type i) -> bool { + return i == u[0]; + })) { + _unique[u[0]] = true; + } + minus_letters_in_word(row_index, v); + if (!_empty_word && !v.empty() + && std::all_of(v.cbegin() + 1, v.cend(), [&v](letter_type i) -> bool { + return i == v[0]; + })) { + _unique[v[0]] = true; + } + for (size_t x = 0; x < _nr_gens; x++) { + if (matrix(row_index, x) != 0) { + _preserve[x] = true; } - size_t last_seen = UNDEFINED; - for (size_t x = 0; x < _nr_gens; ++x) { - if (_seen[x]) { - if (last_seen != UNDEFINED) { - _letter_components.unite(last_seen, x); - } - last_seen = x; + } + if (_preserve_length && !matrix_row_sums_to_0(row_index)) { + _preserve_length = false; + } + size_t last_seen = UNDEFINED; + for (size_t x = 0; x < _nr_gens; ++x) { + if (_seen[x]) { + if (last_seen != UNDEFINED) { + _letter_components.unite(last_seen, x); } + last_seen = x; } } - } // namespace detail + } bool is_obviously_infinite(ToddCoxeter const& tc) { auto const& d = tc.word_graph(); @@ -205,9 +214,12 @@ namespace libsemigroups { } auto p = tc.presentation(); // TODO don't copy this here presentation::normalize_alphabet(p); - detail::IsObviouslyInfinite ioi(p.alphabet().size()); - ioi.add_rules(p.rules.cbegin(), p.rules.cend()); - ioi.add_rules(tc.generating_pairs().cbegin(), tc.generating_pairs().cend()); + IsObviouslyInfinite ioi(p.alphabet().size()); + ioi.add_rules_no_checks(p.rules.cbegin(), p.rules.cend()); + // TODO is the next line correct, shouldn't the generating pairs also + // be normalized? + ioi.add_rules_no_checks(tc.generating_pairs().cbegin(), + tc.generating_pairs().cend()); return ioi.result(); } @@ -216,9 +228,9 @@ namespace libsemigroups { if (p.alphabet().empty()) { return false; } - detail::IsObviouslyInfinite ioi(p.alphabet().size()); + IsObviouslyInfinite ioi(p.alphabet().size()); // This function required because of the p.alphabet below! - ioi.add_rules(p.alphabet(), p.rules.cbegin(), p.rules.cend()); + ioi.add_rules_no_checks(p.alphabet(), p.rules.cbegin(), p.rules.cend()); return ioi.result(); } diff --git a/tests/test-obvinf.cpp b/tests/test-obvinf.cpp index 140932409..8370951f0 100644 --- a/tests/test-obvinf.cpp +++ b/tests/test-obvinf.cpp @@ -18,26 +18,30 @@ // TODO(later): // 1. add more tests -#include - #include "catch_amalgamated.hpp" // for REQUIRE, REQUIRE_THROWS_AS, REQUI... +#include "libsemigroups/exception.hpp" #include "test-main.hpp" -#include "libsemigroups/obvinf.hpp" +#include "libsemigroups/obvinf.hpp" // for IsObviouslyInfinite +#include "libsemigroups/words.hpp" // for operator""_w namespace libsemigroups { + using namespace literals; // TODO(v2): uncomment these tests or remove them - /*LIBSEMIGROUPS_TEST_CASE("ObviouslyInfinite", - "001", - "Multiple rule additions", - "[quick]") { - detail::IsObviouslyInfinite ioi(3); - std::vector v + LIBSEMIGROUPS_TEST_CASE_V3("ObviouslyInfinite", + "001", + "Multiple rule additions", + "[quick]") { + IsObviouslyInfinite ioi(3); + std::vector v = {"aababbaccabbc", "a", "aaabbbbaaabbbbacbbb", "bb"}; - ioi.add_rules(v.cbegin(), v.cend()); + + REQUIRE_THROWS_AS(ioi.add_rules_no_checks("ab", v.cbegin(), v.cend()), + LibsemigroupsException); + ioi.add_rules_no_checks("abc", v.cbegin(), v.cend()); REQUIRE(ioi.result()); v = {"cc", "bababab"}; - ioi.add_rules(v.cbegin(), v.cend()); + ioi.add_rules_no_checks("abc", v.cbegin(), v.cend()); REQUIRE(ioi.result()); v = {"bbbaaaaabbbaaaaabbbaaaaabbbcccbbbbbbbbb", "bcbab", @@ -45,214 +49,202 @@ namespace libsemigroups { "aa", "", "aaaaaaabbbbbbbbbc"}; - ioi.add_rules(v.cbegin(), v.cend()); + ioi.add_rules_no_checks("abc", v.cbegin(), v.cend()); REQUIRE(ioi.result()); v = {"a", "aa"}; - ioi.add_rules(v.cbegin(), v.cend()); + ioi.add_rules_no_checks("abc", v.cbegin(), v.cend()); REQUIRE(!ioi.result()); v = {"b", "bbaa", "caa", "ccbbbaa"}; - ioi.add_rules(v.cbegin(), v.cend()); + ioi.add_rules_no_checks("abc", v.cbegin(), v.cend()); REQUIRE(!ioi.result()); } - - LIBSEMIGROUPS_TEST_CASE( + LIBSEMIGROUPS_TEST_CASE_V3( "ObviouslyInfinite", "002", "A power of the generator 'b' does not occur on its own in any relation", "[quick]") { - detail::IsObviouslyInfinite ioi(2); - std::vector v = {"ab", "a", "aba", "ba"}; - ioi.add_rules(v.cbegin(), v.cend()); + IsObviouslyInfinite ioi(2); + std::vector v = {"ab", "a", "aba", "ba"}; + ioi.add_rules_no_checks("ab", v.cbegin(), v.cend()); REQUIRE(ioi.result()); } - LIBSEMIGROUPS_TEST_CASE( + LIBSEMIGROUPS_TEST_CASE_V3( "ObviouslyInfinite", "003", "Preserves the number of occurrences of the generator 'a'", "[quick]") { - detail::IsObviouslyInfinite ioi(2); + IsObviouslyInfinite ioi(2); std::vector v = {"aba", "aa", "bb", "b", "abab", "abbba"}; - ioi.add_rules(v.cbegin(), v.cend()); + ioi.add_rules_no_checks("ab", v.cbegin(), v.cend()); REQUIRE(ioi.result()); } - LIBSEMIGROUPS_TEST_CASE("ObviouslyInfinite", - "004", - "Less relations than generators", - "[quick]") { - detail::IsObviouslyInfinite ioi(3); - std::vector v = {"aba", "bc", "ca", "b"}; - ioi.add_rules(v.cbegin(), v.cend()); + LIBSEMIGROUPS_TEST_CASE_V3("ObviouslyInfinite", + "004", + "Less relations than generators", + "[quick]") { + IsObviouslyInfinite ioi(3); + std::vector v = {"aba", "bc", "ca", "b"}; + ioi.add_rules_no_checks("abc", v.cbegin(), v.cend()); REQUIRE(ioi.result()); } - LIBSEMIGROUPS_TEST_CASE("ObviouslyInfinite", - "005", - "Relations preserve length", - "[quick]") { - detail::IsObviouslyInfinite ioi(3); - std::vector v + LIBSEMIGROUPS_TEST_CASE_V3("ObviouslyInfinite", + "005", + "Relations preserve length", + "[quick]") { + IsObviouslyInfinite ioi(3); + std::vector v = {"aaa", "bbc", "cccc", "bcba", "bb", "cb", "cba", "bbc"}; - ioi.add_rules(v.cbegin(), v.cend()); + ioi.add_rules_no_checks("abc", v.cbegin(), v.cend()); REQUIRE(ioi.result()); } - LIBSEMIGROUPS_TEST_CASE("ObviouslyInfinite", - "006", - "Matrix has non empty kernel", - "[quick]") { - detail::IsObviouslyInfinite ioi(2); + LIBSEMIGROUPS_TEST_CASE_V3("ObviouslyInfinite", + "006", + "Matrix has non empty kernel", + "[quick]") { + IsObviouslyInfinite ioi(2); std::vector v = {"aa", "bba", "bbaa", "bbbbbb"}; - ioi.add_rules(v.cbegin(), v.cend()); + ioi.add_rules_no_checks("ab", v.cbegin(), v.cend()); REQUIRE(ioi.result()); } - LIBSEMIGROUPS_TEST_CASE("ObviouslyInfinite", - "007", - "Free product of trivial semigroups", - "[quick]") { - detail::IsObviouslyInfinite ioi(2); - std::vector v = {"a", "aa", "b", "bb"}; - ioi.add_rules(v.cbegin(), v.cend()); + LIBSEMIGROUPS_TEST_CASE_V3("ObviouslyInfinite", + "007", + "Free product of trivial semigroups", + "[quick]") { + IsObviouslyInfinite ioi(2); + std::vector v = {"a", "aa", "b", "bb"}; + ioi.add_rules_no_checks("ab", v.cbegin(), v.cend()); REQUIRE(ioi.result()); } - LIBSEMIGROUPS_TEST_CASE("ObviouslyInfinite", - "008", - "Another free product", - "[quick]") { - detail::IsObviouslyInfinite ioi(5); - std::vector v + LIBSEMIGROUPS_TEST_CASE_V3("ObviouslyInfinite", + "008", + "Another free product", + "[quick]") { + IsObviouslyInfinite ioi(5); + std::vector v = {"a", "aa", "b", "bb", "abe", "eee", "dc", "c", "cc", "ddd"}; - ioi.add_rules(v.cbegin(), v.cend()); + ioi.add_rules_no_checks("abcde", v.cbegin(), v.cend()); REQUIRE(ioi.result()); } - LIBSEMIGROUPS_TEST_CASE("ObviouslyInfinite", - "009", - "Infinite but not obviously so", - "[quick]") { - detail::IsObviouslyInfinite ioi(2); - std::vector v = {"a", "abb", "b", "baa"}; - ioi.add_rules(v.cbegin(), v.cend()); + LIBSEMIGROUPS_TEST_CASE_V3("ObviouslyInfinite", + "009", + "Infinite but not obviously so", + "[quick]") { + IsObviouslyInfinite ioi(2); + std::vector v = {"a", "abb", "b", "baa"}; + ioi.add_rules_no_checks("ab", v.cbegin(), v.cend()); REQUIRE(!ioi.result()); // Currently the test does not pass, but the semigroup // is infinite! Contains (ab)^n for all n. } - LIBSEMIGROUPS_TEST_CASE("ObviouslyInfinite", - "010", - "Finite semigroup", - "[quick]") { - detail::IsObviouslyInfinite ioi(3); - std::vector v + LIBSEMIGROUPS_TEST_CASE_V3("ObviouslyInfinite", + "010", + "Finite semigroup", + "[quick]") { + IsObviouslyInfinite ioi(3); + std::vector v = {"a", "aa", "b", "bb", "", "cc", "ac", "cb", "abab", "ab"}; - ioi.add_rules(v.cbegin(), v.cend()); + ioi.add_rules_no_checks("abc", v.cbegin(), v.cend()); REQUIRE(!ioi.result()); // This is a presentation for a finite semigroup so // we should never detect it as obviously infinite - }*/ + } - LIBSEMIGROUPS_TEST_CASE("ObviouslyInfinite", - "011", - "Multiple rule additions", - "[quick][integer-alphabet]") { - detail::IsObviouslyInfinite ioi(3); - std::vector v - = {{0, 0, 1, 0, 1, 1, 0, 2, 2, 0, 1, 1, 2}, - {0}, - {0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 2, 1, 1, 1}, - {1, 1}}; - ioi.add_rules(v.cbegin(), v.cend()); + LIBSEMIGROUPS_TEST_CASE_V3("ObviouslyInfinite", + "011", + "Multiple rule additions", + "[quick][integer-alphabet]") { + IsObviouslyInfinite ioi(3); + auto w = 00_w; + std::vector v + = {0010110220112_w, 0_w, 0001111000111102111_w, 11_w}; + ioi.add_rules_no_checks(v.cbegin(), v.cend()); REQUIRE(ioi.result()); - v = {{2, 2}, {1, 0, 1, 0, 1, 0, 1}}; - ioi.add_rules(v.cbegin(), v.cend()); + v = {22_w, 1010101_w}; + ioi.add_rules_no_checks(v.cbegin(), v.cend()); #ifdef LIBSEMIGROUPS_EIGEN_ENABLED REQUIRE(ioi.result()); #else REQUIRE(!ioi.result()); #endif - v = {{1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1}, - {1, 2, 1, 0, 1}, - {0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 2, 2, 2}, - {0, 0}, + v = {111000001110000011100000111222111111111_w, + 12101_w, + 00010100111222_w, + 00_w, {}, - {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2}}; - ioi.add_rules(v.cbegin(), v.cend()); + 00000001111111112_w}; + ioi.add_rules_no_checks(v.cbegin(), v.cend()); #ifdef LIBSEMIGROUPS_EIGEN_ENABLED REQUIRE(ioi.result()); #else REQUIRE(!ioi.result()); #endif - v = {{0}, {0, 0}}; - ioi.add_rules(v.cbegin(), v.cend()); + v = {0_w, 00_w}; + ioi.add_rules_no_checks(v.cbegin(), v.cend()); REQUIRE(!ioi.result()); - v = {{1}, {1, 1, 0, 0}, {2, 0, 0}, {2, 2, 1, 1, 1, 0, 0}}; - ioi.add_rules(v.cbegin(), v.cend()); + v = {1_w, 1100_w, 200_w, 2211100_w}; + ioi.add_rules_no_checks(v.cbegin(), v.cend()); REQUIRE(!ioi.result()); } - LIBSEMIGROUPS_TEST_CASE( + LIBSEMIGROUPS_TEST_CASE_V3( "ObviouslyInfinite", "012", "A power of the generator 'b' does not occur on its own in any relation", "[quick][integer-alphabet]") { - detail::IsObviouslyInfinite ioi(2); - std::vector v = {{0, 1}, {0}, {0, 1, 0}, {1, 0}}; - ioi.add_rules(v.cbegin(), v.cend()); + IsObviouslyInfinite ioi(2); + std::vector v = {01_w, 0_w, 010_w, 10_w}; + ioi.add_rules_no_checks(v.cbegin(), v.cend()); REQUIRE(ioi.result()); } - LIBSEMIGROUPS_TEST_CASE( + LIBSEMIGROUPS_TEST_CASE_V3( "ObviouslyInfinite", "013", "Preserves the number of occurrences of the generator 'a'", "[quick][integer-alphabet]") { - detail::IsObviouslyInfinite ioi(2); - std::vector v - = {{0, 1, 0}, {0, 0}, {1, 1}, {1}, {0, 1, 0, 1}, {0, 1, 1, 1, 0}}; - ioi.add_rules(v.cbegin(), v.cend()); + IsObviouslyInfinite ioi(2); + std::vector v = {010_w, 00_w, 11_w, 1_w, 0101_w, 01110_w}; + ioi.add_rules_no_checks(v.cbegin(), v.cend()); REQUIRE(ioi.result()); } - LIBSEMIGROUPS_TEST_CASE("ObviouslyInfinite", - "014", - "Less relations than generators", - "[quick][integer-alphabet]") { - detail::IsObviouslyInfinite ioi(3); - std::vector v = {{0, 1, 0}, {1, 2}, {2, 0}, {1}}; - ioi.add_rules(v.cbegin(), v.cend()); + LIBSEMIGROUPS_TEST_CASE_V3("ObviouslyInfinite", + "014", + "Less relations than generators", + "[quick][integer-alphabet]") { + IsObviouslyInfinite ioi(3); + std::vector v = {010_w, 12_w, 20_w, 1_w}; + ioi.add_rules_no_checks(v.cbegin(), v.cend()); REQUIRE(ioi.result()); } - LIBSEMIGROUPS_TEST_CASE("ObviouslyInfinite", - "015", - "Relations preserve length", - "[quick][integer-alphabet]") { - detail::IsObviouslyInfinite ioi(3); - std::vector v = {{0, 0, 0}, - {1, 1, 2}, - {2, 2, 2, 2}, - {1, 2, 1, 0}, - {1, 1}, - {2, 1}, - {2, 1, 0}, - {1, 1, 2}}; - ioi.add_rules(v.cbegin(), v.cend()); + LIBSEMIGROUPS_TEST_CASE_V3("ObviouslyInfinite", + "015", + "Relations preserve length", + "[quick][integer-alphabet]") { + IsObviouslyInfinite ioi(3); + std::vector v + = {000_w, 112_w, 2222_w, 1210_w, 11_w, 21_w, 210_w, 112_w}; + ioi.add_rules_no_checks(v.cbegin(), v.cend()); REQUIRE(ioi.result()); } - LIBSEMIGROUPS_TEST_CASE("ObviouslyInfinite", - "016", - "Matrix has non empty kernel", - "[quick][integer-alphabet]") { - detail::IsObviouslyInfinite ioi(2); - std::vector v - = {{0, 0}, {1, 1, 0}, {1, 1, 0, 0}, {1, 1, 1, 1, 1, 1}}; - ioi.add_rules(v.cbegin(), v.cend()); + LIBSEMIGROUPS_TEST_CASE_V3("ObviouslyInfinite", + "016", + "Matrix has non empty kernel", + "[quick][integer-alphabet]") { + IsObviouslyInfinite ioi(2); + std::vector v = {00_w, 110_w, 1100_w, 111111_w}; + ioi.add_rules_no_checks(v.cbegin(), v.cend()); #ifdef LIBSEMIGROUPS_EIGEN_ENABLED REQUIRE(ioi.result()); #else @@ -260,63 +252,47 @@ namespace libsemigroups { #endif } - LIBSEMIGROUPS_TEST_CASE("ObviouslyInfinite", - "017", - "Free product of trivial semigroups", - "[quick][integer-alphabet]") { - detail::IsObviouslyInfinite ioi(2); - std::vector v = {{0}, {0, 0}, {1}, {1, 1}}; - ioi.add_rules(v.cbegin(), v.cend()); + LIBSEMIGROUPS_TEST_CASE_V3("ObviouslyInfinite", + "017", + "Free product of trivial semigroups", + "[quick][integer-alphabet]") { + IsObviouslyInfinite ioi(2); + std::vector v = {0_w, 00_w, 1_w, 11_w}; + ioi.add_rules_no_checks(v.cbegin(), v.cend()); REQUIRE(ioi.result()); } - LIBSEMIGROUPS_TEST_CASE("ObviouslyInfinite", - "018", - "Another free product", - "[quick][integer-alphabet]") { - detail::IsObviouslyInfinite ioi(5); - std::vector v = {{0}, - {0, 0}, - {1}, - {1, 1}, - {0, 1, 4}, - {4, 4, 4}, - {3, 2}, - {2}, - {2, 2}, - {3, 3, 3}}; - ioi.add_rules(v.cbegin(), v.cend()); + LIBSEMIGROUPS_TEST_CASE_V3("ObviouslyInfinite", + "018", + "Another free product", + "[quick][integer-alphabet]") { + IsObviouslyInfinite ioi(5); + std::vector v + = {0_w, 00_w, 1_w, 11_w, 014_w, 444_w, 32_w, 2_w, 22_w, 333_w}; + ioi.add_rules_no_checks(v.cbegin(), v.cend()); REQUIRE(ioi.result()); } - LIBSEMIGROUPS_TEST_CASE("ObviouslyInfinite", - "019", - "Infinite but not obviously so", - "[quick][integer-alphabet]") { - detail::IsObviouslyInfinite ioi(2); - std::vector v = {{0}, {0, 1, 1}, {1}, {1, 0, 0}}; - ioi.add_rules(v.cbegin(), v.cend()); + LIBSEMIGROUPS_TEST_CASE_V3("ObviouslyInfinite", + "019", + "Infinite but not obviously so", + "[quick][integer-alphabet]") { + IsObviouslyInfinite ioi(2); + std::vector v = {0_w, 011_w, 1_w, 100_w}; + ioi.add_rules_no_checks(v.cbegin(), v.cend()); REQUIRE(!ioi.result()); // Currently the test does not pass, but the semigroup // is infinite! Contains (ab)^n for all n. } - LIBSEMIGROUPS_TEST_CASE("ObviouslyInfinite", - "020", - "Finite semigroup", - "[quick][integer-alphabet]") { - detail::IsObviouslyInfinite ioi(3); - std::vector v = {{0}, - {0, 0}, - {1}, - {1, 1}, - {}, - {2, 2}, - {0, 2}, - {2, 1}, - {0, 1, 0, 1}, - {0, 1}}; - ioi.add_rules(v.cbegin(), v.cend()); + LIBSEMIGROUPS_TEST_CASE_V3("ObviouslyInfinite", + "020", + "Finite semigroup", + "[quick][integer-alphabet]") { + IsObviouslyInfinite ioi(3); + std::vector v + = {0_w, 00_w, 1_w, 11_w, {}, 22_w, 02_w, 21_w, 0101_w, 01_w}; + ioi.add_rules_no_checks(v.cbegin(), v.cend()); REQUIRE(!ioi.result()); // This is a presentation for a finite semigroup so // we should never detect it as obviously infinite