diff --git a/src/fpsemi-examples.cpp b/src/fpsemi-examples.cpp index 4e1039585..b2d137c40 100644 --- a/src/fpsemi-examples.cpp +++ b/src/fpsemi-examples.cpp @@ -1165,7 +1165,7 @@ namespace libsemigroups { Presentation full_transformation_monoid(size_t n, author val) { if (n < 4) { LIBSEMIGROUPS_EXCEPTION( - "the 1st argument (size_t) must be at least 4, found {}", n); + "the 1st argument (degree) must be at least 4, found {}", n); } else if (val != author::Aizenstat && val != author::Iwahori) { LIBSEMIGROUPS_EXCEPTION( "expected 2nd argument to be author::Aizenstat or " diff --git a/src/sims1.cpp b/src/sims1.cpp index d2f277d4c..9d0e3649e 100644 --- a/src/sims1.cpp +++ b/src/sims1.cpp @@ -130,6 +130,9 @@ namespace libsemigroups { size_t m = std::distance(s->presentation().rules.cbegin(), s->cbegin_long_rules()); p.rules.erase(p.rules.begin() + m, p.rules.end()); + // TODO could be slightly less space allocated here + _2_sided_include.assign(2 * n * p.alphabet().size(), word_type()); + _2_sided_words.assign(n, word_type()); _felsch_graph.init(std::move(p)); // n == 0 only when the iterator is cend _felsch_graph.number_of_active_nodes(n == 0 ? 0 : 1); @@ -147,7 +150,9 @@ namespace libsemigroups { _felsch_graph(that._felsch_graph), _mtx(), _pending(that._pending), - _sims1(that._sims1) {} + _sims1(that._sims1), + _2_sided_include(that._2_sided_include), + _2_sided_words(that._2_sided_words) {} // Intentionally don't copy the mutex, it doesn't compile, wouldn't make // sense if the mutex was used here. @@ -160,7 +165,9 @@ namespace libsemigroups { _felsch_graph(std::move(that._felsch_graph)), _mtx(), _pending(std::move(that._pending)), - _sims1(that._sims1) {} + _sims1(that._sims1), + _2_sided_include(std::move(that._2_sided_include)), + _2_sided_words(std::move(that._2_sided_words)) {} // Intentionally don't copy the mutex, it doesn't compile, wouldn't make // sense if the mutex was used here. @@ -175,6 +182,10 @@ namespace libsemigroups { // keep our own _mtx _pending = that._pending; _sims1 = that._sims1; + + _2_sided_include = that._2_sided_include; + _2_sided_words = that._2_sided_words; + return *this; } @@ -188,8 +199,10 @@ namespace libsemigroups { _min_target_node = std::move(that._min_target_node); // protected - _felsch_graph = std::move(that._felsch_graph); - _pending = std::move(that._pending); + _felsch_graph = std::move(that._felsch_graph); + _pending = std::move(that._pending); + _2_sided_include = std::move(that._2_sided_include); + _2_sided_words = std::move(that._2_sided_words); // keep our own _mtx _sims1 = that._sims1; return *this; @@ -218,9 +231,6 @@ namespace libsemigroups { if (_min_target_node == 0) { _pending.emplace_back(0, 0, 0, 0, 1, false); } - // TODO maybe use less space in _2_sided_include - _2_sided_include.assign((n * (n - 1)), word_type()); - _2_sided_words.assign(n, word_type()); } } @@ -253,9 +263,33 @@ namespace libsemigroups { // Don't call number_of_edges because this calls the function in // WordGraph - size_type start = _felsch_graph.definitions().size(); - size_type const prev_num_non_tree_edges - = 2 * ((start - _felsch_graph.number_of_active_nodes()) + 2); + size_type start = _felsch_graph.definitions().size(); + + size_type prev_num_non_tree_edges; + if (current.target_is_new_node) { + // number of tree edges is number_of_active_nodes - 2 + // because the active nodes include the target which is a new node + prev_num_non_tree_edges = 2 + * ((_felsch_graph.definitions().size() + - _felsch_graph.number_of_active_nodes()) + + 2); + LIBSEMIGROUPS_ASSERT(_felsch_graph.definitions().size() + - (prev_num_non_tree_edges / 2) + == _felsch_graph.number_of_active_nodes() - 2); + } else { + // number of tree edges is number_of_active_nodes - 1 because the + // target is not a new node + prev_num_non_tree_edges = 2 + * ((_felsch_graph.definitions().size() + - _felsch_graph.number_of_active_nodes()) + + 1); + LIBSEMIGROUPS_ASSERT(_felsch_graph.definitions().size() + - (prev_num_non_tree_edges / 2) + == _felsch_graph.number_of_active_nodes() - 1); + } + + LIBSEMIGROUPS_ASSERT(prev_num_non_tree_edges / 2 + <= _felsch_graph.definitions().size()); _felsch_graph.set_target_no_checks( current.source, current.generator, current.target); @@ -280,7 +314,7 @@ namespace libsemigroups { while (start < _felsch_graph.definitions().size()) { for (size_t i = start, j = 0; i < _felsch_graph.definitions().size(); ++i) { - auto e = _felsch_graph.definitions()[i]; + auto e = _felsch_graph.definitions()[i]; // TODO reference if (current.target_is_new_node && e.first == current.source && e.second == current.generator) { continue; @@ -304,13 +338,15 @@ namespace libsemigroups { first = _2_sided_include.cbegin(); last = _2_sided_include.cbegin() + num_non_tree_edges; start = _felsch_graph.definitions().size(); + prev_num_non_tree_edges = num_non_tree_edges; if (!felsch_graph::make_compatible( _felsch_graph, 0, _felsch_graph.number_of_active_nodes(), first, - last)) { + last) + || !_felsch_graph.process_definitions(start)) { return false; } } diff --git a/tests/gap.g b/tests/gap.g new file mode 100644 index 000000000..36c3189d0 --- /dev/null +++ b/tests/gap.g @@ -0,0 +1,77 @@ +AsWordGraph := function(C) + local lookup, S, A, out, next, pos, class, a; + + lookup := EquivalenceRelationCanonicalLookup(C); + S := Source(C); + A := GeneratorsOfSemigroup(S); + + out := []; + + for class in EquivalenceClasses(C) do + next := []; + for a in A do + pos := PositionCanonical(S, Representative(class) * a); + Add(next, lookup[pos]); + od; + Add(out, next); + od; + + return Digraph(out); +end; + +StandardizeWordGraph := function(D) + local s, t, N, n, result, x, r; + + # TODO arg checks + if IsNullDigraph(D) then + return D; + fi; + s := 0; + t := 0; + N := OutNeighbours(D); + n := Length(N[1]); + result := DigraphMutableCopy(D); + + while s <= t do + for x in [1 .. n] do + r := N[s + 1][x]; + if r > t then + t := t + 1; + if r > t then + OnDigraphs(result, (t + 1, r)); + N := OutNeighbours(result); + fi; + fi; + od; + s := s + 1; + od; + return result; +end; + +ToWordGraphs := function(Cs) + local Ds; + Ds := List(Cs, AsWordGraph); + Ds := List(Ds, x -> List(x, y -> y{[2 .. Length(y)]})); + return Ds - 1; +end; + +AllMultiplicationTables:= function(S) + local result; + result := ShallowCopy(Orbit(SymmetricGroup(Size(S)), + MultiplicationTable(S), + OnMultiplicationTable)); + if not IsSelfDualSemigroup(S) then + Append(result, + Orbit(SymmetricGroup(Size(S)), + TransposedMat(MultiplicationTable(S)), + OnMultiplicationTable)); + fi; + return result; +end; + + + +ExpectedNumber := function(num_gens, max_size) + + +end; diff --git a/tests/test-sims1.cpp b/tests/test-sims1.cpp index fd3efa625..0b22f81b5 100644 --- a/tests/test-sims1.cpp +++ b/tests/test-sims1.cpp @@ -2835,7 +2835,10 @@ namespace libsemigroups { REQUIRE(s.number_of_threads(4).number_of_congruences(std::pow(2, 6)) == 0); } - LIBSEMIGROUPS_TEST_CASE("Sims1", "092", "2-sided example", "[quick][sims1]") { + LIBSEMIGROUPS_TEST_CASE("Sims1", + "092", + "2-sided full transformation monoid 2", + "[quick][sims1]") { Presentation p; p.alphabet(2); p.contains_empty_word(true); @@ -2843,13 +2846,16 @@ namespace libsemigroups { presentation::add_rule(p, 01_w, 1_w); presentation::add_rule(p, 11_w, 1_w); Sims1 s(congruence_kind::twosided, p); - // REQUIRE(s.number_of_congruences(4) == 4); // Verified with GAP + REQUIRE(s.number_of_congruences(4) == 4); // Verified with GAP auto it = s.cbegin(4); - REQUIRE(*(it++) == to_word_graph(4, {{0, 0}})); - REQUIRE(*(it++) == to_word_graph(4, {{0, 1}, {1, 1}})); - REQUIRE(*(it++) == to_word_graph(4, {{1, 2}, {0, 2}, {2, 2}})); + REQUIRE(*(it++) == to_word_graph(4, {{0, 0}})); // ok + REQUIRE(*(it++) == to_word_graph(4, {{0, 1}, {1, 1}})); // ok + REQUIRE(*(it++) + == to_word_graph(4, {{1, 2}, {0, 2}, {2, 2}})); // ok + REQUIRE(*(it++) - == to_word_graph(4, {{1, 2}, {0, 2}, {3, 2}, {2, 2}})); + == to_word_graph( + 4, {{1, 2}, {0, 2}, {3, 2}, {2, 2}})); // ok } LIBSEMIGROUPS_TEST_CASE("Sims1", "093", "2-sided example", "[quick][sims1]") { @@ -2867,8 +2873,9 @@ namespace libsemigroups { presentation::add_rule(p, {0, 1, 0, 1}, {0}); Sims1 s(congruence_kind::twosided, p); - // REQUIRE(s.number_of_congruences(4) == 6); // Verified with GAP + REQUIRE(s.number_of_congruences(4) == 6); // Verified with GAP auto it = s.cbegin(5); + // Verified in 000 REQUIRE(*(it++) == to_word_graph(5, {{0, 0}})); REQUIRE(*(it++) == to_word_graph(5, {{1, 0}, {1, 1}})); REQUIRE(*(it++) == to_word_graph(5, {{1, 1}, {1, 1}})); @@ -2877,6 +2884,105 @@ namespace libsemigroups { REQUIRE(*(it++) == to_word_graph(5, {{1, 2}, {1, 1}, {3, 2}, {3, 3}})); } + + LIBSEMIGROUPS_TEST_CASE("Sims1", + "095", + "2-sided full transf. monoid 3", + "[quick][sims1]") { + Presentation p; + p.alphabet("abc"); + p.contains_empty_word(true); + presentation::add_rule(p, "b^2"_p, ""_p); + presentation::add_rule(p, "bc"_p, "ac"_p); + presentation::add_rule(p, "c^2"_p, "c"_p); + presentation::add_rule(p, "a^3"_p, ""_p); + presentation::add_rule(p, "a^2b"_p, "ba"_p); + presentation::add_rule(p, "aba"_p, "b"_p); + presentation::add_rule(p, "baa"_p, "ab"_p); + presentation::add_rule(p, "bab"_p, "aa"_p); + presentation::add_rule(p, "bac"_p, "c"_p); + presentation::add_rule(p, "cac"_p, "cb"_p); + presentation::add_rule(p, "aca^2c"_p, "ca^2c"_p); + presentation::add_rule(p, "ca^2cb"_p, "ca^2ca"_p); + presentation::add_rule(p, "ca^2cab"_p, "ca^2c"_p); + Sims1 s(congruence_kind::twosided, p); + REQUIRE(s.number_of_congruences(27) == 7); // Verified with GAP + + auto it = s.cbegin(27); + + REQUIRE(*(it++) == to_word_graph(27, {{0, 0, 0}})); // ok + REQUIRE(*(it++) + == to_word_graph(27, {{0, 0, 1}, {1, 1, 1}})); // ok + REQUIRE(*(it++) + == to_word_graph( + 27, {{0, 1, 2}, {1, 0, 2}, {2, 2, 2}})); // ok + REQUIRE(*(it++) + == to_word_graph(27, + {{1, 2, 3}, + {4, 5, 3}, + {6, 0, 3}, + {3, 3, 3}, + {0, 6, 3}, + {2, 1, 3}, + {5, 4, 3}})); // ok + REQUIRE(*(it++) + == to_word_graph(27, + {{1, 2, 3}, + {4, 5, 6}, + {7, 0, 6}, + {8, 3, 3}, + {0, 7, 9}, + {2, 1, 9}, + {10, 6, 6}, + {5, 4, 3}, + {11, 11, 3}, + {12, 9, 9}, + {13, 13, 6}, + {3, 8, 14}, + {15, 15, 9}, + {6, 10, 14}, + {14, 14, 14}, + {9, 12, 14}})); // ok + REQUIRE(*(it++) + == to_word_graph( + 27, {{1, 2, 3}, {4, 5, 6}, {7, 0, 6}, {8, 9, 3}, + {0, 7, 10}, {2, 1, 10}, {11, 12, 6}, {5, 4, 3}, + {13, 14, 9}, {15, 3, 9}, {16, 17, 10}, {18, 19, 12}, + {20, 6, 12}, {3, 15, 21}, {9, 8, 21}, {14, 13, 3}, + {22, 23, 17}, {24, 10, 17}, {6, 20, 21}, {12, 11, 21}, + {19, 18, 6}, {21, 21, 21}, {10, 24, 21}, {17, 16, 21}, + {23, 22, 10}})); // ok + REQUIRE(*(it++) + == to_word_graph( + 27, {{1, 2, 3}, {4, 5, 6}, {7, 0, 6}, {8, 9, 3}, + {0, 7, 10}, {2, 1, 10}, {11, 12, 6}, {5, 4, 3}, + {13, 14, 9}, {15, 3, 9}, {16, 17, 10}, {18, 19, 12}, + {20, 6, 12}, {3, 15, 21}, {9, 8, 21}, {14, 13, 3}, + {22, 23, 17}, {24, 10, 17}, {6, 20, 21}, {12, 11, 21}, + {19, 18, 6}, {25, 25, 21}, {10, 24, 21}, {17, 16, 21}, + {23, 22, 10}, {26, 21, 25}, {21, 26, 21}})); // ok + } + + LIBSEMIGROUPS_TEST_CASE("Sims1", + "096", + "2-sided free monoid", + "[quick][sims1]") { + Presentation p; + p.alphabet("ab"); + p.contains_empty_word(true); + Sims1 s(congruence_kind::twosided, p); + REQUIRE(s.number_of_congruences(1) == 1); + REQUIRE(s.number_of_congruences(2) == 7); + REQUIRE(s.number_of_congruences(3) == 27); + REQUIRE(s.number_of_congruences(4) == 94); + REQUIRE(s.number_of_congruences(5) == 275); + REQUIRE(s.number_of_congruences(6) == 833); + REQUIRE(s.number_of_congruences(7) == 2'307); + REQUIRE(s.number_of_congruences(8) == 6'488); + REQUIRE(s.number_of_congruences(9) == 18'207); + REQUIRE(s.number_of_congruences(10) == 52'960); + } + } // namespace libsemigroups // [[[0, 0, 0]], #1#