Skip to content

Commit

Permalink
Refactor x4
Browse files Browse the repository at this point in the history
  • Loading branch information
james-d-mitchell committed Dec 13, 2024
1 parent cee21ce commit b8e344a
Show file tree
Hide file tree
Showing 3 changed files with 245 additions and 5 deletions.
7 changes: 4 additions & 3 deletions include/libsemigroups/cong-intf.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,8 @@ namespace libsemigroups {
//! This function returns the generating pairs of the congruence
//! represented by any derived class of a CongruenceInterface. This is
//! always a std::vector of \ref word_type, regardless of the
//! \ref native_presentation_type of the derived class.
//! type of the presentation used by the implementation in the derived
//! class.
//!
//! \returns
//! A const reference to the generating pairs.
Expand Down Expand Up @@ -277,8 +278,8 @@ namespace libsemigroups {
//! This page contains documentation for helper functions for the classes
//! Congruence, Kambites, KnuthBendix, and \ref todd_coxeter_class_group
//! "ToddCoxeter". The functions documented on this page belong to all of
//! the namespaces \ref cong_intf_helpers_group "congruence_interface", \ref
//! congruence, \ref kambites, \ref knuth_bendix, and \ref
//! the namespaces \ref cong_intf_helpers_group "congruence_interface",
//! ``congruence``, \ref kambites, \ref knuth_bendix, and \ref
//! todd_coxeter_helpers_group "todd_coxeter".

////////////////////////////////////////////////////////////////////////
Expand Down
39 changes: 37 additions & 2 deletions include/libsemigroups/cong.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,15 +146,15 @@ namespace libsemigroups {
// No rvalue ref version because we anyway must copy p multiple times
Congruence& init(congruence_kind knd, Presentation<word_type> const& p);

//! \copydoc Congruence(congruence_kind, Presentation<word_type>&)
//! \copydoc Congruence(congruence_kind, Presentation<word_type> const&)
// No rvalue ref version because we are not able to use p directly anyway
template <typename Word>
Congruence(congruence_kind knd, Presentation<Word> const& p)
: Congruence(knd, to_presentation<word_type>(p, [](auto const& x) {
return x;
})) {}

//! \copydoc init(congruence_kind, Presentation<word_type>&)
//! \copydoc init(congruence_kind, Presentation<word_type> const&)
// No rvalue ref version because we are not able to use p directly anyway
template <typename Word>
Congruence& init(congruence_kind knd, Presentation<Word> const& p) {
Expand Down Expand Up @@ -291,6 +291,22 @@ namespace libsemigroups {
using CongruenceInterface::currently_contains;

public:
//! \brief Check containment of a pair of words via iterators.
//!
//! This function checks whether or not the words represented by the ranges
//! \p first1 to \p last1 and \p first2 to \p last2 are already known to be
//! contained in the congruence represented by a \ref Congruence instance.
//! This function performs no enumeration, so it is possible for the words
//! to be contained in the congruence, but that this is not currently known.
//!
//! \cong_intf_params_contains
//!
//! \returns
//! * tril::TRUE if the words are known to belong to the congruence;
//! * tril::FALSE if the words are known to not belong to the congruence;
//! * tril::unknown otherwise.
//!
//! \cong_intf_throws_if_letters_out_of_bounds
template <typename Iterator1,
typename Iterator2,
typename Iterator3,
Expand All @@ -302,6 +318,22 @@ namespace libsemigroups {
return currently_contains<Congruence>(first1, last1, first2, last2);
}

//! \brief Check containment of a pair of words via iterators.
//!
//! This function checks whether or not the words represented by the ranges
//! \p first1 to \p last1 and \p first2 to \p last2 are contained in the
//! congruence represented by a \ref todd_coxeter_class_group "ToddCoxeter"
//! instance. This function triggers a full enumeration, which may never
//! terminate.
//!
//! \cong_intf_params_contains
//!
//! \returns Whether or not the pair belongs to the congruence.
//!
//! \warning Determining the number of classes is undecidable in general,
//! and this may never terminate.
//!
//! \cong_intf_warn_assume_letters_in_bounds
template <typename Iterator1,
typename Iterator2,
typename Iterator3,
Expand Down Expand Up @@ -354,6 +386,9 @@ namespace libsemigroups {
//! * tril::unknown otherwise.
//!
//! \cong_intf_throws_if_letters_out_of_bounds
//!
//! \warning Determining the number of classes is undecidable in general,
//! and this may never terminate.
template <typename Iterator1,
typename Iterator2,
typename Iterator3,
Expand Down
204 changes: 204 additions & 0 deletions include/libsemigroups/cong.tpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
//
// libsemigroups - C++ library for semigroups and monoids
// Copyright (C) 2024 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 <http://www.gnu.org/licenses/>.
//

namespace libsemigroups {
////////////////////////////////////////////////////////////////////////
// Congruence - out of line implementations
////////////////////////////////////////////////////////////////////////

template <typename Node>
Congruence& Congruence::init(congruence_kind knd,
FroidurePinBase& S,
WordGraph<Node> const& wg) {
if (S.is_finite() != tril::FALSE) {
S.run();
} else {
LIBSEMIGROUPS_EXCEPTION(
"the 2nd argument does not represent a finite semigroup!");
}
CongruenceInterface::init(knd);

// TODO(later) if necessary make a runner that tries to S.run(), then get
// the Cayley graph and use that in the ToddCoxeter, at present that'll
// happen here in the constructor, same for the creation of the
// presentation this could take place in the Runner so that they are done
// in parallel
add_runner(std::make_shared<ToddCoxeter>(to_todd_coxeter(knd, S, wg)));

auto tc = to_todd_coxeter(knd, S, wg);
tc.strategy(ToddCoxeter::options::strategy::felsch);
add_runner(std::make_shared<ToddCoxeter>(std::move(tc)));

tc = ToddCoxeter(knd, to_presentation<word_type>(S));
add_runner(std::make_shared<ToddCoxeter>(std::move(tc)));

tc = ToddCoxeter(knd, to_presentation<word_type>(S));
tc.strategy(ToddCoxeter::options::strategy::felsch);
add_runner(std::make_shared<ToddCoxeter>(std::move(tc)));

return *this;
}

template <typename Iterator1,
typename Iterator2,
typename Iterator3,
typename Iterator4>
[[nodiscard]] tril
Congruence::currently_contains_no_checks(Iterator1 first1,
Iterator2 last1,
Iterator3 first2,
Iterator4 last2) const {
if (finished()) {
auto winner_kind = _runner_kinds[_race.winner_index()];
if (winner_kind == RunnerKind::TC) {
return std::static_pointer_cast<ToddCoxeter>(_race.winner())
->contains_no_checks(first1, last1, first2, last2)
? tril::TRUE
: tril::FALSE;
} else if (winner_kind == RunnerKind::KB) {
return std::static_pointer_cast<KnuthBendix<>>(_race.winner())
->contains_no_checks(first1, last1, first2, last2)
? tril::TRUE
: tril::FALSE;
} else {
LIBSEMIGROUPS_ASSERT(winner_kind == RunnerKind::K);
return std::static_pointer_cast<Kambites<word_type>>(_race.winner())
->contains_no_checks(first1, last1, first2, last2)
? tril::TRUE
: tril::FALSE;
}
}
init_runners();
tril result = tril::unknown;
for (auto const& [i, runner] : rx::enumerate(_race)) {
if (_runner_kinds[i] == RunnerKind::TC) {
result
= std::static_pointer_cast<ToddCoxeter>(runner)
->currently_contains_no_checks(first1, last1, first2, last2);
} else if (_runner_kinds[i] == RunnerKind::KB) {
result
= std::static_pointer_cast<KnuthBendix<>>(runner)
->currently_contains_no_checks(first1, last1, first2, last2);
} else {
LIBSEMIGROUPS_ASSERT(_runner_kinds[i] == RunnerKind::K);
result
= std::static_pointer_cast<Kambites<word_type>>(runner)
->currently_contains_no_checks(first1, last1, first2, last2);
}
if (result != tril::unknown) {
break;
}
}
return result;
}

template <typename Iterator1,
typename Iterator2,
typename Iterator3,
typename Iterator4>
[[nodiscard]] bool Congruence::contains_no_checks(Iterator1 first1,
Iterator2 last1,
Iterator3 first2,
Iterator4 last2) {
run();
LIBSEMIGROUPS_ASSERT(_race.winner_index() != UNDEFINED);
auto winner_kind = _runner_kinds[_race.winner_index()];
if (winner_kind == RunnerKind::TC) {
return std::static_pointer_cast<ToddCoxeter>(_race.winner())
->contains_no_checks(first1, last1, first2, last2);
} else if (winner_kind == RunnerKind::KB) {
return std::static_pointer_cast<KnuthBendix<>>(_race.winner())
->contains_no_checks(first1, last1, first2, last2);
} else {
LIBSEMIGROUPS_ASSERT(winner_kind == RunnerKind::K);
return std::static_pointer_cast<Kambites<word_type>>(_race.winner())
->contains_no_checks(first1, last1, first2, last2);
}
}

template <typename Iterator1, typename Iterator2>
void Congruence::throw_if_letter_out_of_bounds(Iterator1 first,
Iterator2 last) const {
if (!_race.empty()) {
size_t index = (finished() ? _race.winner_index() : 0);

if (_runner_kinds[index] == RunnerKind::TC) {
std::static_pointer_cast<ToddCoxeter>(*_race.begin())
->throw_if_letter_out_of_bounds(first, last);
} else if (_runner_kinds[index] == RunnerKind::KB) {
std::static_pointer_cast<KnuthBendix<>>(*_race.begin())
->throw_if_letter_out_of_bounds(first, last);
} else {
LIBSEMIGROUPS_ASSERT(_runner_kinds[index] == RunnerKind::K);
std::static_pointer_cast<Kambites<word_type>>(*_race.begin())
->throw_if_letter_out_of_bounds(first, last);
}
return;
}
LIBSEMIGROUPS_EXCEPTION(
"No presentation has been set, so cannot validate the word!");
}

namespace congruence {
template <typename Range>
std::vector<std::vector<std::decay_t<typename Range::output_type>>>
partition(Congruence& cong, Range r) {
cong.run();
if (cong.has<ToddCoxeter>() && cong.get<ToddCoxeter>()->finished()) {
return todd_coxeter::partition(*cong.get<ToddCoxeter>(), r);
} else if (cong.has<KnuthBendix<>>()
&& cong.get<KnuthBendix<>>()->finished()) {
return knuth_bendix::partition(*cong.get<KnuthBendix<>>(), r);
} else if (cong.has<Kambites<word_type>>()
&& cong.get<Kambites<word_type>>()->success()) {
return kambites::partition(*cong.get<Kambites<word_type>>(), r);
}
LIBSEMIGROUPS_EXCEPTION("Cannot compute the non-trivial classes!");
}

template <typename Range, typename Word, typename>
std::vector<std::vector<Word>> non_trivial_classes(Congruence& ci,
Range r) {
auto result = partition(ci, r);
result.erase(
std::remove_if(result.begin(),
result.end(),
[](auto const& x) -> bool { return x.size() <= 1; }),
result.end());
return result;
}

// The following doesn't work, because the types of the two returned values
// aren't the same.
// TODO(1) implement a class containing a variant for this.
// template <typename Word>
// auto normal_forms(Congruence& cong) {
// cong.run();
// if (cong.has<ToddCoxeter>() && cong.get<ToddCoxeter>()->finished()) {
// return todd_coxeter::normal_forms(*cong.get<ToddCoxeter>());
// } else if (cong.has<KnuthBendix<>>()
// && cong.get<KnuthBendix<>>()->finished()) {
// return knuth_bendix::normal_forms(*cong.get<KnuthBendix<>>());
// }
// // There's currently no normal_forms function for Kambites, so can't
// // return anything in that case.
// LIBSEMIGROUPS_EXCEPTION("Cannot compute the non-trivial classes!");
// }

} // namespace congruence
} // namespace libsemigroups

0 comments on commit b8e344a

Please sign in to comment.