//
// libsemigroups - C++ library for semigroups and monoids
// Copyright (C) 2022 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/>.
//
#include <cstddef> // for size_t
#include <iostream> // for cout
#include <stdexcept> // for number_of_congruencestime_error
#include <unordered_set> // for unordered_set
#include <vector> // for vector
#include "catch.hpp" // for REQUIRE, REQUIRE_THROWS_AS, REQUI...
#include "test-main.hpp" // for LIBSEMIGROUPS_TEST_CASE
#include "libsemigroups/bipart.hpp" // for Bipartition
#include "libsemigroups/config.hpp" // for LIBSEMIGROUPS_ENABLE_STATS
#include "libsemigroups/digraph-helper.hpp" // for action_digraph_helper
#include "libsemigroups/fpsemi-examples.hpp" // for brauer_monoid etc
#include "libsemigroups/froidure-pin.hpp" // for FroidurePin
#include "libsemigroups/knuth-bendix.hpp" // for redundant_rule
#include "libsemigroups/make-froidure-pin.hpp" // for make
#include "libsemigroups/make-present.hpp" // for make
#include "libsemigroups/sims1.hpp" // for Sims1
#include "libsemigroups/transf.hpp" // for Transf
#include "libsemigroups/types.hpp" // for word_type
//
namespace libsemigroups {
using Sims1_ = Sims1<uint32_t>;
using digraph_type = typename Sims1_::digraph_type;
using node_type = typename digraph_type::node_type;
using fpsemigroup::author;
using fpsemigroup::make;
using fpsemigroup::brauer_monoid;
using fpsemigroup::chinese_monoid;
using fpsemigroup::fibonacci_semigroup;
using fpsemigroup::full_transformation_monoid;
using fpsemigroup::monogenic_semigroup;
using fpsemigroup::partition_monoid;
using fpsemigroup::plactic_monoid;
using fpsemigroup::rectangular_band;
using fpsemigroup::rook_monoid;
using fpsemigroup::singular_brauer_monoid;
using fpsemigroup::stellar_monoid;
using fpsemigroup::stylic_monoid;
using fpsemigroup::temperley_lieb_monoid;
namespace {
template < typename P>
void check_extra(congruence_kind ck, P const& p, P const& e, size_t n) {
P f = e;
if (ck == congruence_kind::left) {
presentation::reverse(f);
}
auto foo = [&f]( auto const& ad) {
using action_digraph_helper::follow_path_nc;
for ( auto it = f.rules.cbegin(); it != f.rules.cend(); it += 2) {
if (follow_path_nc(ad, 0, *it) != follow_path_nc(ad, 0, *(it + 1))) {
return false;
}
}
return true;
};
Sims1_ S(ck);
S.short_rules(p);
Sims1_ T(ck);
T.short_rules(p).extra(e);
REQUIRE( static_cast<uint64_t>(std::count_if(S.cbegin(n), S.cend(n), foo))
== T.number_of_congruences(n));
}
constexpr unsigned int factorial( unsigned int n) {
return n > 1 ? n * factorial(n - 1) : 1;
}
} // namespace
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"000",
"fp example 1",
"[quick][low-index]") {
auto rg = ReportGuard( false);
Presentation<word_type> p;
p.contains_empty_word( true);
p.alphabet({ 0, 1});
presentation::add_rule_and_check(p, { 0, 0, 0}, { 0});
presentation::add_rule_and_check(p, { 1, 1}, { 1});
presentation::add_rule_and_check(p, { 0, 1, 0, 1}, { 0});
{
Sims1_ S(congruence_kind::right);
REQUIRE(S.short_rules(p).number_of_threads( 2).number_of_congruences( 5)
== 6);
REQUIRE_THROWS_AS(S.number_of_congruences( 0), LibsemigroupsException);
REQUIRE_THROWS_AS(S.find_if( 0, []( auto) { return false; }),
LibsemigroupsException);
REQUIRE_THROWS_AS(S.for_each( 0, []( auto) { return false; }),
LibsemigroupsException);
REQUIRE_THROWS_AS(S.cbegin( 0), LibsemigroupsException);
REQUIRE_THROWS_AS(S.cend( 0), LibsemigroupsException);
REQUIRE(S.number_of_congruences( 1) == 1);
auto it = S.cbegin( 1);
REQUIRE(*it == action_digraph_helper::make<node_type>( 1, {{ 0, 0}}));
it = S.cbegin( 5);
REQUIRE(*(it++) == action_digraph_helper::make<node_type>( 5, {{ 0, 0}}));
REQUIRE(*(it++)
== action_digraph_helper::make<node_type>( 5, {{ 1, 0}, { 1, 1}}));
REQUIRE(*(it++)
== action_digraph_helper::make<node_type>( 5, {{ 1, 1}, { 1, 1}}));
REQUIRE(*(it++)
== action_digraph_helper::make<node_type>(
5, {{ 1, 2}, { 1, 1}, { 1, 2}}));
REQUIRE(*(it++)
== action_digraph_helper::make<node_type>(
5, {{ 1, 2}, { 1, 1}, { 2, 2}}));
REQUIRE(*(it++)
== action_digraph_helper::make<node_type>(
5, {{ 1, 2}, { 1, 1}, { 3, 2}, { 3, 3}}));
REQUIRE(*(it++) == ActionDigraph<node_type>( 0, 2));
REQUIRE(*(it++) == ActionDigraph<node_type>( 0, 2));
REQUIRE(*(it++) == ActionDigraph<node_type>( 0, 2));
it = S.cbegin( 3);
REQUIRE(*it == action_digraph_helper::make<node_type>( 3, {{ 0, 0}}));
}
// [[[0, 0]],
// [[1, 2], [1, 1], [3, 2], [3, 3]],
// [[1, 2], [1, 1], [2, 2]],
// [[1, 2], [1, 1], [1, 2]],
// [[1, 1], [1, 1]],
// [[1, 0], [1, 1]]]
{
Sims1_ S(congruence_kind::left);
REQUIRE(S.short_rules(p).number_of_congruences( 5) == 9);
for ( auto it = S.cbegin( 5); it != S.cend( 5); ++it) {
REQUIRE(action_digraph_helper::follow_path_nc(*it, 0, { 1, 0, 1, 0})
== action_digraph_helper::follow_path_nc(*it, 0, { 0}));
}
}
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"001",
"fp example 2",
"[quick][low-index]") {
auto rg = ReportGuard( false);
Presentation<word_type> p;
p.contains_empty_word( true);
p.alphabet({ 0, 1, 2});
presentation::add_rule_and_check(p, { 0, 1, 0}, { 0, 0});
presentation::add_rule_and_check(p, { 2, 2}, { 0, 0});
presentation::add_rule_and_check(p, { 0, 0, 0}, { 0, 0});
presentation::add_rule_and_check(p, { 2, 1}, { 1, 2});
presentation::add_rule_and_check(p, { 2, 0}, { 0, 0});
presentation::add_rule_and_check(p, { 1, 1}, { 1});
presentation::add_rule_and_check(p, { 0, 2}, { 0, 0});
{
Sims1_ S(congruence_kind::right);
S.short_rules(p);
REQUIRE(S.number_of_congruences( 1) == 1);
REQUIRE(S.number_of_congruences( 2) == 3);
REQUIRE(S.number_of_congruences( 3) == 13);
REQUIRE(S.number_of_congruences( 4) == 36);
REQUIRE(S.number_of_congruences( 5) == 82);
REQUIRE(S.number_of_congruences( 6) == 135);
REQUIRE(S.number_of_congruences( 7) == 166);
REQUIRE(S.number_of_congruences( 8) == 175);
REQUIRE(S.number_of_congruences( 9) == 176);
REQUIRE(S.number_of_congruences( 10) == 176);
auto it = S.cbegin( 2);
REQUIRE(*(it++)
== action_digraph_helper::make<node_type>( 2, {{ 0, 0, 0}}));
REQUIRE(
*(it++)
== action_digraph_helper::make<node_type>( 2, {{ 1, 0, 1}, { 1, 1, 1}}));
REQUIRE(
*(it++)
== action_digraph_helper::make<node_type>( 2, {{ 1, 1, 1}, { 1, 1, 1}}));
REQUIRE(*(it++) == ActionDigraph<node_type>( 0, 3));
REQUIRE(*(it++) == ActionDigraph<node_type>( 0, 3));
}
{
Sims1_ S(congruence_kind::left);
S.short_rules(p);
REQUIRE(S.number_of_congruences( 11) == 176);
}
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"002",
"ToddCoxeter failing example",
"[quick][low-index]") {
auto rg = ReportGuard( false);
Presentation<word_type> p;
p.contains_empty_word( false);
// a A b B c C e
p.alphabet({ 0, 1, 2, 3, 4, 5, 6});
presentation::add_identity_rules(p, 6);
presentation::add_inverse_rules(p, { 1, 0, 3, 2, 5, 4, 6}, 6);
presentation::add_rule_and_check(p, { 0, 0, 5, 0, 4}, { 6});
presentation::add_rule_and_check(p, { 0, 4, 2, 2, 1, 5, 2}, { 6});
presentation::add_rule_and_check(p, { 1, 3, 0, 2, 4, 4, 4}, { 6});
Sims1_ S(congruence_kind::right);
S.short_rules(p);
REQUIRE(S.number_of_congruences( 1) == 1);
REQUIRE(S.number_of_congruences( 3) == 14);
REQUIRE(S.number_of_congruences( 4) == 14);
REQUIRE(S.number_of_congruences( 5) == 14);
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"003",
"ToddCoxeter failing example",
"[quick][low-index]") {
auto rg = ReportGuard( false);
Presentation<std::string> p;
p.contains_empty_word( false);
p.alphabet( "aAbBcCe");
presentation::add_identity_rules(p, 'e');
presentation::add_inverse_rules(p, "AaBbCce", 'e');
presentation::add_rule_and_check(p, "aaCac", "e");
presentation::add_rule_and_check(p, "acbbACb", "e");
presentation::add_rule_and_check(p, "ABabccc", "e");
Sims1_ S(congruence_kind::right);
S.short_rules(p);
REQUIRE(S.number_of_congruences( 3) == 14);
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"004",
"partition_monoid(2) right",
"[quick][low-index]") {
auto rg = ReportGuard( false);
Presentation<word_type> p;
p.contains_empty_word( false);
p.alphabet({ 0, 1, 2, 3});
presentation::add_identity_rules(p, 0);
presentation::add_rule_and_check(p, { 1, 1}, { 0});
presentation::add_rule_and_check(p, { 1, 3}, { 3});
presentation::add_rule_and_check(p, { 2, 2}, { 2});
presentation::add_rule_and_check(p, { 3, 1}, { 3});
presentation::add_rule_and_check(p, { 3, 3}, { 3});
presentation::add_rule_and_check(p, { 2, 3, 2}, { 2});
presentation::add_rule_and_check(p, { 3, 2, 3}, { 3});
presentation::add_rule_and_check(p, { 1, 2, 1, 2}, { 2, 1, 2});
presentation::add_rule_and_check(p, { 2, 1, 2, 1}, { 2, 1, 2});
Sims1_ S(congruence_kind::right);
S.short_rules(p);
REQUIRE(S.number_of_congruences( 2) == 4);
REQUIRE(S.number_of_congruences( 3) == 7);
REQUIRE(S.number_of_congruences( 4) == 14);
REQUIRE(S.number_of_congruences( 5) == 23);
REQUIRE(S.number_of_congruences( 6) == 36);
REQUIRE(S.number_of_congruences( 7) == 51);
REQUIRE(S.number_of_congruences( 8) == 62);
REQUIRE(S.number_of_congruences( 9) == 74);
REQUIRE(S.number_of_congruences( 10) == 86);
REQUIRE(S.number_of_congruences( 11) == 95);
REQUIRE(S.number_of_congruences( 12) == 100);
REQUIRE(S.number_of_congruences( 13) == 102);
REQUIRE(S.number_of_congruences( 14) == 104);
REQUIRE(S.number_of_congruences( 15) == 105);
REQUIRE(S.number_of_congruences( 16) == 105);
REQUIRE(S.number_of_congruences( 17) == 105);
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"005",
"partition_monoid(3)",
"[standard][low-index][no-coverage]") {
auto rg = ReportGuard( false);
auto p
= make<Presentation<word_type>>(partition_monoid( 3, author::Machine));
REQUIRE(!p.contains_empty_word());
REQUIRE(p.alphabet() == word_type({ 0, 1, 2, 3, 4}));
Sims1_ S(congruence_kind::right);
S.short_rules(p).long_rule_length( 11).number_of_threads(
4); // This actually helps here!
REQUIRE(S.number_of_congruences( 17) == 1589);
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"006",
"full_transformation_monoid(3) right",
"[quick][low-index]") {
auto rg = ReportGuard( false);
FroidurePin<Transf< 3>> S({Transf< 3>::make({ 1, 2, 0}),
Transf< 3>::make({ 1, 0, 2}),
Transf< 3>::make({ 0, 1, 0})});
REQUIRE(S.size() == 27);
REQUIRE(S.number_of_generators() == 3);
REQUIRE(S.number_of_rules() == 16);
auto p = make<Presentation<word_type>>(S);
REQUIRE( static_cast<size_t>(std::distance(p.rules.cbegin(), p.rules.cend()))
== 2 * S.number_of_rules());
Sims1_ C(congruence_kind::right);
C.short_rules(p);
REQUIRE(C.number_of_congruences( 27) == 287);
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"007",
"full_transformation_monoid(3) left",
"[quick][low-index]") {
auto rg = ReportGuard( false);
FroidurePin<Transf< 3>> S(
{Transf< 3>({ 1, 2, 0}), Transf< 3>({ 1, 0, 2}), Transf< 3>({ 0, 1, 0})});
REQUIRE(S.size() == 27);
auto p = make<Presentation<word_type>>(S);
Sims1_ C(congruence_kind::left);
C.short_rules(p);
REQUIRE(C.number_of_congruences( 27) == 120);
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"008",
"full_transformation_monoid(4) left",
"[fail][low-index][babbage]") {
auto rg = ReportGuard( true);
auto p = make<Presentation<word_type>>(
full_transformation_monoid( 4, author::Iwahori));
REQUIRE(p.alphabet().size() == 4);
auto w = presentation::longest_common_subword(p);
while (!w.empty()) {
presentation::replace_subword(p, w);
w = presentation::longest_common_subword(p);
}
presentation::sort_each_rule(p);
presentation::sort_rules(p);
presentation::remove_duplicate_rules(p);
presentation::reduce_complements(p);
presentation::remove_trivial_rules(p);
do {
auto it = presentation::redundant_rule(p, std::chrono::milliseconds( 100));
p.rules.erase(it, it + 2);
} while (presentation::length(p) > 700);
Sims1_ C(congruence_kind::right);
C.short_rules(p);
// Takes about 1h31m to run!
REQUIRE(C.number_of_threads( 6).number_of_congruences( 256) == 22'069'828);
// Sims1_ C(congruence_kind::left);
// C.short_rules(p);
// REQUIRE(C.number_of_threads(6).number_of_congruences(256) == 120'121);
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"009",
"rook_monoid(2, 1)",
"[quick][low-index]") {
auto rg = ReportGuard( false);
Presentation<word_type> p;
p.contains_empty_word( false);
p.alphabet( 3);
for ( auto const& rel : rook_monoid( 2, 1)) {
p.add_rule_and_check(rel.first.cbegin(),
rel.first.cend(),
rel.second.cbegin(),
rel.second.cend());
}
Sims1_ C(congruence_kind::right);
C.short_rules(p);
REQUIRE(C.number_of_congruences( 7) == 10); // Should be 10
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"010",
"symmetric_inverse_monoid(2) from FroidurePin",
"[quick][low-index]") {
auto rg = ReportGuard( false);
FroidurePin<PPerm< 2>> S({PPerm< 2>({ 1, 0}), PPerm< 2>({ 0}, { 0}, 2)});
REQUIRE(S.size() == 7);
auto p = make<Presentation<word_type>>(S);
Sims1_ C(congruence_kind::left);
C.short_rules(p);
REQUIRE(C.number_of_congruences( 7) == 10);
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"011",
"symmetric_inverse_monoid(3)",
"[quick][low-index]") {
auto rg = ReportGuard( false);
Presentation<word_type> p;
p.contains_empty_word( false);
p.alphabet( 4);
for ( auto const& rel : rook_monoid( 3, 1)) {
p.add_rule_and_check(rel.first.cbegin(),
rel.first.cend(),
rel.second.cbegin(),
rel.second.cend());
}
Sims1_ C(congruence_kind::left);
C.short_rules(p);
REQUIRE(C.number_of_congruences( 34) == 274);
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"012",
"symmetric_inverse_monoid(4)",
"[extreme][low-index]") {
auto rg = ReportGuard( true);
Presentation<word_type> p;
p.contains_empty_word( false);
p.alphabet( 5);
for ( auto const& rel : rook_monoid( 4, 1)) {
p.add_rule_and_check(rel.first.cbegin(),
rel.first.cend(),
rel.second.cbegin(),
rel.second.cend());
}
presentation::remove_duplicate_rules(p);
presentation::sort_each_rule(p);
presentation::sort_rules(p);
REQUIRE(presentation::length(p) == 78);
REQUIRE(p.alphabet().size() == 5);
REQUIRE(*presentation::shortest_rule(p) == word_type({ 0, 0}));
REQUIRE(*(presentation::shortest_rule(p) + 1) == word_type({ 0}));
REQUIRE(presentation::longest_rule_length(p) == 8);
Sims1_ C(congruence_kind::right);
C.short_rules(p);
REQUIRE(C.number_of_threads(std::thread::hardware_concurrency())
.number_of_congruences( 209)
== 195'709);
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"013",
"symmetric_inverse_monoid(5)",
"[fail][low-index]") {
// This might take an extremely long time to terminate
auto rg = ReportGuard( true);
Presentation<word_type> p;
p.contains_empty_word( false);
p.alphabet( 6);
for ( auto const& rel : rook_monoid( 5, 1)) {
p.add_rule_and_check(rel.first.cbegin(),
rel.first.cend(),
rel.second.cbegin(),
rel.second.cend());
}
Sims1_ C(congruence_kind::left);
C.short_rules(p);
REQUIRE(C.number_of_threads( 6).number_of_congruences( 1'546) == 0);
// On 24/08/2022 JDM ran this for approx. 16 hours overnight on his laptop,
// the last line of output was:
// #4: Sims1: found 63'968'999 congruences in 52156s!
// #21: Sims1: found 759'256'468 congruences in 244617.546875
// #12: Sims1: found 943'233'501 congruences in 321019.531250!
// #30: Sims1: found 1'005'857'634 congruences in 350411.000000!
// #45: Sims1: found 1'314'588'296 congruences in 487405.000000!
// #20: Sims1: found 4'619'043'843 congruences in 2'334'184.500000!
// #49: Sims1: found 5'582'499'404 congruences in 2'912'877.000000!
// #10: Sims1: found 6'825'113'083 congruences in 3705611.250000!
//
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"014",
"temperley_lieb_monoid(3) from presentation",
"[quick][low-index]") {
auto rg = ReportGuard( false);
Presentation<word_type> p;
p.contains_empty_word( true);
p.alphabet( 2);
for ( auto const& rel : temperley_lieb_monoid( 3)) {
p.add_rule_and_check(rel.first.cbegin(),
rel.first.cend(),
rel.second.cbegin(),
rel.second.cend());
}
{
Sims1_ S(congruence_kind::right);
S.short_rules(p);
REQUIRE(S.number_of_congruences( 14) == 9);
}
{
Sims1_ S(congruence_kind::left);
S.short_rules(p);
REQUIRE(S.number_of_congruences( 14) == 9);
}
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"015",
"temperley_lieb_monoid(4) from presentation",
"[quick][low-index]") {
auto rg = ReportGuard( false);
Presentation<word_type> p;
p.contains_empty_word( true);
p.alphabet( 3);
for ( auto const& rel : temperley_lieb_monoid( 4)) {
p.add_rule_and_check(rel.first.cbegin(),
rel.first.cend(),
rel.second.cbegin(),
rel.second.cend());
}
{
Sims1_ S(congruence_kind::right);
S.short_rules(p);
REQUIRE(S.number_of_congruences( 14) == 79);
}
{
Sims1_ S(congruence_kind::left);
S.short_rules(p);
REQUIRE(S.number_of_congruences( 14) == 79);
}
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"016",
"fp semigroup containing given pairs #1",
"[quick][low-index]") {
auto rg = ReportGuard( false);
Presentation<word_type> p;
p.contains_empty_word( true);
p.alphabet({ 0, 1});
presentation::add_rule_and_check(p, { 0, 0, 0}, { 0});
presentation::add_rule_and_check(p, { 1, 1}, { 1});
presentation::add_rule_and_check(p, { 0, 1, 0, 1}, { 0});
Presentation<word_type> e;
e.contains_empty_word( true);
e.alphabet({ 0, 1});
presentation::add_rule_and_check(e, { 0}, { 1});
Sims1_ S(congruence_kind::right);
S.short_rules(p).extra(e);
REQUIRE(S.number_of_congruences( 5) == 2);
check_extra(congruence_kind::right, p, e, 5);
check_extra(congruence_kind::left, p, e, 5);
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"017",
"fp semigroup containing given pairs #2",
"[quick][low-index]") {
auto rg = ReportGuard( false);
Presentation<word_type> p;
p.contains_empty_word( true);
p.alphabet({ 0, 1});
presentation::add_rule_and_check(p, { 0, 0, 0}, { 0});
presentation::add_rule_and_check(p, { 1, 1}, { 1});
presentation::add_rule_and_check(p, { 0, 1, 0, 1}, { 0});
Presentation<word_type> e;
e.contains_empty_word( true);
e.alphabet({ 0, 1});
presentation::add_rule_and_check(e, { 0, 1}, { 1});
Sims1_ T(congruence_kind::right);
T.short_rules(p).extra(e);
REQUIRE(T.number_of_congruences( 5) == 2);
check_extra(congruence_kind::right, p, e, 5);
check_extra(congruence_kind::left, p, e, 5);
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"018",
"fp semigroup containing given pairs #3",
"[quick][low-index]") {
auto rg = ReportGuard( false);
Presentation<word_type> p;
p.contains_empty_word( true);
p.alphabet({ 0, 1});
presentation::add_rule_and_check(p, { 0, 0, 0}, { 0});
presentation::add_rule_and_check(p, { 1, 1}, { 1});
presentation::add_rule_and_check(p, { 0, 1, 0, 1}, { 0});
Presentation<word_type> e;
e.contains_empty_word( true);
e.alphabet({ 0, 1});
presentation::add_rule_and_check(e, { 0, 1, 0, 1}, { 0});
{
Sims1_ T(congruence_kind::right);
T.short_rules(p).extra(e);
REQUIRE(T.number_of_congruences( 5) == 6);
}
{
Sims1_ T(congruence_kind::left);
T.short_rules(p).extra(e);
REQUIRE(T.number_of_congruences( 5) == 9); // Verified with GAP
}
check_extra(congruence_kind::right, p, e, 5);
check_extra(congruence_kind::left, p, e, 5);
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"019",
"ToddCoxeter failing example",
"[quick][low-index]") {
auto rg = ReportGuard( false);
Presentation<std::string> p;
p.contains_empty_word( false);
p.alphabet( "aAbBcCe");
presentation::add_identity_rules(p, 'e');
presentation::add_inverse_rules(p, "AaBbCce", 'e');
presentation::add_rule_and_check(p, "aaCac", "e");
presentation::add_rule_and_check(p, "acbbACb", "e");
presentation::add_rule_and_check(p, "ABabccc", "e");
Presentation<std::string> e;
e.alphabet(p.alphabet());
presentation::add_rule_and_check(p, "a", "A");
presentation::add_rule_and_check(p, "a", "b");
Sims1_ S(congruence_kind::right);
S.short_rules(p).extra(e);
REQUIRE(S.number_of_congruences( 3) == 2);
check_extra(congruence_kind::right, S.short_rules(), S.extra(), 3);
check_extra(congruence_kind::left, S.short_rules(), S.extra(), 3);
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"020",
"fp example 2",
"[quick][low-index]") {
auto rg = ReportGuard( false);
Presentation<word_type> p;
p.contains_empty_word( true);
p.alphabet({ 0, 1, 2});
presentation::add_rule_and_check(p, { 0, 1, 0}, { 0, 0});
presentation::add_rule_and_check(p, { 2, 2}, { 0, 0});
presentation::add_rule_and_check(p, { 0, 0, 0}, { 0, 0});
presentation::add_rule_and_check(p, { 2, 1}, { 1, 2});
presentation::add_rule_and_check(p, { 2, 0}, { 0, 0});
presentation::add_rule_and_check(p, { 1, 1}, { 1});
presentation::add_rule_and_check(p, { 0, 2}, { 0, 0});
Presentation<word_type> e;
e.alphabet(p.alphabet());
presentation::add_rule_and_check(e, { 1}, { 0, 0});
check_extra(congruence_kind::right, p, e, 11);
check_extra(congruence_kind::left, p, e, 11);
}
LIBSEMIGROUPS_TEST_CASE( "Sims1", "021", "exceptions", "[quick][low-index]") {
Presentation<word_type> p;
p.alphabet({ 0, 1, 2});
presentation::add_rule_and_check(p, { 0, 1, 0}, { 0, 0});
Presentation<word_type> e;
e.alphabet({ 0, 1});
REQUIRE_THROWS_AS(Sims1_(congruence_kind::right).short_rules(p).extra(e),
LibsemigroupsException);
REQUIRE_THROWS_AS(
Sims1_(congruence_kind::right).short_rules(p).long_rules(e),
LibsemigroupsException);
REQUIRE_THROWS_AS(
Sims1_(congruence_kind::right).long_rules(p).short_rules(e),
LibsemigroupsException);
REQUIRE_THROWS_AS(Sims1_(congruence_kind::right).long_rules(p).extra(e),
LibsemigroupsException);
REQUIRE_THROWS_AS(Sims1_(congruence_kind::right).extra(p).short_rules(e),
LibsemigroupsException);
REQUIRE_THROWS_AS(Sims1_(congruence_kind::right).extra(p).long_rules(e),
LibsemigroupsException);
REQUIRE_NOTHROW(Sims1_(congruence_kind::right).extra(p).extra(e));
REQUIRE_NOTHROW(
Sims1_(congruence_kind::right).short_rules(p).short_rules(e));
REQUIRE_NOTHROW(Sims1_(congruence_kind::right).long_rules(p).long_rules(e));
REQUIRE_THROWS_AS(Sims1_(congruence_kind::twosided),
LibsemigroupsException);
Sims1_ S(congruence_kind::right);
REQUIRE_THROWS_AS(S.number_of_threads( 0), LibsemigroupsException);
RepOrc ro;
REQUIRE_THROWS_AS(ro.number_of_threads( 0), LibsemigroupsException);
MinimalRepOrc mro;
REQUIRE_THROWS_AS(mro.number_of_threads( 0), LibsemigroupsException);
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"022",
"singular_brauer_monoid(4) (Maltcev-Mazorchuk)",
"[extreme][sims1]") {
auto rg = ReportGuard( true);
auto p = make<Presentation<word_type>>(singular_brauer_monoid( 4));
REQUIRE(p.alphabet().size() == 12);
REQUIRE(presentation::length(p) == 660);
REQUIRE(*presentation::shortest_rule(p) == word_type({ 0}));
REQUIRE(*(presentation::shortest_rule(p) + 1) == word_type({ 3}));
presentation::remove_redundant_generators(p);
presentation::remove_duplicate_rules(p);
presentation::sort_each_rule(p);
presentation::sort_rules(p);
REQUIRE(presentation::shortest_rule_length(p) == 3);
REQUIRE(*presentation::shortest_rule(p) == word_type({ 0, 0}));
REQUIRE(*(presentation::shortest_rule(p) + 1) == word_type({ 0}));
REQUIRE(presentation::longest_rule_length(p) == 6);
REQUIRE(*presentation::longest_rule(p) == word_type({ 0, 4, 8}));
REQUIRE(*(presentation::longest_rule(p) + 1) == word_type({ 0, 2, 8}));
REQUIRE(p.alphabet().size() == 6);
REQUIRE(presentation::length(p) == 462);
REQUIRE(p.rules.size() == 186);
p.contains_empty_word( true);
p.validate();
MinimalRepOrc orc;
auto d = orc.short_rules(p)
.target_size( 82)
.number_of_threads(std::thread::hardware_concurrency())
.report_interval( 1'999)
.digraph();
REQUIRE(d.number_of_nodes() == 18);
REQUIRE(orc.target_size() == 82);
p.contains_empty_word( false);
Sims1_ C(congruence_kind::right);
C.short_rules(p);
REQUIRE(C.short_rules().rules.size() == 186);
REQUIRE(C.number_of_threads(std::thread::hardware_concurrency())
.number_of_congruences( 81)
== 601'265);
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"023",
"Brauer(4) from FroidurePin",
"[extreme][sims1]") {
auto rg = ReportGuard( true);
FroidurePin<Bipartition> S;
S.add_generator(Bipartition({{ 1, - 1}, { 2, - 2}, { 3, - 3}, { 4, - 4}}));
S.add_generator(Bipartition({{ 1, - 2}, { 2, - 3}, { 3, - 4}, { 4, - 1}}));
S.add_generator(Bipartition({{ 1, - 2}, { 2, - 1}, { 3, - 3}, { 4, - 4}}));
S.add_generator(Bipartition({{ 1, 2}, { 3, - 3}, { 4, - 4}, {- 1, - 2}}));
REQUIRE(S.size() == 105);
auto p = make<Presentation<word_type>>(S);
REQUIRE(presentation::length(p) == 359);
presentation::remove_duplicate_rules(p);
REQUIRE(presentation::length(p) == 359);
presentation::reduce_complements(p);
REQUIRE(presentation::length(p) == 359);
presentation::sort_each_rule(p);
presentation::sort_rules(p);
REQUIRE(p.rules.size() == 86);
do {
auto it = presentation::redundant_rule(p, std::chrono::milliseconds( 100));
p.rules.erase(it, it + 2);
} while (presentation::length(p) > 300);
presentation::replace_subword(p, presentation::longest_common_subword(p));
Sims1_ C(congruence_kind::right);
C.short_rules(p).long_rule_length( 8);
REQUIRE(C.number_of_threads(std::thread::hardware_concurrency())
.number_of_congruences( 105)
== 103'406);
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"024",
"brauer_monoid(4) (Kudryavtseva-Mazorchuk)",
"[extreme][sims1]") {
auto rg = ReportGuard( true);
auto p = make<Presentation<word_type>>(brauer_monoid( 4));
REQUIRE(p.alphabet().size() == 7);
REQUIRE(presentation::length(p) == 182);
presentation::remove_duplicate_rules(p);
REQUIRE(presentation::length(p) == 162);
presentation::reduce_complements(p);
REQUIRE(presentation::length(p) == 159);
presentation::sort_each_rule(p);
presentation::sort_rules(p);
REQUIRE(p.rules.size() == 86);
auto d = MinimalRepOrc().short_rules(p).target_size( 105).digraph();
REQUIRE(d.number_of_nodes() == 22);
REQUIRE(action_digraph_helper::is_strictly_cyclic(d));
REQUIRE(
d
== action_digraph_helper::make<uint32_t>(
22, {{ 0, 0, 1, 0, 2, 3, 2}, { 1, 4, 0, 5, 6, 3, 7},
{ 2, 2, 2, 2, 2, 2, 2}, { 3, 8, 3, 9, 6, 3, 7},
{ 4, 1, 4, 10, 6, 2, 11}, { 5, 10, 5, 1, 12, 2, 7},
{ 6, 6, 8, 12, 6, 3, 13}, { 7, 11, 9, 7, 13, 3, 7},
{ 8, 3, 6, 14, 6, 3, 11}, { 9, 14, 7, 3, 12, 3, 7},
{ 10, 5, 15, 4, 12, 16, 11}, { 11, 7, 17, 11, 13, 16, 11},
{ 12, 12, 18, 6, 12, 16, 13}, { 13, 13, 19, 13, 13, 20, 13},
{ 14, 9, 21, 8, 12, 20, 11}, { 15, 15, 10, 15, 2, 16, 2},
{ 16, 18, 16, 17, 12, 16, 11}, { 17, 21, 11, 16, 6, 16, 11},
{ 18, 16, 12, 21, 12, 16, 7}, { 19, 20, 13, 20, 13, 20, 13},
{ 20, 19, 20, 19, 13, 20, 13}, { 21, 17, 14, 18, 6, 20, 7}}));
auto S = make<FroidurePin<Transf< 0, node_type>>>(d);
REQUIRE(S.size() == 105);
REQUIRE(S.generator( 0) == Transf< 0, node_type>::identity( 22));
REQUIRE(
S.generator( 1)
== Transf< 0, node_type>({ 0, 4, 2, 8, 1, 10, 6, 11, 3, 14, 5,
7, 12, 13, 9, 15, 18, 21, 16, 20, 19, 17}));
REQUIRE(
S.generator( 2)
== Transf< 0, node_type>({ 1, 0, 2, 3, 4, 5, 8, 9, 6, 7, 15,
17, 18, 19, 21, 10, 16, 11, 12, 13, 20, 14}));
REQUIRE(S.generator( 3)
== Transf< 0, node_type>({ 0, 5, 2, 9, 10, 1, 12, 7, 14, 3, 4, 11,
6, 13, 8, 15, 17, 16, 21, 20, 19, 18}));
REQUIRE(S.generator( 4)
== Transf< 0, node_type>({ 2, 6, 2, 6, 6, 12, 6, 13, 6, 12, 12,
13, 12, 13, 12, 2, 12, 6, 12, 13, 13, 6}));
REQUIRE(
S.generator( 5)
== Transf< 0, node_type>({ 3, 3, 2, 3, 2, 2, 3, 3, 3, 3, 16,
16, 16, 20, 20, 16, 16, 16, 16, 20, 20, 20}));
REQUIRE(
S.generator( 6)
== Transf< 0, node_type>({ 2, 7, 2, 7, 11, 7, 13, 7, 11, 7, 11,
11, 13, 13, 11, 2, 11, 11, 7, 13, 13, 7}));
Sims1_ C(congruence_kind::right);
C.short_rules(p);
REQUIRE(C.number_of_threads(std::thread::hardware_concurrency())
.number_of_congruences( 105)
== 103'406);
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"025",
"brauer_monoid(5) (Kudryavtseva-Mazorchuk)",
"[extreme][sims1]") {
auto rg = ReportGuard( true);
auto p = make<Presentation<word_type>>(brauer_monoid( 5));
REQUIRE(presentation::length(p) == 295);
presentation::remove_duplicate_rules(p);
presentation::reduce_complements(p);
presentation::sort_each_rule(p);
presentation::sort_rules(p);
REQUIRE(presentation::length(p) == 249);
REQUIRE(presentation::shortest_rule_length(p) == 3);
REQUIRE(*presentation::shortest_rule(p) == word_type({ 0, 0}));
REQUIRE(*(presentation::shortest_rule(p) + 1) == word_type({ 0}));
REQUIRE(p.alphabet().size() == 9);
presentation::remove_redundant_generators(p);
REQUIRE(p.alphabet() == word_type({ 1, 2, 3, 4, 5, 6, 7, 8}));
REQUIRE(p.alphabet().size() == 8);
REQUIRE(presentation::length(p) == 268);
REQUIRE(*presentation::longest_rule(p) == word_type({ 1, 1, 1, 1}));
REQUIRE(*(presentation::longest_rule(p) + 1) == word_type({ 1, 1}));
REQUIRE(presentation::longest_common_subword(p) == word_type({ 1, 1}));
p.alphabet( 9);
presentation::replace_subword(p, { 1, 1}, { 0});
REQUIRE(presentation::length(p) == 246);
// This is just very long running (without e!) and I haven't managed to run
// it to completion.
Presentation<word_type> e;
e.alphabet( 9);
presentation::add_rule_and_check(e, { 0}, { 1});
auto d = MinimalRepOrc()
.short_rules(p)
.extra(e)
.target_size( 945)
.number_of_threads( 8)
.report_interval( 100)
.digraph();
// WARNING: the number below is not necessarily the minimal degree of an
// action on right congruences, only the minimal degree of an action on
// right congruences containing the pair {0}, {1}.
REQUIRE(d.number_of_nodes() == 46);
auto S = make<FroidurePin<Transf< 0, node_type>>>(d);
REQUIRE(S.size() == 945);
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"026",
"uniform_block_bijection_monoid(4) (Fitzgerald)",
"[extreme][sims1]") {
auto rg = ReportGuard( true);
auto p = make<Presentation<word_type>>(
uniform_block_bijection_monoid( 4, author::FitzGerald));
presentation::remove_duplicate_rules(p);
presentation::reduce_complements(p);
presentation::sort_each_rule(p);
presentation::sort_rules(p);
Sims1_ C(congruence_kind::right);
C.short_rules(p);
REQUIRE(C.number_of_threads(std::thread::hardware_concurrency())
.number_of_congruences( 131)
== 280'455);
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"027",
"from https://mathoverflow.net/questions/423541/",
"[quick][sims1]") {
auto rg = ReportGuard( false);
Presentation<std::string> p;
p.contains_empty_word( false);
p.alphabet( "aAbBe");
presentation::add_identity_rules(p, 'e');
presentation::add_inverse_rules(p, "AaBbe", 'e');
presentation::add_rule_and_check(p, "aaa", "e");
presentation::add_rule_and_check(p, "baBBBABA", "e");
Sims1_ C(congruence_kind::right);
C.short_rules(p);
REQUIRE(C.number_of_congruences( 10) == 3);
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"028",
"from https://mathoverflow.net/questions/423541/",
"[quick][sims1]") {
auto rg = ReportGuard( false);
Presentation<std::string> p;
p.contains_empty_word( true);
p.alphabet( "aAbB");
presentation::add_inverse_rules(p, "AaBb");
presentation::add_rule_and_check(p, "aaa", "");
presentation::add_rule_and_check(p, "baBBBABA", "");
Sims1_ C(congruence_kind::right);
C.short_rules(p);
REQUIRE(C.number_of_congruences( 10) == 3);
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"029",
"fibonacci_semigroup(4, 6)",
"[standard][sims1][no-valgrind]") {
auto rg = ReportGuard( true); // for code coverage
auto p = make<Presentation<word_type>>(fibonacci_semigroup( 4, 6));
presentation::remove_duplicate_rules(p);
presentation::sort_each_rule(p);
presentation::sort_rules(p);
REQUIRE(presentation::length(p) == 30);
REQUIRE(p.rules.size() == 12);
REQUIRE(p.rules[ 0].size() + p.rules[ 1].size() == 5);
Sims1_ C(congruence_kind::right);
C.short_rules(p).report_interval( 1);
REQUIRE(C.number_of_congruences( 3) == 5);
C.number_of_threads( 2);
REQUIRE(C.number_of_congruences( 3) == 5);
REQUIRE_THROWS_AS(C.find_if( 0, []( auto const&) { return true; }),
LibsemigroupsException);
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"030",
"presentation with one free generator",
"[quick][sims1]") {
auto rg = ReportGuard( false);
Presentation<word_type> p;
p.alphabet( 4);
presentation::add_rule(p, { 1, 2, 1}, { 1, 1});
presentation::add_rule(p, { 3, 3}, { 1, 1});
presentation::add_rule(p, { 1, 1, 1}, { 1, 1});
presentation::add_rule(p, { 3, 2}, { 2, 3});
presentation::add_rule(p, { 3, 1}, { 1, 1});
presentation::add_rule(p, { 2, 2}, { 2});
presentation::add_rule(p, { 1, 3}, { 1, 1});
p.validate();
Sims1_ C(congruence_kind::right);
C.short_rules(p);
REQUIRE(C.number_of_congruences( 2) == 67);
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"031",
"presentation with non-zero index generators",
"[quick][sims1]") {
auto rg = ReportGuard( false);
Presentation<word_type> p;
presentation::add_rule(p, { 1, 2, 1}, { 1, 1});
presentation::add_rule(p, { 3, 3}, { 1, 1});
presentation::add_rule(p, { 1, 1, 1}, { 1, 1});
presentation::add_rule(p, { 3, 2}, { 2, 3});
presentation::add_rule(p, { 3, 1}, { 1, 1});
presentation::add_rule(p, { 2, 2}, { 2});
presentation::add_rule(p, { 1, 3}, { 1, 1});
p.alphabet_from_rules();
p.validate();
Sims1_ C(congruence_kind::right);
C.short_rules(p);
REQUIRE(C.number_of_congruences( 2) == 7);
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"032",
"presentation with empty word",
"[quick][sims1]") {
auto rg = ReportGuard( false);
Presentation<word_type> p;
p.contains_empty_word( true);
// a A b B c C
p.alphabet({ 0, 1, 2, 3, 4, 5});
presentation::add_inverse_rules(p, { 1, 0, 3, 2, 5, 4});
presentation::add_rule_and_check(p, { 0, 0, 5, 0, 4}, {});
presentation::add_rule_and_check(p, { 0, 4, 2, 2, 1, 5, 2}, {});
presentation::add_rule_and_check(p, { 1, 3, 0, 2, 4, 4, 4}, {});
Sims1_ S(congruence_kind::right);
S.short_rules(p);
REQUIRE(S.number_of_congruences( 3) == 14);
REQUIRE(S.number_of_congruences( 4) == 14);
REQUIRE(S.number_of_congruences( 5) == 14);
}
LIBSEMIGROUPS_TEST_CASE( "Sims1", "033", "constructors", "[quick][sims1]") {
auto rg = ReportGuard( false);
Presentation<word_type> p;
p.contains_empty_word( true);
// a A b B c C
p.alphabet({ 0, 1, 2, 3, 4, 5});
presentation::add_inverse_rules(p, { 1, 0, 3, 2, 5, 4});
presentation::add_rule_and_check(p, { 0, 0, 5, 0, 4}, {});
presentation::add_rule_and_check(p, { 0, 4, 2, 2, 1, 5, 2}, {});
presentation::add_rule_and_check(p, { 1, 3, 0, 2, 4, 4, 4}, {});
Sims1_ S(congruence_kind::right);
S.short_rules(p);
Sims1_ T(S);
REQUIRE(S.number_of_congruences( 3) == 14);
REQUIRE(T.number_of_congruences( 3) == 14);
Sims1_ U(std::move(S));
REQUIRE(U.number_of_congruences( 3) == 14);
REQUIRE(T.number_of_congruences( 3) == 14);
S = U;
REQUIRE(S.number_of_congruences( 3) == 14);
S = std::move(U);
REQUIRE(S.number_of_congruences( 3) == 14);
Presentation<word_type> e;
e.alphabet({ 0, 1, 2, 5});
Sims1_ C(congruence_kind::right);
REQUIRE_THROWS_AS(C.short_rules(p).extra(e), LibsemigroupsException);
}
LIBSEMIGROUPS_TEST_CASE( "Sims1", "034", "split_at", "[quick][sims1]") {
auto rg = ReportGuard( false);
Presentation<word_type> p;
p.contains_empty_word( true);
// a A b B c C
p.alphabet({ 0, 1, 2, 3, 4, 5});
presentation::add_inverse_rules(p, { 1, 0, 3, 2, 5, 4});
presentation::add_rule_and_check(p, { 0, 0, 5, 0, 4}, {});
presentation::add_rule_and_check(p, { 0, 4, 2, 2, 1, 5, 2}, {});
presentation::add_rule_and_check(p, { 1, 3, 0, 2, 4, 4, 4}, {});
Sims1_ S(congruence_kind::right);
S.short_rules(p);
REQUIRE_THROWS_AS(S.split_at( 10), LibsemigroupsException);
S.split_at( 0);
REQUIRE(S.short_rules().rules.empty());
for (size_t i = 0; i <= p.rules.size() / 2; ++i) {
S.split_at(i);
REQUIRE(S.short_rules().rules.size() == 2 * i);
}
REQUIRE(S.short_rules().rules.size() == p.rules.size());
for (size_t i = p.rules.size() / 2; i > 0; --i) {
S.split_at(i);
REQUIRE(S.short_rules().rules.size() == 2 * i);
}
S.split_at( 7);
REQUIRE(S.number_of_congruences( 3) == 14);
}
#ifdef LIBSEMIGROUPS_ENABLE_STATS
LIBSEMIGROUPS_TEST_CASE( "Sims1", "035", "stats", "[quick][sims1]") {
auto rg = ReportGuard( false);
Presentation<word_type> p;
p.contains_empty_word( true);
// a A b B c C
p.alphabet({ 0, 1, 2, 3, 4, 5});
presentation::add_inverse_rules(p, { 1, 0, 3, 2, 5, 4});
presentation::add_rule_and_check(p, { 0, 0, 5, 0, 4}, {});
presentation::add_rule_and_check(p, { 0, 4, 2, 2, 1, 5, 2}, {});
presentation::add_rule_and_check(p, { 1, 3, 0, 2, 4, 4, 4}, {});
Sims1_ S(congruence_kind::right);
S.short_rules(p);
std::stringbuf buff;
std::ostream os(&buff);
S.number_of_congruences( 2);
// REQUIRE(S.stats().max_pending != 0);
os << S.cbegin( 3).stats(); // Also does not do anything visible
}
#endif
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"036",
"check iterator requirements",
"[quick][sims1]") {
Presentation<word_type> p;
p.contains_empty_word( true);
p.alphabet({ 0, 1});
presentation::add_rule_and_check(p, { 0, 0, 0}, { 0});
presentation::add_rule_and_check(p, { 1, 1}, { 1});
presentation::add_rule_and_check(p, { 0, 1, 0, 1}, { 0});
{
Sims1_ S(congruence_kind::right);
S.short_rules(p);
verify_forward_iterator_requirements(S.cbegin( 10));
auto it = S.cbegin( 10);
REQUIRE(it->number_of_nodes() == 10);
}
{
Sims1_ S(congruence_kind::left);
S.short_rules(p);
verify_forward_iterator_requirements(S.cbegin( 10));
auto it = S.cbegin( 10);
REQUIRE(it->number_of_nodes() == 10);
}
}
// Takes about 30s
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"037",
"rectangular_band(9, 2)",
"[extreme][sims1]") {
auto rg = ReportGuard( true);
auto p = make<Presentation<word_type>>(rectangular_band( 9, 2));
presentation::remove_duplicate_rules(p);
presentation::sort_each_rule(p);
presentation::sort_rules(p);
REQUIRE(MinimalRepOrc()
.short_rules(p)
.target_size( 18)
.number_of_threads(std::thread::hardware_concurrency())
.digraph()
.number_of_nodes()
== 0);
p.contains_empty_word( true);
auto mro = MinimalRepOrc().short_rules(p).target_size( 19).number_of_threads(
std::thread::hardware_concurrency());
auto d = mro.digraph();
REQUIRE(d.number_of_nodes() == 11);
REQUIRE(action_digraph_helper::is_strictly_cyclic(d));
auto S = make<FroidurePin<Transf< 0, node_type>>>(d);
S.add_generator(S.generator( 0).identity());
REQUIRE(S.size() == 19);
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"038",
"partition_monoid(3) - minimal o.r.c. rep",
"[extreme][sims1]") {
auto rg = ReportGuard( true);
auto p
= make<Presentation<word_type>>(partition_monoid( 3, author::Machine));
REQUIRE(!p.contains_empty_word());
REQUIRE(p.alphabet() == word_type({ 0, 1, 2, 3, 4}));
auto d = RepOrc()
.short_rules(p)
.target_size( 203)
.min_nodes( 1)
.max_nodes( 22)
.number_of_threads( 2)
.digraph();
REQUIRE(d.number_of_nodes() == 22);
auto mro
= MinimalRepOrc().short_rules(p).target_size( 203).number_of_threads( 4);
d = mro.digraph();
REQUIRE(action_digraph_helper::is_strictly_cyclic(d));
auto S = make<FroidurePin<Transf< 0, node_type>>>(d);
REQUIRE(S.size() == 203);
// The actual digraph obtained is non-deterministic because we just take
// whichever one is found first.
// REQUIRE(
// d
// == action_digraph_helper::make<node_type>(
// 22,
// {{0, 1, 0, 2, 0}, {1, 3, 3, 4, 5}, {2, 6, 6, 2, 0},
// {3, 0, 1, 2, 5}, {4, 4, 4, 4, 4}, {5, 5, 5, 7, 5},
// {6, 8, 2, 4, 0}, {7, 9, 9, 7, 5}, {8, 2, 8, 4, 10},
// {9, 10, 7, 11, 5}, {10, 7, 10, 12, 10}, {11, 13, 11, 11,
// 14}, {12, 11, 13, 12, 10}, {13, 12, 12, 15, 10}, {14, 16, 14,
// 11, 14}, {15, 15, 15, 15, 17}, {16, 18, 18, 19, 5}, {17, 19,
// 17, 15, 17}, {18, 14, 16, 12, 5}, {19, 20, 20, 19, 21}, {20,
// 17, 19, 15, 21}, {21, 21, 21, 19, 21}}));
REQUIRE(d.number_of_nodes() == 22);
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"039",
"temperley_lieb_monoid(n) - n = 3 .. 6, minimal rep",
"[standard][sims1]") {
auto rg = ReportGuard( false);
std::array<uint64_t, 11> const sizes
= { 0, 1, 2, 5, 14, 42, 132, 429, 1'430, 4'862, 16'796};
std::array<uint64_t, 11> const min_degrees
= { 0, 0, 2, 4, 7, 10, 20, 29, 63, 91, 0};
// The values 63 and 91 are not verified
for (size_t n = 3; n <= 6; ++n) {
auto p = make<Presentation<word_type>>(temperley_lieb_monoid(n));
// There are no relations containing the empty word so we just manually
// add it.
p.contains_empty_word( true);
auto orc
= MinimalRepOrc().short_rules(p).number_of_threads( 2).target_size(
sizes[n]);
auto d = orc.digraph();
REQUIRE(orc.target_size() == sizes[n]);
REQUIRE(action_digraph_helper::is_strictly_cyclic(d));
auto S = make<FroidurePin<Transf< 0, node_type>>>(d);
S.add_generator(S.generator( 0).identity());
REQUIRE(S.size() == sizes[n]);
REQUIRE(d.number_of_nodes() == min_degrees[n]);
}
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"040",
"TransitiveGroup(10, 32) - minimal rep",
"[quick][sims1]") {
auto rg = ReportGuard( false);
Presentation<word_type> p;
p.contains_empty_word( true);
p.alphabet({ 0, 1, 2, 3, 4});
presentation::add_rule_and_check(p, { 0, 0}, {});
presentation::add_rule_and_check(p, { 1, 1}, {});
presentation::add_rule_and_check(p, { 2, 2}, {});
presentation::add_rule_and_check(p, { 3, 3}, {});
presentation::add_rule_and_check(p, { 4, 4}, {});
presentation::add_rule_and_check(p, { 0, 1, 0, 1, 0, 1}, {});
presentation::add_rule_and_check(p, { 0, 2, 0, 2}, {});
presentation::add_rule_and_check(p, { 0, 3, 0, 3}, {});
presentation::add_rule_and_check(p, { 0, 4, 0, 4}, {});
presentation::add_rule_and_check(p, { 1, 2, 1, 2, 1, 2}, {});
presentation::add_rule_and_check(p, { 1, 3, 1, 3}, {});
presentation::add_rule_and_check(p, { 1, 4, 1, 4}, {});
presentation::add_rule_and_check(p, { 2, 3, 2, 3, 2, 3}, {});
presentation::add_rule_and_check(p, { 2, 4, 2, 4}, {});
presentation::add_rule_and_check(p, { 3, 4, 3, 4, 3, 4}, {});
REQUIRE(MinimalRepOrc()
.short_rules(p)
.target_size( 0)
.digraph()
.number_of_nodes()
== 0);
REQUIRE(RepOrc()
.short_rules(p)
.min_nodes( 0)
.max_nodes( 0)
.target_size( 0)
.digraph()
.number_of_nodes()
== 0);
auto d = MinimalRepOrc().short_rules(p).target_size( 720).digraph();
REQUIRE(d.number_of_nodes() == 6);
REQUIRE(action_digraph_helper::is_strictly_cyclic(d));
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"041",
"rectangular_band(4, 4) - minimal o.r.c. rep",
"[standard][sims1]") {
auto rg = ReportGuard( false);
auto p = make<Presentation<word_type>>(rectangular_band( 4, 4));
p.contains_empty_word( true);
auto d = MinimalRepOrc()
.short_rules(p)
.number_of_threads( 2)
.target_size( 17)
.digraph();
REQUIRE(action_digraph_helper::is_strictly_cyclic(d));
auto S = make<FroidurePin<Transf< 0, node_type>>>(d);
REQUIRE(S.size() == 16);
REQUIRE(d.number_of_nodes() == 7);
p.contains_empty_word( false);
d = MinimalRepOrc()
.short_rules(p)
.target_size( 16)
.number_of_threads( 2)
.digraph();
REQUIRE(d.number_of_nodes() == 0);
}
// unbuffer -p ./test_sims1 "[042]" | ag -v FroidurePin
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"042",
"rectangular_band(m, n) - m = 1 .. 5, n = 1 .. 5",
"[fail][sims1]") {
// This doesn't fail it's just very extreme
std::vector<std::array<size_t, 6>> results = {{ 0, 0, 0, 0, 0, 0},
{ 0, 2, 2, 3, 4, 5},
{ 0, 3, 4, 5, 5, 6},
{ 0, 4, 5, 6, 6, 7},
{ 0, 5, 6, 7, 7, 8},
{ 0, 6, 7, 8, 8, 9}};
auto rg = ReportGuard( true);
for (size_t m = 1; m <= 5; ++m) {
for (size_t n = 1; n <= 5; ++n) {
std::cout << std::string( 72, '#') << "\n"
<< "CASE m, n = " << m << ", " << n << "\n"
<< std::string( 72, '#') << std::endl;
auto p = make<Presentation<word_type>>(rectangular_band(m, n));
p.contains_empty_word( true);
auto d = MinimalRepOrc()
.short_rules(p)
.target_size(m * n + 1)
.number_of_threads( 6)
.digraph();
REQUIRE(action_digraph_helper::is_strictly_cyclic(d));
auto S = make<FroidurePin<Transf< 0, node_type>>>(d);
REQUIRE(S.size() == m * n);
REQUIRE(d.number_of_nodes() == results[m][n]);
}
}
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"043",
"rectangular_band(2, 2) - with and without identity",
"[quick][sims1]") {
auto rg = ReportGuard( false);
auto p = make<Presentation<word_type>>(rectangular_band( 2, 2));
Sims1_ S(congruence_kind::right);
S.short_rules(p);
REQUIRE(S.number_of_congruences( 4) == 6);
p.contains_empty_word( true);
Sims1_ T(congruence_kind::right);
T.short_rules(p);
REQUIRE(T.number_of_congruences( 5) == 9);
auto it = S.cbegin( 4);
REQUIRE(*it++
== action_digraph_helper::make<node_type>(
5, {{ 1, 1, 1, 1}, { 1, 1, 1, 1}})); // Good
REQUIRE(*it++
== action_digraph_helper::make<node_type>(
5, {{ 1, 1, 1, 2}, { 1, 1, 1, 2}, { 1, 1, 1, 2}})); // Good
REQUIRE(*it++
== action_digraph_helper::make<node_type>(
5, {{ 1, 1, 2, 1}, { 1, 1, 1, 1}, { 2, 2, 2, 2}})); // Good
REQUIRE(
*it++
== action_digraph_helper::make<node_type>(
5,
{{ 1, 1, 2, 1}, { 1, 1, 1, 1}, { 2, 2, 2, 3}, { 2, 2, 2, 3}})); // Good
REQUIRE(
*it++
== action_digraph_helper::make<node_type>(
5,
{{ 1, 1, 2, 3}, { 1, 1, 1, 3}, { 2, 2, 2, 2}, { 1, 1, 1, 3}})); // Good
REQUIRE(*it++
== action_digraph_helper::make<node_type>( 5,
{{ 1, 1, 2, 3},
{ 1, 1, 1, 3},
{ 2, 2, 2, 4},
{ 1, 1, 1, 3},
{ 2, 2, 2, 4}})); // Good
REQUIRE(it->number_of_nodes() == 0);
it = T.cbegin( 5);
REQUIRE(*it++ == action_digraph_helper::make<node_type>( 5, {{ 0, 0, 0, 0}}));
REQUIRE(*it++
== action_digraph_helper::make<node_type>(
5, {{ 0, 0, 0, 1}, { 0, 0, 0, 1}}));
REQUIRE(*it++
== action_digraph_helper::make<node_type>(
5, {{ 1, 1, 1, 0}, { 1, 1, 1, 0}}));
REQUIRE(*it++
== action_digraph_helper::make<node_type>(
5, {{ 1, 1, 1, 1}, { 1, 1, 1, 1}}));
REQUIRE(*it++
== action_digraph_helper::make<node_type>(
5, {{ 1, 1, 1, 2}, { 1, 1, 1, 2}, { 1, 1, 1, 2}}));
REQUIRE(*it++
== action_digraph_helper::make<node_type>(
5, {{ 1, 1, 2, 1}, { 1, 1, 1, 1}, { 2, 2, 2, 2}}));
REQUIRE(*it++
== action_digraph_helper::make<node_type>(
5, {{ 1, 1, 2, 1}, { 1, 1, 1, 1}, { 2, 2, 2, 3}, { 2, 2, 2, 3}}));
REQUIRE(*it++
== action_digraph_helper::make<node_type>(
5, {{ 1, 1, 2, 3}, { 1, 1, 1, 3}, { 2, 2, 2, 2}, { 1, 1, 1, 3}}));
REQUIRE(*it++
== action_digraph_helper::make<node_type>( 5,
{{ 1, 1, 2, 3},
{ 1, 1, 1, 3},
{ 2, 2, 2, 4},
{ 1, 1, 1, 3},
{ 2, 2, 2, 4}}));
REQUIRE(it->number_of_nodes() == 0);
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"044",
"trivial group - minimal o.r.c. rep",
"[quick][sims1]") {
auto rg = ReportGuard( false);
Presentation<std::string> p;
p.alphabet( "aAbB");
p.contains_empty_word( true);
presentation::add_inverse_rules(p, "AaBb");
presentation::add_rule_and_check(p, "ab", "");
presentation::add_rule_and_check(p, "abb", "");
Sims1_ S(congruence_kind::right);
S.short_rules(p);
REQUIRE(S.number_of_congruences( 10) == 1);
auto d = MinimalRepOrc().short_rules(p).target_size( 1).digraph();
REQUIRE(d.number_of_nodes() == 1);
REQUIRE(action_digraph_helper::is_strictly_cyclic(d));
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"045",
"right zero semigroup - minimal o.r.c. rep",
"[quick][sims1]") {
// This is an example of a semigroup with a strictly cyclic faithful
// right representation.
auto rg = ReportGuard( false);
size_t const n = 5;
auto p = make<Presentation<word_type>>(rectangular_band( 1, n));
auto d = MinimalRepOrc().short_rules(p).target_size(n).digraph();
REQUIRE(action_digraph_helper::is_strictly_cyclic(d));
auto S = make<FroidurePin<Transf< 0, node_type>>>(d);
REQUIRE(S.size() == n);
REQUIRE(d.number_of_nodes() == 5);
}
LIBSEMIGROUPS_TEST_CASE(
"Sims1",
"046",
"semigroup with faithful non-strictly cyclic action of right congruence",
"[quick][sims1]") {
// Found with Smallsemi, this example is minimal wrt size of the semigroup.
auto rg = ReportGuard( false);
FroidurePin<Transf< 6>> S({Transf< 6>::make({ 0, 0, 2, 1, 4, 1}),
Transf< 6>::make({ 0, 0, 2, 3, 4, 3}),
Transf< 6>::make({ 0, 2, 2, 0, 4, 4})});
REQUIRE(S.size() == 5);
auto p = make<Presentation<word_type>>(S);
auto d = MinimalRepOrc().short_rules(p).target_size( 5).digraph();
REQUIRE(action_digraph_helper::is_strictly_cyclic(d));
REQUIRE(d.number_of_nodes() == 4);
REQUIRE(d
== action_digraph_helper::make<uint32_t>(
4, {{ 2, 2, 3}, { 0, 1, 2}, { 2, 2, 2}, { 3, 3, 3}}));
auto T = make<FroidurePin<Transf< 4>>>(d);
REQUIRE(T.generator( 0) == Transf< 4>({ 2, 0, 2, 3}));
REQUIRE(T.generator( 1) == Transf< 4>({ 2, 1, 2, 3}));
REQUIRE(T.generator( 2) == Transf< 4>({ 3, 2, 2, 3}));
REQUIRE(T.size() == 5);
auto dd = action_digraph_helper::make<uint8_t>( 5,
{{ 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 2},
{ 2, 2, 2, 2, 2},
{ 0, 1, 2, 3, 0},
{ 4, 4, 4, 4, 4}});
REQUIRE(!action_digraph_helper::is_strictly_cyclic(dd));
REQUIRE(dd.number_of_nodes() == 5);
auto U = make<FroidurePin<Transf< 5>>>(dd);
REQUIRE(U.size() == 5);
Sims1_ C(congruence_kind::right);
C.short_rules(p);
REQUIRE(C.number_of_congruences( 5) == 9);
uint64_t strictly_cyclic_count = 0;
uint64_t non_strictly_cyclic_count = 0;
for ( auto it = C.cbegin( 5); it != C.cend( 5); ++it) {
auto W = make<FroidurePin<Transf< 0, node_type>>>(
*it, 1, it->number_of_active_nodes());
if (p.contains_empty_word()) {
auto one = W.generator( 0).identity();
if (!W.contains(one)) {
W.add_generator(one);
}
}
if (W.size() == 5) {
auto result = *it;
result.induced_subdigraph( 1, result.number_of_active_nodes());
result.number_of_active_nodes(result.number_of_active_nodes() - 1);
if (action_digraph_helper::is_strictly_cyclic(result)) {
strictly_cyclic_count++;
} else {
REQUIRE(W.generator( 0) == Transf< 0, node_type>({ 3, 0, 2, 3, 4}));
REQUIRE(W.generator( 1) == Transf< 0, node_type>({ 3, 1, 2, 3, 4}));
REQUIRE(W.generator( 2) == Transf< 0, node_type>({ 4, 3, 2, 3, 4}));
REQUIRE(
result
== action_digraph_helper::make<uint32_t>(
5, {{ 3, 3, 4}, { 0, 1, 3}, { 2, 2, 2}, { 3, 3, 3}, { 4, 4, 4}}));
non_strictly_cyclic_count++;
}
}
}
REQUIRE(strictly_cyclic_count == 2);
REQUIRE(non_strictly_cyclic_count == 1);
}
// Takes about 3 to 4 minutes
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"047",
"rectangular_band(m, n) - m = 1 .. 5, n = 1 .. 5",
"[fail][sims1]") {
// This doesn't fail it's just very extreme
std::vector<std::array<size_t, 7>> left
= {{ 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0},
{ 0, 0, 6, 22, 94, 454, 2'430},
{ 0, 0, 30, 205, 1'555, 12'880},
{ 0, 0, 240, 4'065, 72'465, 1'353'390},
{ 0, 0, 2'756, 148'772, 8'174'244, 456'876'004}};
// Seems like the m,n-th entry of the table above is:
// {m, n} -> Sum([0 .. n], k -> Bell(m)^k*Stirling2(n, k));
auto rg = ReportGuard( true);
for (size_t m = 2; m <= 5; ++m) {
for (size_t n = 2; n <= 6; ++n) {
std::cout << std::string( 72, '#') << "\n"
<< "CASE m, n = " << m << ", " << n << "\n"
<< std::string( 72, '#') << std::endl;
auto p = make<Presentation<word_type>>(rectangular_band(m, n));
Sims1_ S(congruence_kind::left);
S.short_rules(p);
REQUIRE(S.number_of_threads( 4).number_of_congruences(m * n)
== left[m][n]);
Sims1_ T(congruence_kind::right);
T.short_rules(p);
REQUIRE(T.number_of_threads( 4).number_of_congruences(m * n)
== left[n][m]);
}
}
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"048",
"stellar_monoid(n) n = 3 .. 4",
"[fail][sims1][babbage]") {
std::array<uint64_t, 10> const size = { 0, 0, 0, 16, 65};
std::array<uint64_t, 10> const num_left = { 0, 0, 0, 1'550, 0};
std::array<uint64_t, 10> const num_right = { 0, 0, 0, 1'521, 0};
for (size_t n = 3; n < 5; ++n) {
auto p = make<Presentation<word_type>>(rook_monoid(n, 0));
auto q = make<Presentation<word_type>>(stellar_monoid(n));
p.rules.insert(p.rules.end(), q.rules.cbegin(), q.rules.cend());
REQUIRE(p.alphabet().size() == n + 1);
{
Sims1_ S(congruence_kind::left);
S.short_rules(p).number_of_threads( 4);
REQUIRE(S.number_of_congruences(size[n]) == num_left[n]);
}
{
Sims1_ S(congruence_kind::right);
S.short_rules(p).number_of_threads( 4);
REQUIRE(S.number_of_congruences(size[n]) == num_right[n]);
}
}
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"049",
"stylic_monoid(n) n = 3, 4",
"[fail][sims1]") {
auto rg = ReportGuard( true);
std::array<uint64_t, 10> const size = { 0, 0, 0, 14, 51};
// 1505s
std::array<uint64_t, 10> const num_left = { 0, 0, 0, 1'214, 1'429'447'174};
std::array<uint64_t, 10> const num_right = { 0, 0, 0, 1'214, 1'429'455'689};
for (size_t n = 3; n < 5; ++n) {
auto p = make<Presentation<word_type>>(stylic_monoid(n));
{
Sims1_ S(congruence_kind::right);
S.short_rules(p).number_of_threads( 4);
REQUIRE(S.number_of_congruences(size[n]) == num_right[n]);
}
{
Sims1_ S(congruence_kind::left);
S.short_rules(p).number_of_threads( 4);
REQUIRE(S.number_of_congruences(size[n]) == num_left[n]);
}
}
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"050",
"(2, 3, 7)-triangle group - index 50",
"[extreme][sims1]") {
auto rg = ReportGuard( true);
Presentation<std::string> p;
p.contains_empty_word( true);
p.alphabet( "xy");
presentation::add_rule_and_check(p, "xx", "");
presentation::add_rule_and_check(p, "yyy", "");
presentation::add_rule_and_check(p, "xyxyxyxyxyxyxy", "");
Sims1_ S(congruence_kind::right);
S.short_rules(p).number_of_threads( 4);
REQUIRE(S.number_of_congruences( 50) == 75'971);
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"051",
"Heineken group - index 10",
"[extreme][sims1]") {
auto rg = ReportGuard( true);
Presentation<std::string> p;
p.contains_empty_word( true);
p.alphabet( "xXyY");
presentation::add_inverse_rules(p, "XxYy");
presentation::add_rule_and_check(p, "yXYYxyYYxyyXYYxyyXyXYYxy", "x");
Presentation<std::string> q;
q.alphabet( "xXyY");
presentation::add_rule_and_check(
q, "YxyyXXYYxyxYxyyXYXyXYYxxyyXYXyXYYxyx", "y");
Sims1_ S(congruence_kind::right);
S.short_rules(p).long_rules(q).number_of_threads( 8);
REQUIRE(S.number_of_congruences( 10) == 1);
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"052",
"temperley_lieb_monoid(n) - n = 3 .. 6",
"[extreme][low-index][babbage]") {
std::array<uint64_t, 10> const size = { 0, 0, 0, 5, 14, 42, 132, 429};
std::array<uint64_t, 10> const num_right
= { 0, 0, 0, 9, 79, 2'157, 4'326'459};
auto rg = ReportGuard( true);
for (size_t n = 3; n < 7; ++n) {
auto p = make<Presentation<word_type>>(temperley_lieb_monoid(n));
p.contains_empty_word( true);
Sims1_ S(congruence_kind::right);
S.short_rules(p).number_of_threads( 4);
REQUIRE(S.number_of_congruences(size[n]) == num_right[n]);
}
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"053",
"partial_transformation_monoid(3)",
"[extreme][low-index]") {
auto rg = ReportGuard( true);
auto p = make<Presentation<word_type>>(
partial_transformation_monoid( 3, author::Machine));
{
Sims1_ S(congruence_kind::right);
S.short_rules(p).number_of_threads( 4);
REQUIRE(S.number_of_congruences( 64) == 92'703);
}
{
Sims1_ S(congruence_kind::left);
S.short_rules(p).number_of_threads( 4);
REQUIRE(S.number_of_congruences( 64) == 371);
}
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"054",
"partial_transformation_monoid(4) from FroidurePin",
"[fail][low-index]") {
using Transf_ = Transf< 5>;
auto rg = ReportGuard( true);
FroidurePin<Transf_> S({Transf_({ 1, 0, 2, 3, 4}),
Transf_({ 3, 0, 1, 2, 4}),
Transf_({ 4, 1, 2, 3, 4}),
Transf_({ 1, 1, 2, 3, 4})});
REQUIRE(S.size() == 625);
auto p = make<Presentation<word_type>>(S);
Sims1_ C(congruence_kind::left);
REQUIRE(presentation::longest_rule_length(p) == 18);
REQUIRE(presentation::shortest_rule_length(p) == 3);
presentation::remove_duplicate_rules(p);
presentation::remove_trivial_rules(p);
presentation::sort_each_rule(p);
presentation::sort_rules(p);
word_type w = presentation::longest_common_subword(p);
while (!w.empty()) {
presentation::replace_subword(p, w);
w = presentation::longest_common_subword(p);
}
REQUIRE(presentation::length(p) == 1414);
REQUIRE(presentation::longest_rule_length(p) == 6);
C.short_rules(p).long_rule_length( 6).number_of_threads( 8).report_interval(
100);
REQUIRE(C.number_of_congruences( 625) == 10);
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"055",
"plactic_monoid(3) up to index 8",
"[extreme][low-index][plactic]") {
std::array<uint64_t, 9> const num
= { 0, 1, 29, 484, 6'896, 103'204, 1'773'360, 35'874'182, 849'953'461};
auto rg = ReportGuard( true);
auto p = make<Presentation<word_type>>(plactic_monoid( 3));
for (size_t n = 2; n < 9; ++n) {
{
Sims1_ S(congruence_kind::right);
S.short_rules(p).number_of_threads( 4);
REQUIRE(S.number_of_congruences(n) == num[n]);
}
{
Sims1_ S(congruence_kind::left);
S.short_rules(p).number_of_threads( 4);
REQUIRE(S.number_of_congruences(n) == num[n]);
}
}
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"056",
"plactic_monoid(4) up to index 6",
"[extreme][low-index][plactic]") {
std::array<uint64_t, 8> const num
= { 0, 1, 67, 2'794, 106'264, 4'795'980, 278'253'841, 20'855'970'290};
// Last value too 1h34m to compute so is not included.
auto rg = ReportGuard( true);
auto p = make<Presentation<word_type>>(plactic_monoid( 4));
for (size_t n = 2; n < 7; ++n) {
{
Sims1_ S(congruence_kind::right);
S.short_rules(p).number_of_threads( 4);
REQUIRE(S.number_of_congruences(n) == num[n]);
}
{
Sims1_ S(congruence_kind::left);
S.short_rules(p).number_of_threads( 4);
REQUIRE(S.number_of_congruences(n) == num[n]);
}
}
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"057",
"plactic_monoid(5) up to index 5",
"[extreme][low-index][plactic]") {
std::array<uint64_t, 7> const num
= { 0, 1, 145, 14'851, 1'496'113, 198'996'912, 37'585'675'984};
// Last value took 5h11m to compute
auto rg = ReportGuard( true);
auto p = make<Presentation<word_type>>(plactic_monoid( 5));
for (size_t n = 3; n < 6; ++n) {
{
Sims1_ S(congruence_kind::right);
S.short_rules(p).number_of_threads( 4);
REQUIRE(S.number_of_congruences(n) == num[n]);
}
{
Sims1_ S(congruence_kind::left);
S.short_rules(p).number_of_threads( 4);
REQUIRE(S.number_of_congruences(n) == num[n]);
}
}
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"058",
"plactic_monoid(6) up to index 4",
"[extreme][low-index][plactic]") {
std::array<uint64_t, 6> const num
= { 0, 1, 303, 77'409, 20'526'128, 7'778'840'717};
// The last value took 4h5m to run and is omitted.
auto rg = ReportGuard( true);
auto p = make<Presentation<word_type>>(plactic_monoid( 6));
for (size_t n = 2; n < 5; ++n) {
{
Sims1_ S(congruence_kind::right);
S.short_rules(p).number_of_threads( 4);
REQUIRE(S.number_of_congruences(n) == num[n]);
}
{
Sims1_ S(congruence_kind::left);
S.short_rules(p).number_of_threads( 4);
REQUIRE(S.number_of_congruences(n) == num[n]);
}
}
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"059",
"plactic_monoid(7) up to index 3",
"[extreme][low-index][plactic]") {
std::array<uint64_t, 5> const num = { 0, 1, 621, 408'024, 281'600'130};
// The last value took approx. 12m34s to run and is omitted from the
// extreme test 12m34s to run and is omitted from the extreme test.
auto rg = ReportGuard( true);
auto p = make<Presentation<word_type>>(plactic_monoid( 7));
for (size_t n = 2; n < 4; ++n) {
{
Sims1_ S(congruence_kind::right);
S.short_rules(p).number_of_threads( 4);
REQUIRE(S.number_of_congruences(n) == num[n]);
}
{
Sims1_ S(congruence_kind::left);
S.short_rules(p).number_of_threads( 4);
REQUIRE(S.number_of_congruences(n) == num[n]);
}
}
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"060",
"plactic_monoid(8) up to index 3",
"[extreme][low-index][plactic]") {
std::array<uint64_t, 4> const num = { 0, 1, 1'259, 2'201'564};
auto rg = ReportGuard( true);
auto p = make<Presentation<word_type>>(plactic_monoid( 8));
for (size_t n = 2; n < 4; ++n) {
{
Sims1_ S(congruence_kind::right);
S.short_rules(p).number_of_threads( 4);
REQUIRE(S.number_of_congruences(n) == num[n]);
}
{
Sims1_ S(congruence_kind::left);
S.short_rules(p).number_of_threads( 4);
REQUIRE(S.number_of_congruences(n) == num[n]);
}
}
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"061",
"chinese_monoid(3) up to index 8",
"[extreme][low-index][chinese]") {
std::array<uint64_t, 9> const num
= { 0, 1, 31, 559, 8'904, 149'529, 2'860'018, 63'828'938, 1'654'488'307};
// index 8 is doable and the value is included above, but it took about X
// minutes to run, so isn't included in the loop below.
auto rg = ReportGuard( true);
auto p = make<Presentation<word_type>>(chinese_monoid( 3));
for (size_t n = 2; n < 8; ++n) {
Sims1_ S(congruence_kind::right);
S.short_rules(p).number_of_threads( 4);
REQUIRE(S.number_of_congruences(n) == num[n]);
}
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"062",
"chinese_monoid(4) up to index 6",
"[extreme][low-index][chinese]") {
// n = 6 took between 3 and 4 minutes
// n = 7 took 6h16m
// both are omitted
std::array<uint64_t, 8> const num
= { 0, 1, 79, 3'809, 183'995, 10'759'706, 804'802'045, 77'489'765'654};
auto rg = ReportGuard( true);
auto p = make<Presentation<word_type>>(chinese_monoid( 4));
for (size_t n = 3; n < 7; ++n) {
Sims1_ S(congruence_kind::right);
S.short_rules(p).number_of_threads( 4);
REQUIRE(S.number_of_congruences(n) == num[n]);
}
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"063",
"chinese_monoid(5) up to index 5",
"[extreme][low-index][chinese]") {
std::array<uint64_t, 7> const num
= { 0, 1, 191, 23'504, 3'382'921, 685'523'226, 199'011'439'587};
// The last value took 21h32m and so is omitted
auto rg = ReportGuard( true);
auto p = make<Presentation<word_type>>(chinese_monoid( 5));
for (size_t n = 3; n < 6; ++n) {
Sims1_ S(congruence_kind::right);
S.short_rules(p).number_of_threads( 4);
REQUIRE(S.number_of_congruences(n) == num[n]);
}
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"064",
"chinese_monoid(6) up to index 4",
"[extreme][low-index][chinese]") {
// 0 1 2 3 4
std::array<uint64_t, 6> const num
= { 0, 1, 447, 137'694, 58'624'384, 40'823'448'867};
// The last value took 9h54m to compute, and is omitted!
auto rg = ReportGuard( true);
auto p = make<Presentation<word_type>>(chinese_monoid( 6));
for (size_t n = 3; n < 5; ++n) {
Sims1_ S(congruence_kind::right);
S.short_rules(p).number_of_threads( 4);
REQUIRE(S.number_of_congruences(n) == num[n]);
}
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"065",
"chinese_monoid(7) up to index 4",
"[extreme][low-index][chinese]") {
// Last value took about 50m to compute
std::array<uint64_t, 5> const num = { 0, 1, 1'023, 786'949, 988'827'143};
auto rg = ReportGuard( true);
auto p = make<Presentation<word_type>>(chinese_monoid( 7));
for (size_t n = 2; n < 4; ++n) {
Sims1_ S(congruence_kind::right);
S.short_rules(p).number_of_threads( 4);
REQUIRE(S.number_of_congruences(n) == num[n]);
}
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"066",
"chinese_monoid(8) up to index 3",
"[extreme][low-index][chinese]") {
std::array<uint64_t, 4> const num = { 0, 1, 2'303, 4'459'599};
auto rg = ReportGuard( true);
auto p = make<Presentation<word_type>>(chinese_monoid( 8));
for (size_t n = 2; n < 4; ++n) {
Sims1_ S(congruence_kind::right);
S.short_rules(p).number_of_threads( 4);
REQUIRE(S.number_of_congruences(n) == num[n]);
}
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"067",
"FreeSemigroup(n) up to index 3",
"[extreme][low-index]") {
// (27^n - 9^n)/2 - 12^n + 6^n
std::array<uint64_t, 10> const num = { 0, 2, 229, 8022, 243241, 6904866};
// 4 ^ n - 2 ^ n
// {0, 2, 13, 57, 241, 993, 4033};
// = {0, 1, 29, 249, 2'033, 16'353, 131'009};
// = {0, 1, 830, 81'762, 7'008'614};
auto rg = ReportGuard( true);
for (size_t n = 2; n < 8; ++n) {
Presentation<word_type> p;
p.contains_empty_word( true);
p.alphabet(n);
Sims1_ S(congruence_kind::right);
S.short_rules(p);
REQUIRE(S.number_of_congruences( 3) == num[n]);
}
// For n >= 1, a(n) is the number of deterministic, completely-defined,
// initially-connected finite automata with n inputs and 3 unlabeled states.
// A020522 counts similar automata with n inputs and 2 unlabeled states.
// According to a comment by Nelma Moreira in A006689 and A006690, the
// number of such automata with N inputs and M unlabeled states is Sum
// (Product_{i=1..M-1} i^(f_i - f_{i-1} - 1)) * M^(M*N - f_{M-1} - 1), where
// the sum is taken over integers f_1, ..., f_{M-1} satisfying 0 <= f_1 < N
// and f_{i-1} < f_{i} < i*N for i = 2..M-1. (See Theorem 8 in Almeida,
// Moreira, and Reis (2007). The value of f_0 is not relevant.) For this
// sequence we have M = 3 unlabeled states, for A020522 we have M = 2
// unlabeled states, for A006689 we have N = 2 inputs, and for A006690 we
// have N = 3 inputs.
}
LIBSEMIGROUPS_TEST_CASE( "Sims1", "068", "RepOrc", "[quick][low-index]") {
auto rg = ReportGuard( false);
auto p = make<Presentation<word_type>>(temperley_lieb_monoid( 9));
// There are no relations containing the empty word so we just manually
// add it.
p.contains_empty_word( true);
RepOrc orc;
// Check bad input
auto d = orc.short_rules(p)
.min_nodes( 100)
.max_nodes( 90)
.target_size( 4'862)
.digraph();
REQUIRE(d.number_of_nodes() == 0);
d = orc.short_rules(p)
.min_nodes( 80)
.max_nodes( 100)
.target_size( 4'862)
.digraph();
auto S = make<FroidurePin<Transf< 0, node_type>>>(d);
S.add_generator(S.generator( 0).identity());
REQUIRE(S.size() == 4'862);
REQUIRE(orc.min_nodes() == 80);
REQUIRE(orc.max_nodes() == 100);
REQUIRE(orc.target_size() == 4'862);
REQUIRE(orc.short_rules().rules.size() == 128);
REQUIRE(orc.long_rules().rules.size() == 0);
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"069",
"fp example 1",
"[quick][low-index]") {
auto rg = ReportGuard( false);
Presentation<word_type> p;
p.contains_empty_word( true);
p.alphabet({ 0, 1});
presentation::add_rule_and_check(p, { 0, 0, 0}, { 0});
presentation::add_rule_and_check(p, { 1, 1}, { 1});
Presentation<word_type> q;
q.contains_empty_word( true);
q.alphabet({ 0, 1});
presentation::add_rule_and_check(q, { 0, 1, 0, 1}, { 0});
Sims1_ S(congruence_kind::right);
REQUIRE(S.short_rules(p)
.long_rules(q)
.number_of_threads( 1)
.number_of_congruences( 5)
== 6);
S.long_rule_length( 5);
REQUIRE(S.long_rules().rules.size() == 2);
REQUIRE(S.short_rules().rules.size() == 4);
S.long_rule_length( 4);
REQUIRE(S.long_rules().rules.size() == 4);
REQUIRE(S.short_rules().rules.size() == 2);
S = Sims1_(congruence_kind::left);
REQUIRE(S.short_rules(p)
.long_rules(q)
.number_of_threads( 1)
.number_of_congruences( 5)
== 9);
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"070",
"temperley_lieb_monoid(3) - n = minimal rep "
"(single-threaded, reporting on)",
"[standard][sims1]") {
auto rg = ReportGuard( true);
for (size_t n = 3; n <= 3; ++n) {
auto p = make<Presentation<word_type>>(temperley_lieb_monoid(n));
// There are no relations containing the empty word so we just manually
// add it.
p.contains_empty_word( true);
auto d = MinimalRepOrc()
.short_rules(p)
.number_of_threads( 1)
.target_size( 5)
.digraph();
REQUIRE(action_digraph_helper::is_strictly_cyclic(d));
auto S = make<FroidurePin<Transf< 0, node_type>>>(d);
S.add_generator(S.generator( 0).identity());
REQUIRE(S.size() == 5);
REQUIRE(d.number_of_nodes() == 4);
}
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"071",
"FreeSemigroup(2) up to index 4",
"[quick][low-index]") {
auto rg = ReportGuard( true);
Presentation<word_type> p;
p.contains_empty_word( true);
p.alphabet( 2);
Sims1_ S(congruence_kind::right);
S.short_rules(p);
REQUIRE(S.number_of_congruences( 4) == 5'477);
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"072",
"symmetric_group(n) for n = 4",
"[quick][low-index]") {
std::array<uint64_t, 10> const num = { 0, 0, 0, 6, 30, 156, 1'455};
auto rg = ReportGuard( false);
size_t n = 4;
auto p
= make<Presentation<word_type>>(symmetric_group(n, author::Carmichael));
Sims1_ C(congruence_kind::right);
C.short_rules(p).number_of_threads( 4);
REQUIRE(C.number_of_congruences(factorial(n)) == num[n]);
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"073",
"corner case no generators + no relations",
"[quick][low-index]") {
Presentation<word_type> p;
p.alphabet( 0);
Sims1_ S(congruence_kind::right);
REQUIRE_THROWS_AS(S.short_rules(p), LibsemigroupsException);
REQUIRE_THROWS_AS(S.number_of_congruences( 1), LibsemigroupsException);
REQUIRE_THROWS_AS(S.cbegin( 2), LibsemigroupsException);
REQUIRE_THROWS_AS(S.cend( 2), LibsemigroupsException);
REQUIRE_THROWS_AS(S.find_if( 2, []( auto) { return true; }),
LibsemigroupsException);
REQUIRE_THROWS_AS(S.for_each( 2, []( auto) {}), LibsemigroupsException);
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"074",
"monogenic_semigroup(m, r) for m, r = 1 .. 10",
"[fail][low-index]") {
auto rg = ReportGuard( false);
std::vector<std::array<uint64_t, 11>> const num
= {{ 1, 2, 2, 3, 2, 4, 2, 4, 3, 4},
{ 2, 4, 4, 6, 4, 8, 4, 8, 6, 8},
{ 3, 6, 6, 9, 6, 12, 6, 12, 9, 12},
{ 4, 8, 8, 12, 8, 16, 8, 16, 12, 16},
{ 5, 10, 10, 15, 10, 20, 10, 20, 15, 20},
{ 6, 12, 12, 18, 12, 24, 12, 24, 18, 24},
{ 7, 14, 14, 21, 14, 28, 14, 28, 21, 28},
{ 8, 16, 16, 24, 16, 32, 16, 32, 24, 32},
{ 9, 18, 18, 27, 18, 36, 18, 36, 27, 36},
{ 10, 20, 20, 30, 20, 40, 20, 40, 30, 40}};
// m * number of divisors of r
for (size_t m = 1; m <= 10; ++m) {
for (size_t r = 1; r <= 10; ++r) {
// Cyclic groups
auto p = make<Presentation<word_type>>(monogenic_semigroup(m, r));
Sims1_ C(congruence_kind::right);
C.short_rules(p);
// std::cout << C.number_of_congruences(m + r) << ", ";
REQUIRE(C.number_of_congruences(m + r) == num[m - 1][r - 1]);
}
}
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"075",
"partial_transformation_monoid(4)",
"[fail][low-index]") {
auto rg = ReportGuard( true);
auto p = make<Presentation<word_type>>(
partial_transformation_monoid( 4, author::Sutov));
auto w = presentation::longest_common_subword(p);
while (!w.empty()) {
presentation::replace_subword(p, presentation::longest_common_subword(p));
w = presentation::longest_common_subword(p);
}
presentation::sort_each_rule(p);
presentation::sort_rules(p);
presentation::remove_duplicate_rules(p);
presentation::reduce_complements(p);
presentation::remove_trivial_rules(p);
do {
auto it = presentation::redundant_rule(p, std::chrono::milliseconds( 100));
p.rules.erase(it, it + 2);
} while (presentation::length(p) > 800);
Sims1_ C(congruence_kind::left);
C.short_rules(p).number_of_threads( 4).report_interval( 10);
REQUIRE(C.number_of_congruences( 624) == 0);
}
LIBSEMIGROUPS_TEST_CASE( "Sims1",
"076",
"uninitialized RepOrc",
"[quick][low-index]") {
auto rg = ReportGuard( false);
Presentation<std::string> p;
p.alphabet( "abc");
presentation::add_rule_and_check(p, "cc", "c");
presentation::add_rule_and_check(p, "abb", "a");
presentation::add_rule_and_check(p, "aca", "aba");
RepOrc orc;
orc.short_rules(p).number_of_threads(std::thread::hardware_concurrency());
REQUIRE(orc.min_nodes() == 0);
REQUIRE(orc.max_nodes() == 0);
REQUIRE(orc.target_size() == 0);
REQUIRE(orc.digraph().number_of_nodes() == 0);
}
} // namespace libsemigroups
// [[[0, 0, 0]], #1#
// [[0, 0, 1], [1, 0, 1]], #2#
// [[0, 1, 0], [1, 1, 0]],
// [[0, 1, 1], [1, 1, 1]]] #3#
// [[[0, 0, 0]], #1#
// [[1, 0, 1], [1, 1, 1]],
// [[1, 0, 2], [1, 1, 1], [1, 2, 1]],
// [[1, 1, 1], [1, 1, 1]], #2#
// [[1, 1, 2], [1, 1, 1], [1, 1, 1]], #4#
// [[1, 0, 1], [2, 1, 2], [2, 2, 2]],
// [[1, 0, 2], [2, 1, 2], [2, 2, 2]],
// [[1, 1, 2], [2, 1, 2], [2, 2, 2]], #3#
// [[1, 2, 2], [2, 1, 2], [2, 2, 2]],
// [[1, 0, 2], [2, 2, 2], [2, 2, 2]],
// [[1, 2, 1], [2, 2, 2], [2, 2, 2]],
// [[1, 2, 2], [2, 2, 2], [2, 2, 2]],
// [[1, 2, 1], [1, 1, 1], [1, 2, 1]]]
¤ Dauer der Verarbeitung: 0.41 Sekunden
¤
*© Formatika GbR, Deutschland
|
|