From 2c0bd7913b91ada2b2c49d19c7c8d179cadb7682 Mon Sep 17 00:00:00 2001 From: "James D. Mitchell" Date: Mon, 7 Apr 2025 18:25:13 +0200 Subject: [PATCH 01/27] Compiles --- GNUmakefile.in | 11 +-- gapbind14/include/gapbind14/cpp_fn.hpp | 27 ++++++ src/bipart.cpp | 41 +++++---- src/cong.cpp | 116 +++++++++++++------------ src/conglatt.cpp | 6 +- src/froidure-pin-base.cpp | 12 ++- src/froidure-pin-fallback.cpp | 4 +- src/froidure-pin.hpp | 22 +++-- src/pkg.cpp | 109 ++++++++--------------- src/pkg.hpp | 14 +-- src/to_cpp.hpp | 57 ++++++------ src/to_gap.hpp | 82 +++++++++-------- 12 files changed, 257 insertions(+), 244 deletions(-) diff --git a/GNUmakefile.in b/GNUmakefile.in index d55c7e4ec..2015db247 100644 --- a/GNUmakefile.in +++ b/GNUmakefile.in @@ -3,7 +3,7 @@ # KEXT_NAME = semigroups -KEXT_CXXFLAGS = @LIBSEMIGROUPS_CFLAGS@ -std=gnu++14 -O3 +KEXT_CXXFLAGS = @LIBSEMIGROUPS_CFLAGS@ -std=gnu++17 -O3 KEXT_LDFLAGS = @LIBSEMIGROUPS_RPATH@ @LIBSEMIGROUPS_LIBS@ # configure settings @@ -47,10 +47,11 @@ ifdef WITH_INCLUDED_LIBSEMIGROUPS # FIXME(later) all the include paths should point into bin/include/ and not to # the sources, or otherwise we should stop make installing into bin ifdef LIBSEMIGROUPS_HPCOMBI_ENABLED -KEXT_CPPFLAGS += -Ilibsemigroups/extern/HPCombi/include -KEXT_CPPFLAGS += -Ilibsemigroups/extern/HPCombi/include/fallback +KEXT_CPPFLAGS += -Ilibsemigroups/third_party/HPCombi/include +KEXT_CPPFLAGS += -Ilibsemigroups/third_party/HPCombi/third_party/ endif -KEXT_CPPFLAGS += -Ilibsemigroups/extern/fmt-8.0.1/include +KEXT_CPPFLAGS += -Ilibsemigroups/third_party/fmt-11.1.4/include +KEXT_CPPFLAGS += -Ilibsemigroups/third_party/magic_enum-0.9.7/include KEXT_CPPFLAGS += -Ilibsemigroups/include endif KEXT_CPPFLAGS += -DFMT_HEADER_ONLY @@ -102,7 +103,7 @@ lint: etc/cpplint.sh format: - clang-format -i src/*.*pp + clang-format -i src/*.[hc] .PHONY: lint format diff --git a/gapbind14/include/gapbind14/cpp_fn.hpp b/gapbind14/include/gapbind14/cpp_fn.hpp index af973883f..d80d28bf4 100644 --- a/gapbind14/include/gapbind14/cpp_fn.hpp +++ b/gapbind14/include/gapbind14/cpp_fn.hpp @@ -42,6 +42,8 @@ namespace gapbind14 { // Overloading //////////////////////////////////////////////////////////////////////// + static constexpr auto const_ = std::true_type{}; + template struct overload_cast_impl { constexpr overload_cast_impl() {} @@ -94,6 +96,11 @@ namespace gapbind14 { using type = R(A...); }; + template + struct remove_class { + using type = R(A...); + }; + template struct strip_function_object { using type = typename remove_class::type; @@ -186,11 +193,21 @@ namespace gapbind14 { struct CppFunction : CppFunctionBase {}; + // noexcept free functions + template + struct CppFunction + : CppFunctionBase {}; + // Function pointers . . . template struct CppFunction : CppFunctionBase {}; + // noexcept function pointer + template + struct CppFunction + : CppFunctionBase {}; + // Member functions . . . template struct CppFunction @@ -201,6 +218,16 @@ namespace gapbind14 { struct CppFunction : CppMemFnBase {}; + // Const noexcept member functions + template + struct CppFunction + : CppMemFnBase {}; + + // Non-const noexcept member functions + template + struct CppFunction + : CppMemFnBase {}; + // std::function objects template struct CppFunction> diff --git a/src/bipart.cpp b/src/bipart.cpp index dd6504154..3d15e5d08 100644 --- a/src/bipart.cpp +++ b/src/bipart.cpp @@ -34,16 +34,15 @@ #include "gap_all.h" // libsemigroups headers -#include "libsemigroups/bipart.hpp" // for Blocks, Bipartition, validate -#include "libsemigroups/report.hpp" // for Reporter, etc -#include "libsemigroups/timer.hpp" // for Timer -#include "semigroups-config.hpp" // for SEMIGROUPS_KERNEL_DEBUG +#include "libsemigroups/bipart.hpp" // for Blocks, Bipartition, validate +#include "libsemigroups/detail/report.hpp" // for Reporter, etc +#include "libsemigroups/detail/timer.hpp" // for Timer +#include "semigroups-config.hpp" // for SEMIGROUPS_KERNEL_DEBUG #include "gapbind14/gapbind14.hpp" // for GAPBIND14_TRY using libsemigroups::Bipartition; using libsemigroups::Blocks; -using libsemigroups::REPORTER; using libsemigroups::detail::Timer; // Global variables @@ -277,7 +276,7 @@ Obj BIPART_PROD(Obj x, Obj y) { Bipartition* yy = bipart_get_cpp(y); Bipartition* z = new Bipartition(xx->degree()); - z->product_inplace(*xx, *yy); + z->product_inplace_no_checks(*xx, *yy); return bipart_new_obj(static_cast(z)); } @@ -706,11 +705,11 @@ Obj BLOCKS_NC(Obj self, Obj gap_blocks) { SEMIGROUPS_ASSERT(IS_INTOBJ(ELM_LIST(block, j))); int jj = INT_INTOBJ(ELM_LIST(block, j)); if (jj < 0) { - blocks->set_block(-jj - 1, i - 1); - blocks->set_is_transverse_block(i - 1, false); + blocks->block(-jj - 1, i - 1); + blocks->is_transverse_block(i - 1, false); } else { - blocks->set_block(jj - 1, i - 1); - blocks->set_is_transverse_block(i - 1, true); + blocks->block(jj - 1, i - 1); + blocks->is_transverse_block(i - 1, true); } } } @@ -1000,8 +999,8 @@ Obj BLOCKS_LEFT_ACT(Obj self, Obj blocks_gap, Obj x_gap) { tab[j] = next; next++; } - out_blocks->set_block(i, tab[j]); - out_blocks->set_is_transverse_block(tab[j], _BUFFER_bool[j]); + out_blocks->block(i, tab[j]); + out_blocks->is_transverse_block(tab[j], _BUFFER_bool[j]); } #ifdef SEMIGROUPS_KERNEL_DEBUG @@ -1055,8 +1054,8 @@ Obj BLOCKS_RIGHT_ACT(Obj self, Obj blocks_gap, Obj x_gap) { tab[j] = next; next++; } - out_blocks->set_block(i - n, tab[j]); - out_blocks->set_is_transverse_block(tab[j], _BUFFER_bool[j]); + out_blocks->block(i - n, tab[j]); + out_blocks->is_transverse_block(tab[j], _BUFFER_bool[j]); } #ifdef SEMIGROUPS_KERNEL_DEBUG libsemigroups::validate(*out_blocks); @@ -1210,7 +1209,7 @@ Obj BLOCKS_INV_RIGHT(Obj self, Obj blocks_gap, Obj x_gap) { continue; } } - if (junk == static_cast(-1)) { + if (junk == (uint32_t) -1) { junk = next; next++; } @@ -1399,10 +1398,10 @@ class IdempotentCounter { } std::vector count() { - libsemigroups::THREAD_ID_MANAGER.reset(); - REPORT_DEFAULT("using %llu / %llu additional threads", - _nr_threads, - std::thread::hardware_concurrency()); + libsemigroups::detail::reset_thread_ids(); + libsemigroups::report_default("using {} / {} additional threads", + _nr_threads, + std::thread::hardware_concurrency()); Timer timer; for (size_t i = 0; i < _nr_threads; i++) { @@ -1414,7 +1413,7 @@ class IdempotentCounter { _threads[i].join(); } - REPORT_TIME(timer); + libsemigroups::report_elapsed_time("", timer); size_t max = *max_element(_ranks.begin(), _ranks.end()) + 1; std::vector out = std::vector(max, 0); @@ -1450,7 +1449,7 @@ class IdempotentCounter { } } } - REPORT_DEFAULT("finished in %llu", timer.string().c_str()); + libsemigroups::report_default("finished in {}", timer); } // This is basically the same as BLOCKS_E_TESTER, but is required because we diff --git a/src/cong.cpp b/src/cong.cpp index 62cada6e5..3001f7b3d 100644 --- a/src/cong.cpp +++ b/src/cong.cpp @@ -37,7 +37,6 @@ // libsemigroups headers #include "libsemigroups/bipart.hpp" // for Bipartition -#include "libsemigroups/cong-intf.hpp" // for congruence_kind #include "libsemigroups/cong.hpp" // for Congruence #include "libsemigroups/constants.hpp" // for UNDEFINED etc #include "libsemigroups/froidure-pin.hpp" // for FroidurePin @@ -54,7 +53,10 @@ namespace libsemigroups { namespace gapbind14 { template <> - struct IsGapBind14Type : std::true_type {}; + struct IsGapBind14Type> + : std::true_type { + static constexpr std::string_view name = "Congruence"; + }; } // namespace gapbind14 @@ -89,58 +91,60 @@ void init_cong(gapbind14::Module& m) { // Cannot use FroidurePinBase rather than the specialisations because there's // no constructor for a Congruence from a FroidurePinBase&. - gapbind14::class_("Congruence") - .def(gapbind14::init const&>{}, - "make_from_froidurepin_bipartition") - .def(gapbind14::init> const&>{}, - "make_from_froidurepin_bmat") - .def(gapbind14::init const&>{}, - "make_from_froidurepin_bmat8") - .def(gapbind14::init const&>{}, - "make_from_froidurepin_pbr") -#ifdef LIBSEMIGROUPS_HPCOMBI_ENABLED - .def(gapbind14::init> const&>{}, - "make_from_froidurepin_leastpperm") - .def(gapbind14::init> const&>{}, - "make_from_froidurepin_leasttransf") -#endif - .def(gapbind14::init> const&>{}, - "make_from_froidurepin_transfUInt2") - .def(gapbind14::init> const&>{}, - "make_from_froidurepin_transfUInt4") - .def(gapbind14::init> const&>{}, - "make_from_froidurepin_ppermUInt2") - .def(gapbind14::init> const&>{}, - "make_from_froidurepin_ppermUInt4") - .def(gapbind14::init{}, - "make_from_fpsemigroup") - .def(gapbind14::init>{}, - "make_from_froidurepinbase") - .def(gapbind14::init{}, - "make_from_table") - .def("set_number_of_generators", &Congruence::set_number_of_generators) - .def("number_of_pairs", &Congruence::number_of_generating_pairs) - .def("add_pair", - overload_cast( - &Congruence::add_pair)) - .def("number_of_classes", &Congruence::number_of_classes) - .def("word_to_class_index", &Congruence::word_to_class_index) - .def("class_index_to_word", &Congruence::class_index_to_word) - .def("contains", &Congruence::contains) - .def("less", &Congruence::less) - .def("add_runner", - &Congruence::add_runner) - .def("is_quotient_obviously_infinite", - &Congruence::is_quotient_obviously_infinite) - .def("ntc", - [](Congruence& C) { - return gapbind14::make_iterator(C.cbegin_ntc(), C.cend_ntc()); - }) - .def("quotient_froidure_pin", &Congruence::quotient_froidure_pin); + // gapbind14::class_("Congruence") + // .def(gapbind14::init + // const&>{}, + // "make_from_froidurepin_bipartition") + // .def(gapbind14::init> const&>{}, + // "make_from_froidurepin_bmat") + // .def(gapbind14::init const&>{}, + // "make_from_froidurepin_bmat8") + // .def(gapbind14::init const&>{}, + // "make_from_froidurepin_pbr") + // #ifdef LIBSEMIGROUPS_HPCOMBI_ENABLED + // .def(gapbind14::init> const&>{}, + // "make_from_froidurepin_leastpperm") + // .def(gapbind14::init> const&>{}, + // "make_from_froidurepin_leasttransf") + // #endif + // .def(gapbind14::init> const&>{}, + // "make_from_froidurepin_transfUInt2") + // .def(gapbind14::init> const&>{}, + // "make_from_froidurepin_transfUInt4") + // .def(gapbind14::init> const&>{}, + // "make_from_froidurepin_ppermUInt2") + // .def(gapbind14::init> const&>{}, + // "make_from_froidurepin_ppermUInt4") + // .def(gapbind14::init{}, + // "make_from_fpsemigroup") + // .def(gapbind14::init>{}, + // "make_from_froidurepinbase") + // .def(gapbind14::init{}, + // "make_from_table") + // .def("set_number_of_generators", + // &Congruence::set_number_of_generators) .def("number_of_pairs", + // &Congruence::number_of_generating_pairs) .def("add_pair", + // overload_cast( + // &Congruence::add_pair)) + // .def("number_of_classes", &Congruence::number_of_classes) + // .def("word_to_class_index", &Congruence::word_to_class_index) + // .def("class_index_to_word", &Congruence::class_index_to_word) + // .def("contains", &Congruence::contains) + // .def("less", &Congruence::less) + // .def("add_runner", + // &Congruence::add_runner) + // .def("is_quotient_obviously_infinite", + // &Congruence::is_quotient_obviously_infinite) + // .def("ntc", + // [](Congruence& C) { + // return gapbind14::make_iterator(C.cbegin_ntc(), C.cend_ntc()); + // }) + // .def("quotient_froidure_pin", &Congruence::quotient_froidure_pin); } diff --git a/src/conglatt.cpp b/src/conglatt.cpp index 6752f0303..1a7c74991 100644 --- a/src/conglatt.cpp +++ b/src/conglatt.cpp @@ -42,8 +42,8 @@ // libsemigroups headers #include "libsemigroups/adapters.hpp" // for Hash -#include "libsemigroups/report.hpp" // for should_report -#include "libsemigroups/string.hpp" // for group_digits +#include "libsemigroups/detail/report.hpp" // for should_report +#include "libsemigroups/detail/string.hpp" // for group_digits // namespace semigroups { @@ -215,7 +215,7 @@ namespace semigroups { auto start_time = std::chrono::high_resolution_clock::now(); auto last_report = start_time; uint32_t last_count = 1; - bool report = libsemigroups::report::should_report(); + bool report = libsemigroups::reporting_enabled(); std::vector gens; gens.reserve(LEN_LIST(list)); diff --git a/src/froidure-pin-base.cpp b/src/froidure-pin-base.cpp index 1e01da412..82b254e24 100644 --- a/src/froidure-pin-base.cpp +++ b/src/froidure-pin-base.cpp @@ -46,16 +46,20 @@ void init_froidure_pin_base(gapbind14::Module& m) { return S->right_cayley_graph(); }) .def("factorisation", - [](FroidurePin_ S, size_t i) { return S->factorisation(i); }) + [](FroidurePin_ S, size_t i) { + return libsemigroups::froidure_pin::factorisation(*S, i); + }) .def("minimal_factorisation", - [](FroidurePin_ S, size_t i) { return S->minimal_factorisation(i); }) + [](FroidurePin_ S, size_t i) { + return libsemigroups::froidure_pin::minimal_factorisation(*S, i); + }) .def("product_by_reduction", [](FroidurePin_ S, size_t i, size_t j) { - return S->product_by_reduction(i, j); + return libsemigroups::froidure_pin::product_by_reduction(*S, i, j); }) .def("current_position", [](FroidurePin_ S, libsemigroups::word_type const& w) { - return S->current_position(w); + return libsemigroups::froidure_pin::current_position(*S, w); }) .def("current_size", [](FroidurePin_ S) { return S->current_size(); }) .def("size", [](FroidurePin_ S) { return S->size(); }) diff --git a/src/froidure-pin-fallback.cpp b/src/froidure-pin-fallback.cpp index d61965562..a7b5e0f0d 100644 --- a/src/froidure-pin-fallback.cpp +++ b/src/froidure-pin-fallback.cpp @@ -32,8 +32,8 @@ #include "semigroups-debug.hpp" // for SEMIGROUPS_ASSERT // libsemigroups headers -#include "libsemigroups/report.hpp" // for REPORTER, Reporter -#include "libsemigroups/timer.hpp" // for Timer +#include "libsemigroups/detail/report.hpp" // for REPORTER, Reporter +#include "libsemigroups/detail/timer.hpp" // for Timer using libsemigroups::detail::Timer; diff --git a/src/froidure-pin.hpp b/src/froidure-pin.hpp index d77c1bcec..5d94af043 100644 --- a/src/froidure-pin.hpp +++ b/src/froidure-pin.hpp @@ -20,7 +20,6 @@ #define SEMIGROUPS_SRC_FROIDURE_PIN_HPP_ #include // for size_t -#include // for shared_ptr #include // for string #include // for true_type #include // for pair @@ -62,26 +61,33 @@ void bind_froidure_pin(gapbind14::Module& m, std::string name) { gapbind14::class_(name) .def(gapbind14::init<>{}, "make") .def(gapbind14::init{}, "copy") - .def("add_generator", &FroidurePin_::add_generator) + .def("add_generator", + [](FroidurePin_& S, element_type const& x) { + return S.add_generator(x); + }) .def("generator", &FroidurePin_::generator) .def("closure", - &FroidurePin_::template closure>) + [](FroidurePin_& S, std::vector const& gens) { + return libsemigroups::froidure_pin::closure(S, gens); + }) .def("number_of_generators", &FroidurePin_::number_of_generators) .def("size", &FroidurePin_::size) .def("at", &FroidurePin_::at) .def("sorted_at", &FroidurePin_::sorted_at) .def("current_position", - gapbind14::overload_cast( - &FroidurePin_::current_position)) + [](FroidurePin_& S, const_reference x) { + return S.current_position(x); + }) .def("sorted_position", &FroidurePin_::sorted_position) .def("number_of_idempotents", &FroidurePin_::number_of_idempotents) .def("enumerate", &FroidurePin_::enumerate) .def("left_cayley_graph", &FroidurePin_::left_cayley_graph) .def("right_cayley_graph", &FroidurePin_::right_cayley_graph) .def("factorisation", - gapbind14::overload_cast(&FroidurePin_::factorisation)) - .def("position_to_sorted_position", - &FroidurePin_::position_to_sorted_position) + [](FroidurePin_& S, size_t i) { + return libsemigroups::froidure_pin::factorisation(S, i); + }) + .def("to_sorted_position", &FroidurePin_::to_sorted_position) .def("fast_product", &FroidurePin_::fast_product) .def("is_idempotent", &FroidurePin_::is_idempotent) .def("finished", &FroidurePin_::finished) diff --git a/src/pkg.cpp b/src/pkg.cpp index 206c4b1e9..63848a838 100644 --- a/src/pkg.cpp +++ b/src/pkg.cpp @@ -51,18 +51,16 @@ #include "gapbind14/gapbind14.hpp" // for class_, InstallGlobalFunction // libsemigroups headers -#include "libsemigroups/bipart.hpp" // for Blocks, Bipartition -#include "libsemigroups/cong-intf.hpp" // for congruence_kind -#include "libsemigroups/digraph.hpp" // for ActionDigraph -#include "libsemigroups/fpsemi.hpp" // for FpSemigroup -#include "libsemigroups/freeband.hpp" // for freeband_equal_to -#include "libsemigroups/report.hpp" // for REPORTER, Reporter -#include "libsemigroups/sims1.hpp" // for Sims1 -#include "libsemigroups/todd-coxeter.hpp" // for ToddCoxeter, ToddCoxeter::table_type -#include "libsemigroups/types.hpp" // for word_type, letter_type - #include "libsemigroups/adapters.hpp" -#include "libsemigroups/uf.hpp" +#include "libsemigroups/bipart.hpp" // for Blocks, Bipartition +#include "libsemigroups/freeband.hpp" // for freeband_equal_to +#include "libsemigroups/presentation.hpp" // for Presentation +#include "libsemigroups/sims.hpp" // for Sims1 +#include "libsemigroups/todd-coxeter.hpp" // for ToddCoxeter, ToddCoxeter::word_graph_type +#include "libsemigroups/types.hpp" // for word_type, letter_type +#include "libsemigroups/word-graph.hpp" // for WordGraph + +#include "libsemigroups/detail/report.hpp" // for REPORTER, Reporter using libsemigroups::Bipartition; using libsemigroups::Blocks; @@ -70,22 +68,16 @@ using libsemigroups::Blocks; using libsemigroups::Hash; using libsemigroups::detail::Duf; -namespace { - void set_report(bool const val) { - libsemigroups::REPORTER.report(val); - } -} // namespace - namespace gapbind14 { template <> struct IsGapBind14Type> : std::true_type {}; template <> - struct IsGapBind14Type> : std::true_type {}; + struct IsGapBind14Type : std::true_type {}; template <> - struct IsGapBind14Type::iterator> + struct IsGapBind14Type : std::true_type {}; template <> @@ -97,9 +89,9 @@ GAPBIND14_MODULE(libsemigroups) { // Free functions //////////////////////////////////////////////////////////////////////// - gapbind14::InstallGlobalFunction("set_report", &set_report); - gapbind14::InstallGlobalFunction("should_report", - &libsemigroups::report::should_report); + // gapbind14::InstallGlobalFunction("set_report", &set_report); + gapbind14::InstallGlobalFunction("reporting_enabled", + &libsemigroups::reporting_enabled); gapbind14::InstallGlobalFunction("hardware_concurrency", &std::thread::hardware_concurrency); gapbind14::InstallGlobalFunction( @@ -127,37 +119,20 @@ GAPBIND14_MODULE(libsemigroups) { init_cong(gapbind14::module()); //////////////////////////////////////////////////////////////////////// - // FpSemigroup + // ToddCoxeter //////////////////////////////////////////////////////////////////////// - using libsemigroups::FpSemigroup; using libsemigroups::word_type; - gapbind14::class_("FpSemigroup") - .def(gapbind14::init<>{}) - .def("set_alphabet", - gapbind14::overload_cast(&FpSemigroup::set_alphabet)) - .def("add_rule", - gapbind14::overload_cast( - &FpSemigroup::add_rule)) - .def("set_identity", - gapbind14::overload_cast( - &FpSemigroup::set_identity)); - - //////////////////////////////////////////////////////////////////////// - // ToddCoxeter - //////////////////////////////////////////////////////////////////////// - using libsemigroups::congruence_kind; - using libsemigroups::congruence::ToddCoxeter; - using table_type = libsemigroups::congruence::ToddCoxeter::table_type; + using libsemigroups::Presentation; + using libsemigroups::ToddCoxeter; - gapbind14::class_("ToddCoxeter") - .def(gapbind14::init{}) - .def("set_number_of_generators", &ToddCoxeter::set_number_of_generators) - .def("number_of_generators", &ToddCoxeter::number_of_generators) - .def("prefill", - gapbind14::overload_cast(&ToddCoxeter::prefill)); + using word_graph_type + = libsemigroups::ToddCoxeter::word_graph_type; + + gapbind14::class_>("ToddCoxeter") + .def(gapbind14::init>{}); using libsemigroups::Presentation; @@ -177,7 +152,8 @@ GAPBIND14_MODULE(libsemigroups) { [](Presentation& thing, bool val) -> void { thing.contains_empty_word(val); }) - .def("validate", &Presentation::validate) + .def("throw_if_bad_alphabet_or_rules", + &Presentation::throw_if_bad_alphabet_or_rules) .def("number_of_rules", [](Presentation const& thing) -> size_t { return thing.rules.size(); @@ -192,40 +168,27 @@ GAPBIND14_MODULE(libsemigroups) { using libsemigroups::Sims1; - gapbind14::class_::iterator>("Sims1Iterator") - .def("increment", [](typename Sims1::iterator& it) { ++it; }) - .def("deref", - [](typename Sims1::iterator const& it) { return *it; }); + gapbind14::class_("Sims1Iterator") + .def("increment", [](typename Sims1::iterator& it) { ++it; }) + .def("deref", [](typename Sims1::iterator const& it) { return *it; }); - gapbind14::class_>("Sims1") - .def(gapbind14::init{}, "make") - .def("short_rules", - [](Sims1& s, Presentation const& p) { - s.short_rules(p); - }) - .def("extra", - [](Sims1& s, Presentation const& p) { - s.extra(p); - }) + gapbind14::class_("Sims1") + .def(gapbind14::init>{}, "make") .def("number_of_threads", - [](Sims1& s, size_t val) { s.number_of_threads(val); }) - .def("number_of_congruences", &Sims1::number_of_congruences) - .def("cbegin", &Sims1::cbegin); + [](Sims1& s, size_t val) { s.number_of_threads(val); }) + .def("number_of_congruences", &Sims1::number_of_congruences) + .def("cbegin", &Sims1::cbegin); using libsemigroups::RepOrc; gapbind14::class_("RepOrc") .def(gapbind14::init<>{}, "make") - .def("short_rules", - [](RepOrc& ro, Presentation const& p) { - ro.short_rules(p); - }) .def("number_of_threads", [](RepOrc& ro, size_t val) { ro.number_of_threads(val); }) .def("max_nodes", [](RepOrc& ro, size_t val) { ro.max_nodes(val); }) .def("min_nodes", [](RepOrc& ro, size_t val) { ro.min_nodes(val); }) .def("target_size", [](RepOrc& ro, size_t val) { ro.target_size(val); }) - .def("digraph", &RepOrc::digraph); + .def("word_graph", &RepOrc::word_graph); } //////////////////////////////////////////////////////////////////////// @@ -324,10 +287,10 @@ void TBlocksObjLoadFunc(Obj o) { Blocks* blocks = new Blocks(deg); for (size_t i = 0; i < deg; i++) { - blocks->set_block(i, LoadUInt4()); + blocks->block(i, LoadUInt4()); } for (size_t i = 0; i < nr_blocks; i++) { - blocks->set_is_transverse_block(i, static_cast(LoadUInt1())); + blocks->is_transverse_block(i, static_cast(LoadUInt1())); } #ifdef SEMIGROUPS_KERNEL_DEBUG libsemigroups::validate(*blocks); @@ -597,7 +560,7 @@ static Int InitKernel(StructInitInfo* module) { } static Int PostRestore(StructInitInfo* module) { - set_report(false); + // TODO set_report(false); return 0; } diff --git a/src/pkg.hpp b/src/pkg.hpp index ae469533f..113db6b63 100644 --- a/src/pkg.hpp +++ b/src/pkg.hpp @@ -33,6 +33,9 @@ #include "gapbind14/gapbind14.hpp" +#include "libsemigroups/todd-coxeter.hpp" // for ToddCoxeter +#include "libsemigroups/types.hpp" // for word_type + extern UInt T_BIPART; extern UInt T_BLOCKS; @@ -75,19 +78,10 @@ extern Obj Integers; extern Obj NrRows; extern Obj Matrix; -namespace libsemigroups { - class FpSemigroup; - namespace congruence { - class ToddCoxeter; - } -} // namespace libsemigroups - namespace gapbind14 { - template <> - struct IsGapBind14Type : std::true_type {}; template <> - struct IsGapBind14Type + struct IsGapBind14Type> : std::true_type {}; } // namespace gapbind14 diff --git a/src/to_cpp.hpp b/src/to_cpp.hpp index 55bfac79b..7473d5fe9 100644 --- a/src/to_cpp.hpp +++ b/src/to_cpp.hpp @@ -49,15 +49,16 @@ #include "gapbind14/to_gap.hpp" // for gap_tnum_type // libsemigroups headers -#include "libsemigroups/adapters.hpp" // for Degree -#include "libsemigroups/bmat8.hpp" // for BMat8 -#include "libsemigroups/cong.hpp" // for Congruence -#include "libsemigroups/constants.hpp" // for NegativeInfinity, PositiveIn... -#include "libsemigroups/containers.hpp" // for DynamicArray2 -#include "libsemigroups/matrix.hpp" // for NTPMat, MaxPlusTruncMat, Min... -#include "libsemigroups/pbr.hpp" // for PBR -#include "libsemigroups/transf.hpp" // for PPerm, Transf, IsPPerm -#include "libsemigroups/types.hpp" // for congruence_kind, congruence_... +#include "libsemigroups/adapters.hpp" // for Degree +#include "libsemigroups/bmat8.hpp" // for BMat8 +#include "libsemigroups/cong.hpp" // for Congruence +#include "libsemigroups/constants.hpp" // for NegativeInfinity, PositiveIn... +#include "libsemigroups/matrix.hpp" // for NTPMat, MaxPlusTruncMat, Min... +#include "libsemigroups/pbr.hpp" // for PBR +#include "libsemigroups/transf.hpp" // for PPerm, Transf, IsPPerm +#include "libsemigroups/types.hpp" // for congruence_kind, congruence_... + +#include "libsemigroups/detail/containers.hpp" // for DynamicArray2 namespace libsemigroups { class Bipartition; @@ -171,7 +172,6 @@ namespace gapbind14 { } } } - GAPBIND14_TRY(libsemigroups::validate(x)); return x; } }; @@ -199,7 +199,7 @@ namespace gapbind14 { } for (size_t j = 0; j < m; j++) { if (ELM_BLIST(row, j + 1) == True) { - x.set(i, j, 1); + x(i, j) = 1; } } } @@ -251,7 +251,7 @@ namespace gapbind14 { x(i, j) = itm; } } - GAPBIND14_TRY(libsemigroups::validate(x)); + // TODO GAPBIND14_TRY(libsemigroups::validate(x)); return x; } } // namespace detail @@ -282,7 +282,7 @@ namespace gapbind14 { ELM_MAT(o, INTOBJ_INT(i + 1), INTOBJ_INT(j + 1))); } } - GAPBIND14_TRY(libsemigroups::validate(x)); + // TODO GAPBIND14_TRY(libsemigroups::validate(x)); return x; } }; @@ -388,15 +388,13 @@ namespace gapbind14 { static gap_tnum_type constexpr gap_type = T_STRING; cpp_type operator()(Obj o) const { - if (!IS_STRING_REP(o)) { + if (TNUM_OBJ(o) != T_STRING && TNUM_OBJ(o) != T_STRING + IMMUTABLE) { ErrorQuit("expected string but got %s!", (Int) TNAM_OBJ(o), 0L); } std::string stype = std::string(CSTR_STRING(o)); - if (stype == "left") { - return congruence_kind::left; - } else if (stype == "right") { - return congruence_kind::right; - } else if (stype == "2-sided") { + if (stype == "onesided") { + return congruence_kind::onesided; + } else if (stype == "twosided") { return congruence_kind::twosided; } else { ErrorQuit("Unrecognised type %s", (Int) stype.c_str(), 0L); @@ -405,20 +403,23 @@ namespace gapbind14 { }; template <> - struct to_cpp { - using cpp_type = libsemigroups::Congruence::options::runners; + struct to_cpp { + using cpp_type = libsemigroups::Order; + static gap_tnum_type constexpr gap_type = T_STRING; cpp_type operator()(Obj o) const { - if (!IS_STRING_REP(o)) { + using Order = libsemigroups::Order; + if (TNUM_OBJ(o) != T_STRING && TNUM_OBJ(o) != T_STRING + IMMUTABLE) { ErrorQuit("expected string but got %s!", (Int) TNAM_OBJ(o), 0L); } - std::string stype = std::string(CSTR_STRING(o)); - if (stype == "none") { - return cpp_type::none; - } else if (stype == "standard") { - return cpp_type::standard; + std::string_view stype = CSTR_STRING(o); + if (stype == "shortlex") { + return Order::shortlex; + } else if (stype == "lex") { + return Order::lex; + // TODO the other cases } else { - ErrorQuit("Unrecognised type %s", (Int) stype.c_str(), 0L); + ErrorQuit("Unrecognised type %s", (Int) stype.begin(), 0L); } } }; diff --git a/src/to_gap.hpp b/src/to_gap.hpp index 6206b5910..b13a73717 100644 --- a/src/to_gap.hpp +++ b/src/to_gap.hpp @@ -41,18 +41,18 @@ #include "semigroups-debug.hpp" // for SEMIGROUPS_ASSERT // gapbind14 headers -#include "gapbind14/gapbind14.hpp" // for gapbind14 +#include "gapbind14/to_gap.hpp" // for gapbind14 // libsemigroups headers -#include "libsemigroups/adapters.hpp" // for Degree -#include "libsemigroups/bipart.hpp" // for Bipartition, IsBipartition -#include "libsemigroups/bmat8.hpp" // for BMat8 -#include "libsemigroups/config.hpp" // for LIBSEMIGROUPS_HPCOMBI_ENABLED -#include "libsemigroups/constants.hpp" // for NEGATIVE_INFINITY etc -#include "libsemigroups/digraph.hpp" // for ActionDigraph -#include "libsemigroups/matrix.hpp" // for matrix_threshold etc -#include "libsemigroups/pbr.hpp" // for PBR -#include "libsemigroups/transf.hpp" // for IsPPerm, IsTransf +#include "libsemigroups/adapters.hpp" // for Degree +#include "libsemigroups/bipart.hpp" // for Bipartition, IsBipartition +#include "libsemigroups/bmat8.hpp" // for BMat8 +#include "libsemigroups/config.hpp" // for LIBSEMIGROUPS_HPCOMBI_ENABLED +#include "libsemigroups/constants.hpp" // for NEGATIVE_INFINITY etc +#include "libsemigroups/matrix.hpp" // for matrix_threshold etc +#include "libsemigroups/pbr.hpp" // for PBR +#include "libsemigroups/transf.hpp" // for IsPPerm, IsTransf +#include "libsemigroups/word-graph.hpp" // for WordGraph using libsemigroups::IsBMat; using libsemigroups::IsIntMat; @@ -169,7 +169,7 @@ namespace gapbind14 { Obj blist = NewBag(T_BLIST, SIZE_PLEN_BLIST(n)); SET_LEN_BLIST(blist, n); for (size_t j = 0; j < n; j++) { - if (x.first.get(i, j)) { + if (x.first(i, j)) { SET_BIT_BLIST(blist, j + 1); } } @@ -241,7 +241,7 @@ namespace gapbind14 { struct to_gap> { using MaxPlusTruncMat_ = libsemigroups::MaxPlusTruncMat<>; Obj operator()(MaxPlusTruncMat_ const& x) { - using libsemigroups::matrix_threshold; + using libsemigroups::matrix::threshold; using scalar_type = typename MaxPlusTruncMat_::scalar_type; auto result = detail::make_matrix( @@ -249,9 +249,8 @@ namespace gapbind14 { return (y == NEGATIVE_INFINITY ? to_gap()(y) : to_gap()(y)); }); - SET_ELM_PLIST(result, - x.number_of_rows() + 1, - to_gap()(matrix_threshold(x))); + SET_ELM_PLIST( + result, x.number_of_rows() + 1, to_gap()(threshold(x))); return result; } }; @@ -265,7 +264,7 @@ namespace gapbind14 { using MinPlusTruncMat_ = libsemigroups::MinPlusTruncMat<>; Obj operator()(MinPlusTruncMat_ const& x) { - using libsemigroups::matrix_threshold; + using libsemigroups::matrix::threshold; using scalar_type = typename MinPlusTruncMat_::scalar_type; auto result = detail::make_matrix( @@ -273,9 +272,8 @@ namespace gapbind14 { return (y == POSITIVE_INFINITY ? to_gap()(y) : to_gap()(y)); }); - SET_ELM_PLIST(result, - x.number_of_rows() + 1, - to_gap()(matrix_threshold(x))); + SET_ELM_PLIST( + result, x.number_of_rows() + 1, to_gap()(threshold(x))); return result; } }; @@ -309,16 +307,14 @@ namespace gapbind14 { Obj operator()(NTPMat_ const& x) { using scalar_type = typename NTPMat_::scalar_type; - using libsemigroups::matrix_period; - using libsemigroups::matrix_threshold; + using libsemigroups::matrix::period; + using libsemigroups::matrix::threshold; auto result = detail::make_matrix(x, NTPMatrixType, 2); - SET_ELM_PLIST(result, - x.number_of_rows() + 1, - to_gap()(matrix_threshold(x))); - SET_ELM_PLIST(result, - x.number_of_rows() + 2, - to_gap()(matrix_period(x))); + SET_ELM_PLIST( + result, x.number_of_rows() + 1, to_gap()(threshold(x))); + SET_ELM_PLIST( + result, x.number_of_rows() + 2, to_gap()(period(x))); return result; } }; @@ -515,24 +511,24 @@ namespace gapbind14 { }; //////////////////////////////////////////////////////////////////////// - // ActionDigraph + // WordGraph //////////////////////////////////////////////////////////////////////// template - struct to_gap> { - using ActionDigraph_ = libsemigroups::ActionDigraph; - Obj operator()(ActionDigraph_ const& ad) const noexcept { - using node_type = typename ActionDigraph_::node_type; + struct to_gap> { + using WordGraph_ = libsemigroups::WordGraph; + Obj operator()(WordGraph_ const& ad) const noexcept { + using node_type = typename WordGraph_::node_type; Obj result = NEW_PLIST(T_PLIST, ad.number_of_nodes()); // this is intentionally not IMMUTABLE - // TODO(ActionDigraph) handle case of zero nodes? + // TODO(WordGraph) handle case of zero nodes? SET_LEN_PLIST(result, ad.number_of_nodes()); for (size_t i = 0; i < ad.number_of_nodes(); ++i) { Obj next = NEW_PLIST(T_PLIST, 0); SET_LEN_PLIST(next, 0); for (size_t j = 0; j < ad.out_degree(); ++j) { - auto val = ad.unsafe_neighbor(i, j); + auto val = ad.target_no_checks(i, j); if (val != UNDEFINED) { AssPlist(next, j + 1, to_gap()(val + 1)); } @@ -544,5 +540,23 @@ namespace gapbind14 { } }; + // TODO could use magic_enum and make this generic + template <> + struct to_gap { + Obj operator()(libsemigroups::Order const& val) const noexcept { + using order = libsemigroups::Order; + switch (val) { + case order::shortlex: + return to_gap()("shortlex"); + case order::lex: + return to_gap()("lex"); + case order::none: + return to_gap()("none"); + case order::recursive: + return to_gap()("recursive"); + } + } + }; + } // namespace gapbind14 #endif // SEMIGROUPS_SRC_TO_GAP_HPP_ From bbeab766c9a3ad3330664c97af54f4a05ecab278 Mon Sep 17 00:00:00 2001 From: "James D. Mitchell" Date: Tue, 8 Apr 2025 10:54:37 +0200 Subject: [PATCH 02/27] More --- src/cong.cpp | 97 ++-------------------------------------------------- src/cong.hpp | 1 + src/pkg.cpp | 44 ++++++++++++++++++++++-- 3 files changed, 45 insertions(+), 97 deletions(-) diff --git a/src/cong.cpp b/src/cong.cpp index 3001f7b3d..625194c0c 100644 --- a/src/cong.cpp +++ b/src/cong.cpp @@ -36,21 +36,10 @@ #include "gapbind14/gapbind14.hpp" // for class_ etc // libsemigroups headers -#include "libsemigroups/bipart.hpp" // for Bipartition #include "libsemigroups/cong.hpp" // for Congruence -#include "libsemigroups/constants.hpp" // for UNDEFINED etc -#include "libsemigroups/froidure-pin.hpp" // for FroidurePin -#include "libsemigroups/matrix.hpp" // for BMat etc -#include "libsemigroups/todd-coxeter.hpp" // for ToddCoxeter -#include "libsemigroups/transf.hpp" // for PPerm etc +#include "libsemigroups/presentation.hpp" // for Presentation #include "libsemigroups/types.hpp" // for word_type -// Forward decls -namespace libsemigroups { - class FpSemigroup; - class PBR; -} // namespace libsemigroups - namespace gapbind14 { template <> struct IsGapBind14Type> @@ -60,91 +49,11 @@ namespace gapbind14 { } // namespace gapbind14 +// TODO rm this file //////////////////////////////////////////////////////////////////////// // Congruence //////////////////////////////////////////////////////////////////////// using gapbind14::overload_cast; -void init_cong(gapbind14::Module& m) { - using libsemigroups::Congruence; - using libsemigroups::congruence_kind; - using libsemigroups::FpSemigroup; - using libsemigroups::FroidurePin; - using libsemigroups::FroidurePinBase; - using libsemigroups::word_type; - - using libsemigroups::Bipartition; - using libsemigroups::BMat; - using libsemigroups::IntMat; - using libsemigroups::LeastPPerm; - using libsemigroups::LeastTransf; - using libsemigroups::MaxPlusMat; - using libsemigroups::MaxPlusTruncMat; - using libsemigroups::MinPlusMat; - using libsemigroups::MinPlusTruncMat; - using libsemigroups::NTPMat; - using libsemigroups::PBR; - using libsemigroups::PPerm; - using libsemigroups::ProjMaxPlusMat; - using libsemigroups::Transf; - - // Cannot use FroidurePinBase rather than the specialisations because there's - // no constructor for a Congruence from a FroidurePinBase&. - // gapbind14::class_("Congruence") - // .def(gapbind14::init - // const&>{}, - // "make_from_froidurepin_bipartition") - // .def(gapbind14::init> const&>{}, - // "make_from_froidurepin_bmat") - // .def(gapbind14::init const&>{}, - // "make_from_froidurepin_bmat8") - // .def(gapbind14::init const&>{}, - // "make_from_froidurepin_pbr") - // #ifdef LIBSEMIGROUPS_HPCOMBI_ENABLED - // .def(gapbind14::init> const&>{}, - // "make_from_froidurepin_leastpperm") - // .def(gapbind14::init> const&>{}, - // "make_from_froidurepin_leasttransf") - // #endif - // .def(gapbind14::init> const&>{}, - // "make_from_froidurepin_transfUInt2") - // .def(gapbind14::init> const&>{}, - // "make_from_froidurepin_transfUInt4") - // .def(gapbind14::init> const&>{}, - // "make_from_froidurepin_ppermUInt2") - // .def(gapbind14::init> const&>{}, - // "make_from_froidurepin_ppermUInt4") - // .def(gapbind14::init{}, - // "make_from_fpsemigroup") - // .def(gapbind14::init>{}, - // "make_from_froidurepinbase") - // .def(gapbind14::init{}, - // "make_from_table") - // .def("set_number_of_generators", - // &Congruence::set_number_of_generators) .def("number_of_pairs", - // &Congruence::number_of_generating_pairs) .def("add_pair", - // overload_cast( - // &Congruence::add_pair)) - // .def("number_of_classes", &Congruence::number_of_classes) - // .def("word_to_class_index", &Congruence::word_to_class_index) - // .def("class_index_to_word", &Congruence::class_index_to_word) - // .def("contains", &Congruence::contains) - // .def("less", &Congruence::less) - // .def("add_runner", - // &Congruence::add_runner) - // .def("is_quotient_obviously_infinite", - // &Congruence::is_quotient_obviously_infinite) - // .def("ntc", - // [](Congruence& C) { - // return gapbind14::make_iterator(C.cbegin_ntc(), C.cend_ntc()); - // }) - // .def("quotient_froidure_pin", &Congruence::quotient_froidure_pin); -} +void init_cong(gapbind14::Module& m) {} diff --git a/src/cong.hpp b/src/cong.hpp index ce6cd4cc3..f5862b8cc 100644 --- a/src/cong.hpp +++ b/src/cong.hpp @@ -1,4 +1,5 @@ // +// TODO rm this file // Semigroups package for GAP // Copyright (C) 2021 James D. Mitchell // diff --git a/src/pkg.cpp b/src/pkg.cpp index 63848a838..6fa6fb3d3 100644 --- a/src/pkg.cpp +++ b/src/pkg.cpp @@ -65,14 +65,22 @@ using libsemigroups::Bipartition; using libsemigroups::Blocks; -using libsemigroups::Hash; -using libsemigroups::detail::Duf; +namespace { + void LIBSEMIGROUPS_REPORTING_ENABLED(bool const val) { + static std::unique_ptr rg; + rg = std::make_unique(val); + } +} // namespace namespace gapbind14 { template <> struct IsGapBind14Type> : std::true_type {}; + template <> + struct IsGapBind14Type> + : std::true_type {}; + template <> struct IsGapBind14Type : std::true_type {}; @@ -89,7 +97,8 @@ GAPBIND14_MODULE(libsemigroups) { // Free functions //////////////////////////////////////////////////////////////////////// - // gapbind14::InstallGlobalFunction("set_report", &set_report); + gapbind14::InstallGlobalFunction("set_report", + &LIBSEMIGROUPS_REPORTING_ENABLED); gapbind14::InstallGlobalFunction("reporting_enabled", &libsemigroups::reporting_enabled); gapbind14::InstallGlobalFunction("hardware_concurrency", @@ -189,6 +198,35 @@ GAPBIND14_MODULE(libsemigroups) { .def("min_nodes", [](RepOrc& ro, size_t val) { ro.min_nodes(val); }) .def("target_size", [](RepOrc& ro, size_t val) { ro.target_size(val); }) .def("word_graph", &RepOrc::word_graph); + + using libsemigroups::Congruence; + using libsemigroups::congruence_kind; + using libsemigroups::FroidurePinBase; + using libsemigroups::Presentation; + using libsemigroups::word_type; + + gapbind14::class_>("Congruence") + .def(gapbind14::init>{}, "make"); + // .def("number_of_generating_pairs", + // &Congruence::number_of_generating_pairs) + // .def("add_generating_pair", + // [](Congruence& self, + // word_type const& u, + // word_type const& v) { + // return libsemigroups::congruence::add_generating_pair(self, u, v); + // }) + // .def("number_of_classes", &Congruence::number_of_classes) + // // .def("index_of", &Congruence::word_to_class_index) + // // .def("word_of", &Congruence::class_index_to_word) + // .def("contains", + // [](Congruence& self, + // word_type const& u, + // word_type const& v) { + // return libsemigroups::congruence::contains(self, u, v); + // }); + // .def("non_trivial_classes", [](Congruence& C) { + // return gapbind14::make_iterator(C.cbegin_ntc(), C.cend_ntc()); + // }); } //////////////////////////////////////////////////////////////////////// From a523edffaf5ddb90b42a45ebb7600ed641137184 Mon Sep 17 00:00:00 2001 From: Joseph Edwards Date: Tue, 2 Sep 2025 19:07:35 +0100 Subject: [PATCH 03/27] Add Presentation --- gap/libsemigroups/fpsemi.gd | 16 ------------ gap/libsemigroups/presentation.gd | 16 ++++++++++++ .../{fpsemi.gi => presentation.gi} | 26 ++++++++++--------- init.g | 2 +- read.g | 2 +- 5 files changed, 32 insertions(+), 30 deletions(-) delete mode 100644 gap/libsemigroups/fpsemi.gd create mode 100644 gap/libsemigroups/presentation.gd rename gap/libsemigroups/{fpsemi.gi => presentation.gi} (62%) diff --git a/gap/libsemigroups/fpsemi.gd b/gap/libsemigroups/fpsemi.gd deleted file mode 100644 index 2176a5db3..000000000 --- a/gap/libsemigroups/fpsemi.gd +++ /dev/null @@ -1,16 +0,0 @@ -############################################################################# -## -## libsemigroups/fpsemi.gd -## Copyright (C) 2022 James D. Mitchell -## -## Licensing information can be found in the README file of this package. -## -############################################################################# -## - -# This file exists to construct a libsemigroups FpSemigroup object for an fp -# semigroup or monoid. The resulting libsemigroups::FpSemigroup object is only -# used to initialize a libsemigroups::Congruence object where most questions -# asked about FpSemigroup/FpMonoids will be answered. - -DeclareGlobalFunction("LibsemigroupsFpSemigroup"); diff --git a/gap/libsemigroups/presentation.gd b/gap/libsemigroups/presentation.gd new file mode 100644 index 000000000..b66110b34 --- /dev/null +++ b/gap/libsemigroups/presentation.gd @@ -0,0 +1,16 @@ +############################################################################# +## +## libsemigroups/presentation.gd +## Copyright (C) 2025 Joseph Edwards +## +## Licensing information can be found in the README file of this package. +## +############################################################################# +## + +# This file exists to construct a libsemigroups Presentation object for an fp +# semigroup or monoid. The resulting libsemigroups::Presentation object is only +# used to initialize a libsemigroups::Congruence object where most questions +# asked about Presentation/FpMonoids will be answered. + +DeclareGlobalFunction("LibsemigroupsPresentation"); diff --git a/gap/libsemigroups/fpsemi.gi b/gap/libsemigroups/presentation.gi similarity index 62% rename from gap/libsemigroups/fpsemi.gi rename to gap/libsemigroups/presentation.gi index 59a02d02a..1708c6ebf 100644 --- a/gap/libsemigroups/fpsemi.gi +++ b/gap/libsemigroups/presentation.gi @@ -1,25 +1,25 @@ ############################################################################# ## -## libsemigroups/fpsemi.gi -## Copyright (C) 2022 James D. Mitchell +## libsemigroups/presentation.gi +## Copyright (C) 2025 Joseph Edwards ## ## Licensing information can be found in the README file of this package. ## ############################################################################# ## -InstallGlobalFunction("LibsemigroupsFpSemigroup", +InstallGlobalFunction("LibsemigroupsPresentation", function(S) local F, SS, R, add_rule, pair; Assert(1, IsFpSemigroup(S) or (HasIsFreeSemigroup(S) and IsFreeSemigroup(S)) or IsFpMonoid(S) or (HasIsFreeMonoid(S) and IsFreeMonoid(S))); - if IsBound(S!.LibsemigroupsFpSemigroup) - and IsValidGapbind14Object(S!.LibsemigroupsFpSemigroup) then - return S!.LibsemigroupsFpSemigroup; + if IsBound(S!.LibsemigroupsPresentation) + and IsValidGapbind14Object(S!.LibsemigroupsPresentation) then + return S!.LibsemigroupsPresentation; fi; - Unbind(S!.LibsemigroupsFpSemigroup); + Unbind(S!.LibsemigroupsPresentation); if IsFpSemigroup(S) then F := FreeSemigroupOfFpSemigroup(S); elif IsFpMonoid(S) then @@ -29,25 +29,27 @@ function(S) F := S; fi; - SS := libsemigroups.FpSemigroup.make(); - libsemigroups.FpSemigroup.set_alphabet(SS, Size(GeneratorsOfSemigroup(S))); + SS := libsemigroups.Presentation.make(); + libsemigroups.Presentation.set_alphabet_size( + SS, + Size(GeneratorsOfSemigroup(S))); if IsMonoid(S) then # The identity must be 0 so that this corresponds to what happens in # FroidurePin, where GeneratorsOfSemigroup(S) is used and the identity is # the first entry. - libsemigroups.FpSemigroup.set_identity(SS, 0); + libsemigroups.Presentation.set_identity(SS, 0); R := RelationsOfFpMonoid(S); else R := RelationsOfFpSemigroup(S); fi; - add_rule := libsemigroups.FpSemigroup.add_rule; + add_rule := libsemigroups.presentation_add_rule; for pair in R do add_rule(SS, Factorization(F, pair[1]) - 1, Factorization(F, pair[2]) - 1); od; - S!.LibsemigroupsFpSemigroup := SS; + S!.LibsemigroupsPresentation := SS; return SS; end); diff --git a/init.g b/init.g index b60e86d88..56c7bca5a 100644 --- a/init.g +++ b/init.g @@ -65,7 +65,7 @@ ReadPackage("semigroups", "gap/elements/pbr.gd"); ReadPackage("semigroups", "gap/elements/pperm.gd"); ReadPackage("semigroups", "gap/elements/trans.gd"); -ReadPackage("semigroups", "gap/libsemigroups/fpsemi.gd"); +ReadPackage("semigroups", "gap/libsemigroups/presentation.gd"); ReadPackage("semigroups", "gap/libsemigroups/froidure-pin.gd"); ReadPackage("semigroups", "gap/libsemigroups/sims1.gd"); diff --git a/read.g b/read.g index 3cc69fd82..65581d5f3 100644 --- a/read.g +++ b/read.g @@ -26,7 +26,7 @@ ReadPackage("semigroups", "gap/elements/elements.gi"); ReadPackage("semigroups", "gap/elements/pperm.gi"); ReadPackage("semigroups", "gap/libsemigroups/cong.gi"); -ReadPackage("semigroups", "gap/libsemigroups/fpsemi.gi"); +ReadPackage("semigroups", "gap/libsemigroups/presentation.gi"); ReadPackage("semigroups", "gap/libsemigroups/froidure-pin.gi"); ReadPackage("semigroups", "gap/libsemigroups/sims1.gi"); From 1f9a1d5bed402a70caf1e3e4f6c7aa9560d81f2e Mon Sep 17 00:00:00 2001 From: Joseph Edwards Date: Tue, 2 Sep 2025 19:24:02 +0100 Subject: [PATCH 04/27] Update to_cpp congruence_kind --- src/to_cpp.hpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/to_cpp.hpp b/src/to_cpp.hpp index 7473d5fe9..e0ad3d413 100644 --- a/src/to_cpp.hpp +++ b/src/to_cpp.hpp @@ -392,12 +392,13 @@ namespace gapbind14 { ErrorQuit("expected string but got %s!", (Int) TNAM_OBJ(o), 0L); } std::string stype = std::string(CSTR_STRING(o)); - if (stype == "onesided") { + if (stype == "left" || stype == "right") { return congruence_kind::onesided; - } else if (stype == "twosided") { + } else if (stype == "2-sided") { return congruence_kind::twosided; } else { - ErrorQuit("Unrecognised type %s", (Int) stype.c_str(), 0L); + ErrorQuit( + "Unrecognised congruence_kind type %s", (Int) stype.c_str(), 0L); } } }; From da5b400d87f538a918e534150ef7e087588020d9 Mon Sep 17 00:00:00 2001 From: Joseph Edwards Date: Tue, 2 Sep 2025 19:24:20 +0100 Subject: [PATCH 05/27] Add to_cpp for WordGraph --- gap/libsemigroups/froidure-pin.gi | 8 ++++--- src/pkg.cpp | 5 +++++ src/pkg.hpp | 6 +++++- src/to_cpp.hpp | 35 +++++++++++++++++++++++++++++++ 4 files changed, 50 insertions(+), 4 deletions(-) diff --git a/gap/libsemigroups/froidure-pin.gi b/gap/libsemigroups/froidure-pin.gi index 99a0f6cc8..b6e25a855 100644 --- a/gap/libsemigroups/froidure-pin.gi +++ b/gap/libsemigroups/froidure-pin.gi @@ -607,7 +607,7 @@ function(S) Error("the argument (a semigroup) is not finite"); fi; F := LibsemigroupsFroidurePin(S); - return FroidurePinMemFnRec(S).left_cayley_graph(F) + 1; + return FroidurePinMemFnRec(S).left_cayley_graph(F); end); InstallMethod(LeftCayleyDigraph, @@ -634,7 +634,9 @@ function(S) Error("the argument (a semigroup) is not finite"); fi; F := LibsemigroupsFroidurePin(S); - return FroidurePinMemFnRec(S).right_cayley_graph(F) + 1; + + # No need to add 1 here, since this is handled by to_gap + return FroidurePinMemFnRec(S).right_cayley_graph(F); end); InstallMethod(RightCayleyDigraph, @@ -821,7 +823,7 @@ function(S) product := FroidurePinMemFnRec(S).product_by_reduction; FroidurePinMemFnRec(S).enumerate(T, N + 1); else - pos_to_pos_sorted := FroidurePinMemFnRec(S).position_to_sorted_position; + pos_to_pos_sorted := FroidurePinMemFnRec(S).to_sorted_position; product := FroidurePinMemFnRec(S).fast_product; fi; for i in [0 .. N - 1] do diff --git a/src/pkg.cpp b/src/pkg.cpp index 6fa6fb3d3..1530fe2cb 100644 --- a/src/pkg.cpp +++ b/src/pkg.cpp @@ -392,6 +392,8 @@ Obj TYPES_PBR; Obj TYPE_PBR; Obj DegreeOfPBR; Obj LARGEST_MOVED_PT_TRANS; +Obj IsDigraph; +Obj OutNeighbours; Obj IsSemigroup; Obj IsMatrixObj; @@ -587,6 +589,9 @@ static Int InitKernel(StructInitInfo* module) { ImportGVarFromLibrary("LARGEST_MOVED_PT_TRANS", &LARGEST_MOVED_PT_TRANS); + ImportGVarFromLibrary("IsDigraph", &IsDigraph); + ImportGVarFromLibrary("OutNeighbours", &OutNeighbours); + ImportGVarFromLibrary("IsSemigroup", &IsSemigroup); ImportGVarFromLibrary("IsMatrixObj", &IsMatrixObj); ImportGVarFromLibrary("BaseDomain", &BaseDomain); diff --git a/src/pkg.hpp b/src/pkg.hpp index 113db6b63..b7d5eab9d 100644 --- a/src/pkg.hpp +++ b/src/pkg.hpp @@ -34,7 +34,7 @@ #include "gapbind14/gapbind14.hpp" #include "libsemigroups/todd-coxeter.hpp" // for ToddCoxeter -#include "libsemigroups/types.hpp" // for word_type +#include "libsemigroups/types.hpp" // for word_type, congruence_kind extern UInt T_BIPART; extern UInt T_BLOCKS; @@ -71,6 +71,10 @@ extern Obj TYPE_BIPART; extern Obj TYPES_BIPART; extern Obj LARGEST_MOVED_PT_TRANS; +extern Obj IsDigraph; +extern Obj DigraphNrVertices; +extern Obj OutNeighbours; + extern Obj IsSemigroup; extern Obj IsMatrixObj; extern Obj BaseDomain; diff --git a/src/to_cpp.hpp b/src/to_cpp.hpp index e0ad3d413..affe4e3df 100644 --- a/src/to_cpp.hpp +++ b/src/to_cpp.hpp @@ -93,6 +93,8 @@ using libsemigroups::UNDEFINED; using libsemigroups::detail::DynamicArray2; +using libsemigroups::WordGraph; + namespace semigroups { NTPSemiring<> const* semiring(size_t threshold, size_t period); @@ -738,5 +740,38 @@ namespace gapbind14 { return result; } }; + + //////////////////////////////////////////////////////////////////////// + // WordGraph + //////////////////////////////////////////////////////////////////////// + template + struct to_cpp> { + using cpp_type = WordGraph; + cpp_type operator()(Obj o) const { + if (CALL_1ARGS(IsDigraph, o) != True) { + ErrorQuit("expected a Digraph but got %s!", (Int) TNAM_OBJ(o), 0L); + } + Obj out_nbs = CALL_1ARGS(OutNeighbours, o); + size_t nr_vertices = LEN_LIST(out_nbs); + size_t out_degree + = (nr_vertices == 0 ? 0 : LEN_LIST(ELM_LIST(out_nbs, 1))); + + cpp_type result(nr_vertices, out_degree); + for (size_t s = 0; s < nr_vertices; ++s) { + Obj nbs = ELM_LIST(out_nbs, s + 1); + if (LEN_LIST(nbs) != out_degree) { + ErrorQuit("expected a digraph with constant out degree, but found " + "vertices with outdegree %d and %d", + (Int) LEN_LIST(nbs), + out_degree); + } + for (size_t a = 0; a < out_degree; ++a) { + size_t t = INT_INTOBJ(ELM_LIST(nbs, a + 1)) - 1; + result.target(s, a, t); + } + } + return result; + } + }; } // namespace gapbind14 #endif // SEMIGROUPS_SRC_TO_CPP_HPP_ From 6423446b6c60df780f51b57fb9805375cf4b8267 Mon Sep 17 00:00:00 2001 From: Joseph Edwards Date: Tue, 2 Sep 2025 19:32:27 +0100 Subject: [PATCH 06/27] Remove congruence_make_from_... --- gap/libsemigroups/cong.gi | 75 +---------------------------- tst/standard/libsemigroups/cong.tst | 24 --------- tst/testinstall.tst | 28 ++++++----- 3 files changed, 16 insertions(+), 111 deletions(-) diff --git a/gap/libsemigroups/cong.gi b/gap/libsemigroups/cong.gi index 68c338e66..0b487df96 100644 --- a/gap/libsemigroups/cong.gi +++ b/gap/libsemigroups/cong.gi @@ -69,84 +69,11 @@ InstallTrueMethod(CanUseLibsemigroupsCongruence, # libsemigroups object directly ########################################################################### -DeclareAttribute("LibsemigroupsCongruenceConstructor", -IsSemigroup and CanUseLibsemigroupsCongruences); - -# Construct a libsemigroups::Congruence from some GAP object - -InstallMethod(LibsemigroupsCongruenceConstructor, -"for a transformation semigroup with CanUseLibsemigroupsCongruences", -[IsTransformationSemigroup and CanUseLibsemigroupsCongruences], -function(S) - local N; - N := DegreeOfTransformationSemigroup(S); - if N <= 16 and IsBound(LIBSEMIGROUPS_HPCOMBI_ENABLED) then - return libsemigroups.Congruence.make_from_froidurepin_leasttransf; - elif N <= 2 ^ 16 then - return libsemigroups.Congruence.make_from_froidurepin_transfUInt2; - elif N <= 2 ^ 32 then - return libsemigroups.Congruence.make_from_froidurepin_transfUInt4; - else - # Cannot currently test the next line - Error("transformation degree is too high!"); - fi; -end); - -InstallMethod(LibsemigroupsCongruenceConstructor, -"for a partial perm semigroup with CanUseLibsemigroupsCongruences", -[IsPartialPermSemigroup and CanUseLibsemigroupsCongruences], -function(S) - local N; - N := Maximum(DegreeOfPartialPermSemigroup(S), - CodegreeOfPartialPermSemigroup(S)); - if N <= 16 and IsBound(LIBSEMIGROUPS_HPCOMBI_ENABLED) then - return libsemigroups.Congruence.make_from_froidurepin_leastpperm; - elif N <= 2 ^ 16 then - return libsemigroups.Congruence.make_from_froidurepin_ppermUInt2; - elif N <= 2 ^ 32 then - return libsemigroups.Congruence.make_from_froidurepin_ppermUInt4; - else - # Cannot currently test the next line - Error("partial perm degree is too high!"); - fi; -end); - -InstallMethod(LibsemigroupsCongruenceConstructor, -"for a boolean matrix semigroup with CanUseLibsemigroupsCongruences", -[IsBooleanMatSemigroup and CanUseLibsemigroupsCongruences], -function(S) - if DimensionOfMatrixOverSemiring(Representative(S)) <= 8 then - return libsemigroups.Congruence.make_from_froidurepin_bmat8; - fi; - return libsemigroups.Congruence.make_from_froidurepin_bmat; -end); - -# Why does this work for types other than boolean matrices? -InstallMethod(LibsemigroupsCongruenceConstructor, -"for a matrix semigroup with CanUseLibsemigroupsCongruences", -[IsMatrixOverSemiringSemigroup and CanUseLibsemigroupsCongruences], -_ -> libsemigroups.Congruence.make_from_froidurepin_bmat); - -InstallMethod(LibsemigroupsCongruenceConstructor, -"for a bipartition semigroup with CanUseLibsemigroupsCongruences", -[IsBipartitionSemigroup and CanUseLibsemigroupsCongruences], -_ -> libsemigroups.Congruence.make_from_froidurepin_bipartition); - -InstallMethod(LibsemigroupsCongruenceConstructor, -"for a PBR semigroup and CanUseLibsemigroupsCongruences", -[IsPBRSemigroup and CanUseLibsemigroupsCongruences], -_ -> libsemigroups.Congruence.make_from_froidurepin_pbr); - -InstallMethod(LibsemigroupsCongruenceConstructor, -"for a quotient semigroup and CanUseLibsemigroupsCongruences", -[IsQuotientSemigroup and CanUseLibsemigroupsCongruences], -_ -> libsemigroups.Congruence.make_from_froidurepinbase); - # Get the libsemigroups::Congruence object associated to a GAP object BindGlobal("LibsemigroupsCongruence", function(C) - local S, make, CC, factor, N, tc, table, add_pair, pair; + local S, make, CC, factor, N, tc, cayley_digraph, add_generating_pair, pair; Assert(1, CanUseLibsemigroupsCongruence(C)); diff --git a/tst/standard/libsemigroups/cong.tst b/tst/standard/libsemigroups/cong.tst index b3cb1d4cb..c10059a13 100644 --- a/tst/standard/libsemigroups/cong.tst +++ b/tst/standard/libsemigroups/cong.tst @@ -18,69 +18,45 @@ gap> SEMIGROUPS.StartTest(); # LibsemigroupsCongruenceConstructor for transf. semigroup gap> S := Semigroup(Transformation([1, 1, 2])); -gap> LibsemigroupsCongruenceConstructor(S); -function( arg1, arg2 ) ... end gap> S := Semigroup(ConstantTransformation(17, 2)); -gap> LibsemigroupsCongruenceConstructor(S); -function( arg1, arg2 ) ... end gap> S := Semigroup(ConstantTransformation(65537, 2)); -gap> LibsemigroupsCongruenceConstructor(S); -function( arg1, arg2 ) ... end # LibsemigroupsCongruenceConstructor for pperm semigroup gap> S := Semigroup(PartialPerm([1, 2, 5])); -gap> LibsemigroupsCongruenceConstructor(S); -function( arg1, arg2 ) ... end gap> S := Semigroup(PartialPerm([1 .. 17])); -gap> LibsemigroupsCongruenceConstructor(S); -function( arg1, arg2 ) ... end gap> S := Semigroup(PartialPerm([1 .. 65537])); -gap> LibsemigroupsCongruenceConstructor(S); -function( arg1, arg2 ) ... end # LibsemigroupsCongruenceConstructor for a bmat semigroup gap> S := Semigroup(Matrix(IsBooleanMat, [[0, 0], [0, 0]]), > Matrix(IsBooleanMat, [[1, 1], [0, 1]])); -gap> LibsemigroupsCongruenceConstructor(S); -function( arg1, arg2 ) ... end gap> S := Semigroup( > Matrix(IsBooleanMat, [[0, 0, 0, 1, 1, 1, 0, 1, 0], [0, 0, 1, 1, 0, 0, 1, 1, 0], > [0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 0, 0, 1, 1, 0, 0, 0], [1, 0, 0, 0, 1, 1, 0, 1, 1], > [0, 0, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 0, 0, 0, 1, 0], [0, 1, 1, 0, 0, 0, 1, 0, 0], > [1, 0, 0, 1, 1, 1, 1, 0, 1]])); -gap> LibsemigroupsCongruenceConstructor(S); -function( arg1, arg2 ) ... end # LibsemigroupsCongruenceConstructor for other matrix over semiring gap> S := Semigroup(Matrix(IsMinPlusMatrix, [[-2, 2], [0, -1]]), > Matrix(IsMinPlusMatrix, [[0, 0], [1, -3]])); -gap> LibsemigroupsCongruenceConstructor(S); -function( arg1, arg2 ) ... end gap> S := Semigroup(Matrix(IsMaxPlusMatrix, [[-2, 2], [0, -1]]), > Matrix(IsMaxPlusMatrix, [[0, 0], [1, -3]])); -gap> LibsemigroupsCongruenceConstructor(S); -function( arg1, arg2 ) ... end # LibsemigroupsCongruenceConstructor for bipartition semigroup gap> S := PartitionMonoid(2); -gap> LibsemigroupsCongruenceConstructor(S); -function( arg1, arg2 ) ... end # LibsemigroupsCongruenceConstructor for pbr semigroup gap> S := FullPBRMonoid(1); -gap> LibsemigroupsCongruenceConstructor(S); -function( arg1, arg2 ) ... end # LibsemigroupsCongruence for a congruence on a semigroup with CanUseLibsemigroupsFroidurePin gap> S := FullBooleanMatMonoid(2); diff --git a/tst/testinstall.tst b/tst/testinstall.tst index a44d7cba5..bd31a9d64 100644 --- a/tst/testinstall.tst +++ b/tst/testinstall.tst @@ -1769,19 +1769,21 @@ gap> NonTrivialEquivalenceClasses(cong); [ ] # Issue 680 -gap> F := FreeSemigroup(2);; -gap> s1 := F.1;; s2 := F.2;; -gap> rels := [[s2 * s1 * s2, s2 * s1], [s1, s1], [s2, s2], -> [s1 * s2, s1 * s2], [s2 * s1, s2 * s1]];; -gap> cong := SemigroupCongruence(F, rels); -<2-sided semigroup congruence over with 1 generating pairs> -gap> NrEquivalenceClasses(cong); -infinity -gap> EquivalenceRelationPartitionWithSingletons(cong); -Error, the argument (a congruence) must have finite range -gap> EquivalenceRelationLookup(cong); -Error, the argument (a 2-sided congruence) must have finite range +# TODO: Uncomment when libsemigroups is updated to check for obviously infinite +# Congruences +# gap> F := FreeSemigroup(2);; +# gap> s1 := F.1;; s2 := F.2;; +# gap> rels := [[s2 * s1 * s2, s2 * s1], [s1, s1], [s2, s2], +# > [s1 * s2, s1 * s2], [s2 * s1, s2 * s1]];; +# gap> cong := SemigroupCongruence(F, rels); +# <2-sided semigroup congruence over with 1 generating pairs> +# gap> NrEquivalenceClasses(cong); +# infinity +# gap> EquivalenceRelationPartitionWithSingletons(cong); +# Error, the argument (a congruence) must have finite range +# gap> EquivalenceRelationLookup(cong); +# Error, the argument (a 2-sided congruence) must have finite range # Issue 788 gap> S := GLM(2, 2); From 086b2b0dab5855609e8bc616a29d10b0c9a48f6d Mon Sep 17 00:00:00 2001 From: Joseph Edwards Date: Tue, 2 Sep 2025 19:33:19 +0100 Subject: [PATCH 07/27] Refactor Congruence and Presentation --- gap/libsemigroups/cong.gi | 101 ++++++++++++++++++------------ gap/libsemigroups/sims1.gi | 6 +- src/pkg.cpp | 122 ++++++++++++++++++++----------------- 3 files changed, 129 insertions(+), 100 deletions(-) diff --git a/gap/libsemigroups/cong.gi b/gap/libsemigroups/cong.gi index 0b487df96..4f3905faa 100644 --- a/gap/libsemigroups/cong.gi +++ b/gap/libsemigroups/cong.gi @@ -87,34 +87,50 @@ function(C) S := Range(C); if IsFpSemigroup(S) or (HasIsFreeSemigroup(S) and IsFreeSemigroup(S)) or IsFpMonoid(S) or (HasIsFreeMonoid(S) and IsFreeMonoid(S)) then - make := libsemigroups.Congruence.make_from_fpsemigroup; - CC := make(CongruenceHandednessString(C), LibsemigroupsFpSemigroup(S)); + CC := libsemigroups.Congruence.make( + CongruenceHandednessString(C), + LibsemigroupsPresentation(S)); factor := Factorization; elif CanUseLibsemigroupsFroidurePin(S) then - CC := LibsemigroupsCongruenceConstructor(S)(CongruenceHandednessString(C), - LibsemigroupsFroidurePin(S)); - factor := MinimalFactorization; + ErrorNoReturn( + Concatenation( + "constructing LibsemigroupsCongruence from a LibsemigroupsFroidurePin ", + "is not yet implemented")); + # TODO: Implement when FroidurePin_to_Congruence function exists + # Something like this: + # fp := LibsemigroupsFroidurePin(S)); + # CC := libsemigroups.FroidurePin_to_Congruence( + # CongruenceHandednessString(C), + # fp, + # fp.right_cayley_graph); + # factor := MinimalFactorization; elif CanUseGapFroidurePin(S) then - N := Length(GeneratorsOfSemigroup(Range(C))); - tc := libsemigroups.ToddCoxeter.make(CongruenceHandednessString(C)); - libsemigroups.ToddCoxeter.set_number_of_generators(tc, N); - if IsRightMagmaCongruence(C) then - table := RightCayleyGraphSemigroup(Range(C)) - 1; - else - table := LeftCayleyGraphSemigroup(Range(C)) - 1; - fi; - libsemigroups.ToddCoxeter.prefill(tc, table); - CC := libsemigroups.Congruence.make_from_table( - CongruenceHandednessString(C), "none"); - libsemigroups.Congruence.set_number_of_generators(CC, N); - libsemigroups.Congruence.add_runner(CC, tc); - factor := MinimalFactorization; + ErrorNoReturn( + Concatenation( + "constructing LibsemigroupsCongruence from a GAP FroidurePin is not ", + "yet implemented")); + # TODO: Implement when ToddCoxeter_tox_FroidurePin and + # FroidurePin_to_Congruence are implemented. Something like: + # if IsRightMagmaCongruence(C) then + # cayley_digraph := RightCayleyDigraph(S); + # else + # cayley := LeftCayleyDigraph(S); + # fi; + # tc := libsemigroups.ToddCoxeter.make_from_wordgraph( + # CongruenceHandednessString(C), + # cayley_digraph); + # fp := libsemigroups.ToddCoexeter_to_FroidurePin(tc); + # CC := libsemigroups.FroidurePin_to_Congruence( + # CongruenceHandednessString(C), + # fp, + # fp.right_cayley_graph); + # factor := MinimalFactorization; else TryNextMethod(); fi; - add_pair := libsemigroups.Congruence.add_pair; + add_generating_pair := libsemigroups.Congruence.add_generating_pair; for pair in GeneratingPairsOfLeftRightOrTwoSidedCongruence(C) do - add_pair(CC, factor(S, pair[1]) - 1, factor(S, pair[2]) - 1); + add_generating_pair(CC, factor(S, pair[1]) - 1, factor(S, pair[2]) - 1); od; C!.LibsemigroupsCongruence := CC; return CC; @@ -235,16 +251,20 @@ function(C) local S, CC, ntc, gens, class, i, j; S := Range(C); if not IsFinite(S) or CanUseLibsemigroupsFroidurePin(S) then - CC := LibsemigroupsCongruence(C); - ntc := libsemigroups.Congruence.ntc(CC) + 1; - gens := GeneratorsOfSemigroup(S); - for i in [1 .. Length(ntc)] do - class := ntc[i]; - for j in [1 .. Length(class)] do - class[j] := EvaluateWord(gens, class[j]); - od; - od; - return ntc; + ErrorNoReturn( + Concatenation( + "computing the non-trivial classes of a Congruence is not yet ", + "implemented")); + # CC := LibsemigroupsCongruence(C); + # ntc := libsemigroups.Congruence.ntc(CC) + 1; + # gens := GeneratorsOfSemigroup(S); + # for i in [1 .. Length(ntc)] do + # class := ntc[i]; + # for j in [1 .. Length(class)] do + # class[j] := EvaluateWord(gens, class[j]); + # od; + # od; + # return ntc; elif CanUseGapFroidurePin(S) then # in this case libsemigroups.Congruence.ntc doesn't work, because S is not # represented in the libsemigroups object @@ -290,15 +310,16 @@ function(C) "number of classes"); fi; - result := EmptyPlist(NrEquivalenceClasses(C)); - CC := LibsemigroupsCongruence(C); - gens := GeneratorsOfSemigroup(Range(C)); - class_index_to_word := libsemigroups.Congruence.class_index_to_word; - for i in [1 .. NrEquivalenceClasses(C)] do - rep := EvaluateWord(gens, class_index_to_word(CC, i - 1) + 1); - result[i] := EquivalenceClassOfElementNC(C, rep); - od; - return result; + ErrorNoReturn("libsemigroups.Congruence has no member 'class_index_to_word'"); + # result := EmptyPlist(NrEquivalenceClasses(C)); + # CC := LibsemigroupsCongruence(C); + # gens := GeneratorsOfSemigroup(Range(C)); + # class_index_to_word := libsemigroups.Congruence.class_index_to_word; + # for i in [1 .. NrEquivalenceClasses(C)] do + # rep := EvaluateWord(gens, class_index_to_word(CC, i - 1) + 1); + # result[i] := EquivalenceClassOfElementNC(C, rep); + # od; + # return result; end); ########################################################################### diff --git a/gap/libsemigroups/sims1.gi b/gap/libsemigroups/sims1.gi index bc235a81f..57ed12f99 100644 --- a/gap/libsemigroups/sims1.gi +++ b/gap/libsemigroups/sims1.gi @@ -51,10 +51,10 @@ function(S, n, extra, kind) if not IsEmpty(rules) then libsemigroups.Presentation.alphabet_from_rules(P); elif (HasIsFreeMonoid(S) and IsFreeMonoid(S)) or IsFpMonoid(S) then - libsemigroups.Presentation.set_alphabet( + libsemigroups.Presentation.alphabet( P, [0 .. Size(GeneratorsOfMonoid(S)) - 1]); elif (HasIsFreeSemigroup(S) and IsFreeSemigroup(S)) or IsFpSemigroup(S) then - libsemigroups.Presentation.set_alphabet( + libsemigroups.Presentation.alphabet( P, [0 .. Size(GeneratorsOfSemigroup(S)) - 1]); fi; libsemigroups.Presentation.validate(P); @@ -68,7 +68,7 @@ function(S, n, extra, kind) if not IsEmpty(extra) then Q := libsemigroups.Presentation.make(); libsemigroups.Presentation.contains_empty_word(Q, IsMonoid(S)); - libsemigroups.Presentation.set_alphabet(Q, + libsemigroups.Presentation.alphabet(Q, libsemigroups.Presentation.alphabet(P)); for pair in extra do diff --git a/src/pkg.cpp b/src/pkg.cpp index 1530fe2cb..5392a5569 100644 --- a/src/pkg.cpp +++ b/src/pkg.cpp @@ -52,18 +52,31 @@ // libsemigroups headers #include "libsemigroups/adapters.hpp" -#include "libsemigroups/bipart.hpp" // for Blocks, Bipartition -#include "libsemigroups/freeband.hpp" // for freeband_equal_to -#include "libsemigroups/presentation.hpp" // for Presentation -#include "libsemigroups/sims.hpp" // for Sims1 -#include "libsemigroups/todd-coxeter.hpp" // for ToddCoxeter, ToddCoxeter::word_graph_type -#include "libsemigroups/types.hpp" // for word_type, letter_type -#include "libsemigroups/word-graph.hpp" // for WordGraph +#include "libsemigroups/bipart.hpp" // for Blocks, Bipartition +#include "libsemigroups/cong-class.hpp" // for Congruence +#include "libsemigroups/freeband.hpp" // for freeband_equal_to +#include "libsemigroups/froidure-pin-base.hpp" // for FroidurePin +#include "libsemigroups/presentation.hpp" // for Presentation +#include "libsemigroups/sims.hpp" // for Sims1 +#include "libsemigroups/to-cong.hpp" // for to +#include "libsemigroups/todd-coxeter.hpp" // for ToddCoxeter, +#include "libsemigroups/types.hpp" // for word_type, letter_type +#include "libsemigroups/word-graph.hpp" // for WordGraph #include "libsemigroups/detail/report.hpp" // for REPORTER, Reporter using libsemigroups::Bipartition; using libsemigroups::Blocks; +using libsemigroups::Congruence; +using libsemigroups::congruence_kind; +using libsemigroups::FroidurePin; +using libsemigroups::FroidurePinBase; +using libsemigroups::Presentation; +using libsemigroups::RepOrc; +using libsemigroups::Sims1; +using libsemigroups::ToddCoxeter; +using libsemigroups::word_type; +using libsemigroups::WordGraph; namespace { void LIBSEMIGROUPS_REPORTING_ENABLED(bool const val) { @@ -74,22 +87,19 @@ namespace { namespace gapbind14 { template <> - struct IsGapBind14Type> - : std::true_type {}; + struct IsGapBind14Type> : std::true_type {}; template <> - struct IsGapBind14Type> - : std::true_type {}; + struct IsGapBind14Type> : std::true_type {}; template <> - struct IsGapBind14Type : std::true_type {}; + struct IsGapBind14Type : std::true_type {}; template <> - struct IsGapBind14Type - : std::true_type {}; + struct IsGapBind14Type : std::true_type {}; template <> - struct IsGapBind14Type : std::true_type {}; + struct IsGapBind14Type : std::true_type {}; } // namespace gapbind14 GAPBIND14_MODULE(libsemigroups) { @@ -105,13 +115,14 @@ GAPBIND14_MODULE(libsemigroups) { &std::thread::hardware_concurrency); gapbind14::InstallGlobalFunction( "freeband_equal_to", - gapbind14::overload_cast( - &libsemigroups::freeband_equal_to)); + gapbind14::overload_cast( + &libsemigroups::freeband_equal_to)); gapbind14::InstallGlobalFunction("LATTICE_OF_CONGRUENCES", &semigroups::LATTICE_OF_CONGRUENCES); + // TODO: Add the to<> functions + //////////////////////////////////////////////////////////////////////// // Initialise from other cpp files //////////////////////////////////////////////////////////////////////// @@ -131,19 +142,17 @@ GAPBIND14_MODULE(libsemigroups) { // ToddCoxeter //////////////////////////////////////////////////////////////////////// - using libsemigroups::word_type; - - using libsemigroups::congruence_kind; - using libsemigroups::Presentation; - using libsemigroups::ToddCoxeter; - - using word_graph_type - = libsemigroups::ToddCoxeter::word_graph_type; + using word_graph_type = ToddCoxeter::word_graph_type; gapbind14::class_>("ToddCoxeter") - .def(gapbind14::init>{}); + .def(gapbind14::init>{}, + "make_from_presentation") + .def(gapbind14::init>{}, + "make_from_wordgraph"); - using libsemigroups::Presentation; + //////////////////////////////////////////////////////////////////////// + // Presentation + //////////////////////////////////////////////////////////////////////// gapbind14::class_>("Presentation") .def(gapbind14::init<>{}, "make") @@ -153,6 +162,10 @@ GAPBIND14_MODULE(libsemigroups) { [](Presentation& thing, word_type val) -> void { thing.alphabet(val); }) + .def("set_alphabet_size", + [](Presentation& thing, size_t size) -> void { + thing.alphabet(size); + }) .def("alphabet_from_rules", [](Presentation& thing) -> void { thing.alphabet_from_rules(); @@ -175,7 +188,9 @@ GAPBIND14_MODULE(libsemigroups) { word_type const&>( &libsemigroups::presentation::add_rule)); - using libsemigroups::Sims1; + //////////////////////////////////////////////////////////////////////// + // Sims + //////////////////////////////////////////////////////////////////////// gapbind14::class_("Sims1Iterator") .def("increment", [](typename Sims1::iterator& it) { ++it; }) @@ -188,8 +203,6 @@ GAPBIND14_MODULE(libsemigroups) { .def("number_of_congruences", &Sims1::number_of_congruences) .def("cbegin", &Sims1::cbegin); - using libsemigroups::RepOrc; - gapbind14::class_("RepOrc") .def(gapbind14::init<>{}, "make") .def("number_of_threads", @@ -199,34 +212,29 @@ GAPBIND14_MODULE(libsemigroups) { .def("target_size", [](RepOrc& ro, size_t val) { ro.target_size(val); }) .def("word_graph", &RepOrc::word_graph); - using libsemigroups::Congruence; - using libsemigroups::congruence_kind; - using libsemigroups::FroidurePinBase; - using libsemigroups::Presentation; - using libsemigroups::word_type; + //////////////////////////////////////////////////////////////////////// + // Congruence + //////////////////////////////////////////////////////////////////////// gapbind14::class_>("Congruence") - .def(gapbind14::init>{}, "make"); - // .def("number_of_generating_pairs", - // &Congruence::number_of_generating_pairs) - // .def("add_generating_pair", - // [](Congruence& self, - // word_type const& u, - // word_type const& v) { - // return libsemigroups::congruence::add_generating_pair(self, u, v); - // }) - // .def("number_of_classes", &Congruence::number_of_classes) - // // .def("index_of", &Congruence::word_to_class_index) - // // .def("word_of", &Congruence::class_index_to_word) - // .def("contains", - // [](Congruence& self, - // word_type const& u, - // word_type const& v) { - // return libsemigroups::congruence::contains(self, u, v); - // }); - // .def("non_trivial_classes", [](Congruence& C) { - // return gapbind14::make_iterator(C.cbegin_ntc(), C.cend_ntc()); - // }); + .def(gapbind14::init>{}, "make") + .def("number_of_generating_pairs", + &Congruence::number_of_generating_pairs) + .def("add_generating_pair", + [](Congruence& self, + word_type const& u, + word_type const& v) { + return libsemigroups::congruence::add_generating_pair(self, u, v); + }) + .def("number_of_classes", &Congruence::number_of_classes) + // .def("index_of", &Congruence::word_to_class_index) + // .def("word_of", &Congruence::class_index_to_word) + .def("contains", + [](Congruence& self, + word_type const& u, + word_type const& v) { + return libsemigroups::congruence::contains(self, u, v); + }); } //////////////////////////////////////////////////////////////////////// From 43a7332542ec870d39c6f7c7a12c75e87e985707 Mon Sep 17 00:00:00 2001 From: James Mitchell Date: Mon, 8 Dec 2025 09:03:17 +0000 Subject: [PATCH 08/27] Fixes --- .gitignore | 3 +++ gap/libsemigroups/froidure-pin.gi | 10 +++++----- gap/libsemigroups/presentation.gi | 2 +- src/bipart.cpp | 22 ++++++++++------------ src/pkg.cpp | 21 +++++++++++++++------ tst/standard/attributes/acting.tst | 2 +- 6 files changed, 35 insertions(+), 25 deletions(-) diff --git a/.gitignore b/.gitignore index dcdb2183d..963f04f07 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,6 @@ /src/pkgconfig.h.in /src/semigroups-config.hpp test-output.w +tags +*.in~ +configure~ diff --git a/gap/libsemigroups/froidure-pin.gi b/gap/libsemigroups/froidure-pin.gi index b6e25a855..57588cb49 100644 --- a/gap/libsemigroups/froidure-pin.gi +++ b/gap/libsemigroups/froidure-pin.gi @@ -211,7 +211,7 @@ function(S) return S!.LibsemigroupsFroidurePin; elif IsFpSemigroup(S) or IsFpMonoid(S) then C := LibsemigroupsCongruence(UnderlyingCongruence(S)); - return libsemigroups.Congruence.quotient_froidure_pin(C); + return libsemigroups.to_froidure_pin(C); elif IsQuotientSemigroup(S) then C := QuotientSemigroupCongruence(S); if not HasGeneratingPairsOfMagmaCongruence(C) then @@ -333,7 +333,7 @@ function(S, x) record := FroidurePinMemFnRec(S); word := _GetElement(S, x); pos := record.current_position(T, word); - while pos < 0 do + while pos = 4294967295 do record.enumerate(T, record.current_size(T) + 1); pos := record.current_position(T, word); od; @@ -345,7 +345,7 @@ function(S, x) fi; pos := FroidurePinMemFnRec(S).position(LibsemigroupsFroidurePin(S), _GetElement(S, x)); - if pos < 0 then + if pos = 4294967295 then return fail; fi; return pos + 1; @@ -378,7 +378,7 @@ function(S, x, _) pos := FroidurePinMemFnRec(S).current_position(LibsemigroupsFroidurePin(S), _GetElement(S, x)); - if pos < 0 then + if pos = 4294967295 then return fail; else return pos + 1; @@ -407,7 +407,7 @@ function(S, x) fi; pos := FroidurePinMemFnRec(S).sorted_position(LibsemigroupsFroidurePin(S), _GetElement(S, x)); - if pos < 0 then + if pos = 4294967295 then return fail; else return pos + 1; diff --git a/gap/libsemigroups/presentation.gi b/gap/libsemigroups/presentation.gi index 1708c6ebf..1e641b442 100644 --- a/gap/libsemigroups/presentation.gi +++ b/gap/libsemigroups/presentation.gi @@ -38,7 +38,7 @@ function(S) # The identity must be 0 so that this corresponds to what happens in # FroidurePin, where GeneratorsOfSemigroup(S) is used and the identity is # the first entry. - libsemigroups.Presentation.set_identity(SS, 0); + libsemigroups.presentation_add_identity_rules(SS, 0); R := RelationsOfFpMonoid(S); else R := RelationsOfFpSemigroup(S); diff --git a/src/bipart.cpp b/src/bipart.cpp index 3d15e5d08..79f113b32 100644 --- a/src/bipart.cpp +++ b/src/bipart.cpp @@ -21,14 +21,12 @@ #include "bipart.hpp" -#include // for fill, min, max, all_of, max_element -#include // for size_t, NULL -#include // for uint32_t -#include // for string -#include // for thread -#include // for conditional<>::type -#include // for pair, make_pair -#include // for vector +#include // for fill, min, max, all_of, max_element +#include // for size_t, NULL +#include // for uint32_t +#include // for thread +#include // for pair, make_pair +#include // for vector // GAP headers #include "gap_all.h" @@ -714,7 +712,7 @@ Obj BLOCKS_NC(Obj self, Obj gap_blocks) { } } #ifdef SEMIGROUPS_KERNEL_DEBUG - libsemigroups::validate(*blocks); + libsemigroups::blocks::throw_if_invalid(*blocks); #endif return blocks_new_obj(blocks); } @@ -1004,7 +1002,7 @@ Obj BLOCKS_LEFT_ACT(Obj self, Obj blocks_gap, Obj x_gap) { } #ifdef SEMIGROUPS_KERNEL_DEBUG - libsemigroups::validate(*out_blocks); + libsemigroups::blocks::throw_if_invalid(*out_blocks); #endif return blocks_new_obj(out_blocks); @@ -1058,7 +1056,7 @@ Obj BLOCKS_RIGHT_ACT(Obj self, Obj blocks_gap, Obj x_gap) { out_blocks->is_transverse_block(tab[j], _BUFFER_bool[j]); } #ifdef SEMIGROUPS_KERNEL_DEBUG - libsemigroups::validate(*out_blocks); + libsemigroups::blocks::throw_if_invalid(*out_blocks); #endif return blocks_new_obj(out_blocks); } @@ -1413,7 +1411,7 @@ class IdempotentCounter { _threads[i].join(); } - libsemigroups::report_elapsed_time("", timer); + // libsemigroups::report_elapsed_time("", timer); size_t max = *max_element(_ranks.begin(), _ranks.end()) + 1; std::vector out = std::vector(max, 0); diff --git a/src/pkg.cpp b/src/pkg.cpp index 5392a5569..a1d5f153b 100644 --- a/src/pkg.cpp +++ b/src/pkg.cpp @@ -59,6 +59,7 @@ #include "libsemigroups/presentation.hpp" // for Presentation #include "libsemigroups/sims.hpp" // for Sims1 #include "libsemigroups/to-cong.hpp" // for to +#include "libsemigroups/to-froidure-pin.hpp" // for to #include "libsemigroups/todd-coxeter.hpp" // for ToddCoxeter, #include "libsemigroups/types.hpp" // for word_type, letter_type #include "libsemigroups/word-graph.hpp" // for WordGraph @@ -121,6 +122,11 @@ GAPBIND14_MODULE(libsemigroups) { gapbind14::InstallGlobalFunction("LATTICE_OF_CONGRUENCES", &semigroups::LATTICE_OF_CONGRUENCES); + gapbind14::InstallGlobalFunction("congruence_to_froidure_pin", + [](Congruence& c) { + return libsemigroups::to(c); + }); + // TODO: Add the to<> functions //////////////////////////////////////////////////////////////////////// @@ -157,7 +163,7 @@ GAPBIND14_MODULE(libsemigroups) { gapbind14::class_>("Presentation") .def(gapbind14::init<>{}, "make") .def("alphabet", - gapbind14::overload_cast<>(&Presentation::alphabet)) + [](Presentation& thing) { return thing.alphabet(); }) .def("set_alphabet", [](Presentation& thing, word_type val) -> void { thing.alphabet(val); @@ -188,6 +194,11 @@ GAPBIND14_MODULE(libsemigroups) { word_type const&>( &libsemigroups::presentation::add_rule)); + gapbind14::InstallGlobalFunction( + "presentation_add_identity_rules", + gapbind14::overload_cast&, size_t>( + &libsemigroups::presentation::add_identity_rules)); + //////////////////////////////////////////////////////////////////////// // Sims //////////////////////////////////////////////////////////////////////// @@ -339,7 +350,7 @@ void TBlocksObjLoadFunc(Obj o) { blocks->is_transverse_block(i, static_cast(LoadUInt1())); } #ifdef SEMIGROUPS_KERNEL_DEBUG - libsemigroups::validate(*blocks); + libsemigroups::blocks::throw_if_invalid(*blocks); #endif ADDR_OBJ(o)[0] = reinterpret_cast(blocks); } @@ -438,10 +449,8 @@ static StructGVarFilt GVarFilts[] = { /*****************************************************************************/ -#define GVAR_ENTRY(srcfile, name, nparam, params) \ - { \ -#name, nparam, params, (ObjFunc) name, srcfile ":Func" #name \ - } +#define GVAR_ENTRY(srcfile, name, nparam, params) \ + {#name, nparam, params, (ObjFunc) name, srcfile ":Func" #name} // Table of functions to export diff --git a/tst/standard/attributes/acting.tst b/tst/standard/attributes/acting.tst index f68508efd..84c9fbd5f 100644 --- a/tst/standard/attributes/acting.tst +++ b/tst/standard/attributes/acting.tst @@ -82,7 +82,7 @@ gap> IdempotentGeneratedSubsemigroup(S); gap> S := InverseSemigroup([PartialPerm([1, 2], [4, 3]), > PartialPerm([1, 2, 5], [1, 2, 4])]);; gap> IdempotentGeneratedSubsemigroup(S); - + # InjectionPrincipalFactor 1/6 gap> D := GreensDClassOfElement( From 285eceb3d6ba4b4609e284116e7f835755f355cd Mon Sep 17 00:00:00 2001 From: James Mitchell Date: Mon, 8 Dec 2025 09:33:34 +0000 Subject: [PATCH 09/27] tst/standard/attributes/attr.tst passes --- gap/libsemigroups/froidure-pin.gi | 4 ++-- src/froidure-pin-base.cpp | 2 +- src/froidure-pin.hpp | 2 +- src/pkg.cpp | 12 ++++++++---- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/gap/libsemigroups/froidure-pin.gi b/gap/libsemigroups/froidure-pin.gi index 57588cb49..b53f588e2 100644 --- a/gap/libsemigroups/froidure-pin.gi +++ b/gap/libsemigroups/froidure-pin.gi @@ -211,14 +211,14 @@ function(S) return S!.LibsemigroupsFroidurePin; elif IsFpSemigroup(S) or IsFpMonoid(S) then C := LibsemigroupsCongruence(UnderlyingCongruence(S)); - return libsemigroups.to_froidure_pin(C); + return libsemigroups.congruence_to_froidure_pin(C); elif IsQuotientSemigroup(S) then C := QuotientSemigroupCongruence(S); if not HasGeneratingPairsOfMagmaCongruence(C) then GeneratingPairsOfMagmaCongruence(C); fi; C := LibsemigroupsCongruence(C); - return libsemigroups.Congruence.quotient_froidure_pin(C); + return libsemigroups.congruence_to_froidure_pin(C); fi; Unbind(S!.LibsemigroupsFroidurePin); record := FroidurePinMemFnRec(S); diff --git a/src/froidure-pin-base.cpp b/src/froidure-pin-base.cpp index 82b254e24..2f3d39956 100644 --- a/src/froidure-pin-base.cpp +++ b/src/froidure-pin-base.cpp @@ -31,7 +31,7 @@ namespace gapbind14 { } void init_froidure_pin_base(gapbind14::Module& m) { - using FroidurePin_ = std::shared_ptr; + using FroidurePin_ = libsemigroups::FroidurePinBase*; gapbind14::class_("FroidurePinBase") .def("enumerate", [](FroidurePin_ S, size_t limit) { S->enumerate(limit); }) diff --git a/src/froidure-pin.hpp b/src/froidure-pin.hpp index 5d94af043..499fe6c98 100644 --- a/src/froidure-pin.hpp +++ b/src/froidure-pin.hpp @@ -38,7 +38,7 @@ namespace gapbind14 { struct IsGapBind14Type> : std::true_type {}; template <> - struct IsGapBind14Type : std::true_type {}; + struct IsGapBind14Type : std::true_type {}; } // namespace gapbind14 diff --git a/src/pkg.cpp b/src/pkg.cpp index a1d5f153b..209cb96d7 100644 --- a/src/pkg.cpp +++ b/src/pkg.cpp @@ -122,10 +122,14 @@ GAPBIND14_MODULE(libsemigroups) { gapbind14::InstallGlobalFunction("LATTICE_OF_CONGRUENCES", &semigroups::LATTICE_OF_CONGRUENCES); - gapbind14::InstallGlobalFunction("congruence_to_froidure_pin", - [](Congruence& c) { - return libsemigroups::to(c); - }); + gapbind14::InstallGlobalFunction( + "congruence_to_froidure_pin", [](Congruence& c) { + // to for a Congruence returns a std::unique_ptr, + // which we have trouble dealing with in gapbind14, so we instead + // just get the raw pointer, and get the std::unique_ptr to release + // its ownership. + return libsemigroups::to(c).release(); + }); // TODO: Add the to<> functions From c6946f2680670e8e7cb4c2e3cebdaf7ab93ba72d Mon Sep 17 00:00:00 2001 From: James Mitchell Date: Mon, 8 Dec 2025 10:40:47 +0000 Subject: [PATCH 10/27] Working on attributes/homomorph.tst --- gap/libsemigroups/cong.gi | 3 ++- src/pkg.cpp | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/gap/libsemigroups/cong.gi b/gap/libsemigroups/cong.gi index 4f3905faa..bb65afe86 100644 --- a/gap/libsemigroups/cong.gi +++ b/gap/libsemigroups/cong.gi @@ -188,7 +188,8 @@ function(C, elm1, elm2) Assert(0, false); fi; CC := LibsemigroupsCongruence(C); - return libsemigroups.Congruence.less(CC, word1 - 1, word2 - 1); + return libsemigroups.Congruence.reduce(CC, word1 - 1) + < libsemigroups.Congruence.reduce(CC, word2 - 1); end); ########################################################################### diff --git a/src/pkg.cpp b/src/pkg.cpp index 209cb96d7..1293814f0 100644 --- a/src/pkg.cpp +++ b/src/pkg.cpp @@ -249,7 +249,10 @@ GAPBIND14_MODULE(libsemigroups) { word_type const& u, word_type const& v) { return libsemigroups::congruence::contains(self, u, v); - }); + }) + .def("reduce", [](Congruence& self, word_type const& u) { + return libsemigroups::congruence::reduce(self, u); + }); } //////////////////////////////////////////////////////////////////////// From 9b099db830bcb23c81d8043fae24fe71baa074dc Mon Sep 17 00:00:00 2001 From: James Mitchell Date: Thu, 18 Dec 2025 11:31:22 +0100 Subject: [PATCH 11/27] standard/attributes/homomorph.tst passes --- gap/congruences/conglatt.gi | 4 +- gap/libsemigroups/cong.gd | 6 ++ gap/libsemigroups/cong.gi | 110 +++++++++++++------------- gap/libsemigroups/froidure-pin.gd | 4 + gap/libsemigroups/froidure-pin.gi | 32 ++++++-- src/froidure-pin-base.cpp | 2 +- src/froidure-pin.hpp | 2 +- src/pkg.cpp | 29 ++++++- src/to_gap.hpp | 12 +-- tst/standard/attributes/acting.tst | 2 +- tst/standard/attributes/homomorph.tst | 5 +- tst/standard/libsemigroups/cong.tst | 21 ++--- 12 files changed, 139 insertions(+), 90 deletions(-) diff --git a/gap/congruences/conglatt.gi b/gap/congruences/conglatt.gi index 2d6ae7545..7641b08b9 100644 --- a/gap/congruences/conglatt.gi +++ b/gap/congruences/conglatt.gi @@ -68,7 +68,7 @@ SEMIGROUPS.PrincipalXCongruencesNC := keep := true; newcong := SemigroupXCongruence(S, [new_pair]); m := NrEquivalenceClasses(newcong); - newcongdiscrim := List(words, w -> CongruenceWordToClassIndex(newcong, w)); + newcongdiscrim := List(words, w -> CongruenceReduce(newcong, w)); if not IsBound(congs[m]) then congs[m] := [newcong]; congs_discrim[m] := [newcongdiscrim]; @@ -244,7 +244,7 @@ function(S, gen_congs, WrappedXCongruence) all_congs := List(AsListCanonical(S), x -> x![1]); else # The default S := List(gen_congs, EquivalenceRelationLookup); - old_value := libsemigroups.should_report(); + old_value := libsemigroups.reporting_enabled(); if InfoLevel(InfoSemigroups) = 4 then libsemigroups.set_report(true); fi; diff --git a/gap/libsemigroups/cong.gd b/gap/libsemigroups/cong.gd index 8b4a4cd0f..35919cd58 100644 --- a/gap/libsemigroups/cong.gd +++ b/gap/libsemigroups/cong.gd @@ -22,3 +22,9 @@ DeclareOperation("CongruenceLessNC", [CanUseLibsemigroupsCongruence, IsMultiplicativeElement, IsMultiplicativeElement]); + +DeclareOperation("CongruenceReduce", + [CanUseLibsemigroupsCongruence, IsHomogeneousList]); +DeclareOperation("CongruenceReduce", + [CanUseLibsemigroupsCongruence, IsMultiplicativeElement]); + diff --git a/gap/libsemigroups/cong.gi b/gap/libsemigroups/cong.gi index bb65afe86..ab3a945eb 100644 --- a/gap/libsemigroups/cong.gi +++ b/gap/libsemigroups/cong.gi @@ -73,7 +73,7 @@ InstallTrueMethod(CanUseLibsemigroupsCongruence, BindGlobal("LibsemigroupsCongruence", function(C) - local S, make, CC, factor, N, tc, cayley_digraph, add_generating_pair, pair; + local S, CC, factor, fp, add_generating_pair, pair; Assert(1, CanUseLibsemigroupsCongruence(C)); @@ -92,18 +92,15 @@ function(C) LibsemigroupsPresentation(S)); factor := Factorization; elif CanUseLibsemigroupsFroidurePin(S) then - ErrorNoReturn( - Concatenation( - "constructing LibsemigroupsCongruence from a LibsemigroupsFroidurePin ", - "is not yet implemented")); - # TODO: Implement when FroidurePin_to_Congruence function exists - # Something like this: - # fp := LibsemigroupsFroidurePin(S)); - # CC := libsemigroups.FroidurePin_to_Congruence( - # CongruenceHandednessString(C), - # fp, - # fp.right_cayley_graph); - # factor := MinimalFactorization; + fp := LibsemigroupsFroidurePin(S); + if CongruenceHandednessString(C) = "left" then + CC := libsemigroups.froidure_pin_to_left_congruence(fp); + elif CongruenceHandednessString(C) = "right" then + CC := libsemigroups.froidure_pin_to_right_congruence(fp); + else + CC := libsemigroups.froidure_pin_to_2_sided_congruence(fp); + fi; + factor := MinimalFactorization; elif CanUseGapFroidurePin(S) then ErrorNoReturn( Concatenation( @@ -138,34 +135,13 @@ end); ######################################################################## -DeclareOperation("CongruenceWordToClassIndex", - [CanUseLibsemigroupsCongruence, IsHomogeneousList]); -DeclareOperation("CongruenceWordToClassIndex", - [CanUseLibsemigroupsCongruence, IsMultiplicativeElement]); - -InstallMethod(CongruenceWordToClassIndex, -"for CanUseLibsemigroupsCongruence and hom. list", -[CanUseLibsemigroupsCongruence, IsHomogeneousList], -function(C, word) - local CC; - CC := LibsemigroupsCongruence(C); - return libsemigroups.Congruence.word_to_class_index(CC, word - 1) + 1; -end); - -InstallMethod(CongruenceWordToClassIndex, -"for CanUseLibsemigroupsCongruence and hom. list", -[CanUseLibsemigroupsCongruence, IsMultiplicativeElement], -{C, x} -> CongruenceWordToClassIndex(C, MinimalFactorization(Range(C), x))); - -######################################################################## - InstallMethod(CongruenceLessNC, "for CanUseLibsemigroupsCongruence and two mult. elements", [CanUseLibsemigroupsCongruence, IsMultiplicativeElement, IsMultiplicativeElement], function(C, elm1, elm2) - local S, pos1, pos2, lookup, word1, word2, CC; + local S, pos1, pos2, lookup, word1, word2; S := Range(C); if CanUseFroidurePin(S) then @@ -187,11 +163,25 @@ function(C, elm1, elm2) # Cannot currently test the next line Assert(0, false); fi; + return CongruenceReduce(C, word1) < CongruenceReduce(C, word2); +end); + +######################################################################## + +InstallMethod(CongruenceReduce, +"for CanUseLibsemigroupsCongruence and hom. list", +[CanUseLibsemigroupsCongruence, IsHomogeneousList], +function(C, word) + local CC; CC := LibsemigroupsCongruence(C); - return libsemigroups.Congruence.reduce(CC, word1 - 1) - < libsemigroups.Congruence.reduce(CC, word2 - 1); + return libsemigroups.Congruence.reduce(CC, word - 1) + 1; end); +InstallMethod(CongruenceReduce, +"for CanUseLibsemigroupsCongruence and mult. elt.", +[CanUseLibsemigroupsCongruence, IsMultiplicativeElement], +{C, x} -> CongruenceReduce(C, MinimalFactorization(Range(C), x))); + ########################################################################### # Functions/methods that are declared elsewhere and that use the # libsemigroups object directly @@ -249,23 +239,22 @@ InstallMethod(EquivalenceRelationPartition, [CanUseLibsemigroupsCongruence and HasGeneratingPairsOfLeftRightOrTwoSidedCongruence], function(C) - local S, CC, ntc, gens, class, i, j; + local S, CC, words, ntc, gens, class, i, j; + S := Range(C); if not IsFinite(S) or CanUseLibsemigroupsFroidurePin(S) then - ErrorNoReturn( - Concatenation( - "computing the non-trivial classes of a Congruence is not yet ", - "implemented")); - # CC := LibsemigroupsCongruence(C); - # ntc := libsemigroups.Congruence.ntc(CC) + 1; - # gens := GeneratorsOfSemigroup(S); - # for i in [1 .. Length(ntc)] do - # class := ntc[i]; - # for j in [1 .. Length(class)] do - # class[j] := EvaluateWord(gens, class[j]); - # od; - # od; - # return ntc; + # TODO this won't work when S is infinite! + CC := LibsemigroupsCongruence(C); + words := List(S, x -> Factorization(S, x)) - 1; + ntc := libsemigroups.congruence_non_trivial_classes(CC, words) + 1; + gens := GeneratorsOfSemigroup(S); + for i in [1 .. Length(ntc)] do + class := ntc[i]; + for j in [1 .. Length(class)] do + class[j] := EvaluateWord(gens, class[j]); + od; + od; + return ntc; elif CanUseGapFroidurePin(S) then # in this case libsemigroups.Congruence.ntc doesn't work, because S is not # represented in the libsemigroups object @@ -295,8 +284,7 @@ function(class1, class2) word1 := Factorization(Range(C), Representative(class1)); word2 := Factorization(Range(C), Representative(class2)); - CC := LibsemigroupsCongruence(C); - return libsemigroups.Congruence.less(CC, word1 - 1, word2 - 1); + return CongruenceReduce(C, word1) < CongruenceReduce(C, word2); end); InstallMethod(EquivalenceClasses, @@ -333,15 +321,23 @@ InstallMethod(EquivalenceRelationPartitionWithSingletons, [CanUseLibsemigroupsCongruence and HasGeneratingPairsOfLeftRightOrTwoSidedCongruence], function(C) - local part, word, i, x; + local map, next, part, CC, word, i, x; + if not IsFinite(Range(C)) then ErrorNoReturn("the argument (a congruence) must have finite range"); fi; + map := HashMap(); + next := 1; part := []; + CC := LibsemigroupsCongruence(C); for x in Range(C) do - word := MinimalFactorization(Range(C), x); - i := CongruenceWordToClassIndex(C, word); + word := CongruenceReduce(C, x); + if not word in map then + map[word] := next; + next := next + 1; + fi; + i := map[word]; if not IsBound(part[i]) then part[i] := []; fi; diff --git a/gap/libsemigroups/froidure-pin.gd b/gap/libsemigroups/froidure-pin.gd index 26858aa41..c27312144 100644 --- a/gap/libsemigroups/froidure-pin.gd +++ b/gap/libsemigroups/froidure-pin.gd @@ -15,3 +15,7 @@ DeclareGlobalFunction("LibsemigroupsFroidurePin"); DeclareProperty("CanUseLibsemigroupsFroidurePin", IsSemigroup); DeclareOperation("HasLibsemigroupsFroidurePin", [IsSemigroup]); + +DeclareOperation("FroidurePinMemFnRec", [IsSemigroup]); +DeclareOperation("FroidurePinMemFnRec", + [IsSemigroup, IsListOrCollection]); diff --git a/gap/libsemigroups/froidure-pin.gi b/gap/libsemigroups/froidure-pin.gi index b53f588e2..772cd8e70 100644 --- a/gap/libsemigroups/froidure-pin.gi +++ b/gap/libsemigroups/froidure-pin.gi @@ -47,10 +47,6 @@ Q -> CanUseLibsemigroupsCongruence(QuotientSemigroupCongruence(Q))); ## Function for getting the correct record from the `libsemigroups` record. ########################################################################### -DeclareOperation("FroidurePinMemFnRec", [IsSemigroup]); -DeclareOperation("FroidurePinMemFnRec", - [IsSemigroup, IsListOrCollection]); - InstallMethod(FroidurePinMemFnRec, "for a semigroup", [IsSemigroup], S -> FroidurePinMemFnRec(S, [])); @@ -161,6 +157,12 @@ InstallMethod(FroidurePinMemFnRec, "for an fp semigroup", InstallMethod(FroidurePinMemFnRec, "for an fp monoid", [IsFpMonoid], S -> libsemigroups.FroidurePinBase); +InstallMethod(FroidurePinMemFnRec, "for a free semigroup", +[IsFreeSemigroup], S -> libsemigroups.FroidurePinBase); + +InstallMethod(FroidurePinMemFnRec, "for a free monoid", +[IsFreeMonoid], S -> libsemigroups.FroidurePinBase); + InstallMethod(FroidurePinMemFnRec, "for quotient semigroup", [IsQuotientSemigroup], S -> libsemigroups.FroidurePinBase); @@ -211,14 +213,18 @@ function(S) return S!.LibsemigroupsFroidurePin; elif IsFpSemigroup(S) or IsFpMonoid(S) then C := LibsemigroupsCongruence(UnderlyingCongruence(S)); - return libsemigroups.congruence_to_froidure_pin(C); + T := libsemigroups.congruence_to_froidure_pin(C); + S!.LibsemigroupsFroidurePin := T; + return T; elif IsQuotientSemigroup(S) then C := QuotientSemigroupCongruence(S); if not HasGeneratingPairsOfMagmaCongruence(C) then GeneratingPairsOfMagmaCongruence(C); fi; C := LibsemigroupsCongruence(C); - return libsemigroups.congruence_to_froidure_pin(C); + T := libsemigroups.congruence_to_froidure_pin(C); + S!.LibsemigroupsFroidurePin := T; + return T; fi; Unbind(S!.LibsemigroupsFroidurePin); record := FroidurePinMemFnRec(S); @@ -340,8 +346,18 @@ function(S, x) return pos + 1; elif IsQuotientSemigroup(S) then T := QuotientSemigroupPreimage(S); - C := QuotientSemigroupCongruence(S); - return CongruenceWordToClassIndex(C, Factorization(T, Representative(x))); + word := Factorization(T, Representative(x)); + record := FroidurePinMemFnRec(S); + T := LibsemigroupsFroidurePin(S); + pos := record.current_position(T, word - 1); + while pos = 4294967295 and not record.finished(T) do + record.enumerate(T, record.current_size(T) + 1); + pos := record.current_position(T, word - 1); + od; + if pos = 4294967295 then + return fail; + fi; + return pos + 1; fi; pos := FroidurePinMemFnRec(S).position(LibsemigroupsFroidurePin(S), _GetElement(S, x)); diff --git a/src/froidure-pin-base.cpp b/src/froidure-pin-base.cpp index 2f3d39956..82b254e24 100644 --- a/src/froidure-pin-base.cpp +++ b/src/froidure-pin-base.cpp @@ -31,7 +31,7 @@ namespace gapbind14 { } void init_froidure_pin_base(gapbind14::Module& m) { - using FroidurePin_ = libsemigroups::FroidurePinBase*; + using FroidurePin_ = std::shared_ptr; gapbind14::class_("FroidurePinBase") .def("enumerate", [](FroidurePin_ S, size_t limit) { S->enumerate(limit); }) diff --git a/src/froidure-pin.hpp b/src/froidure-pin.hpp index 499fe6c98..5d94af043 100644 --- a/src/froidure-pin.hpp +++ b/src/froidure-pin.hpp @@ -38,7 +38,7 @@ namespace gapbind14 { struct IsGapBind14Type> : std::true_type {}; template <> - struct IsGapBind14Type : std::true_type {}; + struct IsGapBind14Type : std::true_type {}; } // namespace gapbind14 diff --git a/src/pkg.cpp b/src/pkg.cpp index 1293814f0..96eb33f1c 100644 --- a/src/pkg.cpp +++ b/src/pkg.cpp @@ -128,7 +128,34 @@ GAPBIND14_MODULE(libsemigroups) { // which we have trouble dealing with in gapbind14, so we instead // just get the raw pointer, and get the std::unique_ptr to release // its ownership. - return libsemigroups::to(c).release(); + return std::shared_ptr( + libsemigroups::to(c).release()); + }); + + gapbind14::InstallGlobalFunction( + "congruence_non_trivial_classes", + [](Congruence& c, std::vector const& words) { + auto ntc = libsemigroups::congruence::non_trivial_classes( + c, words.begin(), words.end()); + return gapbind14::make_iterator(ntc.begin(), ntc.end()); + }); + + gapbind14::InstallGlobalFunction( + "froidure_pin_to_left_congruence", [](FroidurePinBase& fpb) { + return libsemigroups::to>( + congruence_kind::onesided, fpb, fpb.left_cayley_graph()); + }); + + gapbind14::InstallGlobalFunction( + "froidure_pin_to_right_congruence", [](FroidurePinBase& fpb) { + return libsemigroups::to>( + congruence_kind::onesided, fpb, fpb.right_cayley_graph()); + }); + + gapbind14::InstallGlobalFunction( + "froidure_pin_to_2_sided_congruence", [](FroidurePinBase& fpb) { + return libsemigroups::to>( + congruence_kind::twosided, fpb, fpb.right_cayley_graph()); }); // TODO: Add the to<> functions diff --git a/src/to_gap.hpp b/src/to_gap.hpp index b13a73717..86f5fed3f 100644 --- a/src/to_gap.hpp +++ b/src/to_gap.hpp @@ -517,18 +517,18 @@ namespace gapbind14 { template struct to_gap> { using WordGraph_ = libsemigroups::WordGraph; - Obj operator()(WordGraph_ const& ad) const noexcept { + Obj operator()(WordGraph_ const& wg) const noexcept { using node_type = typename WordGraph_::node_type; - Obj result = NEW_PLIST(T_PLIST, ad.number_of_nodes()); + Obj result = NEW_PLIST(T_PLIST, wg.number_of_nodes()); // this is intentionally not IMMUTABLE // TODO(WordGraph) handle case of zero nodes? - SET_LEN_PLIST(result, ad.number_of_nodes()); + SET_LEN_PLIST(result, wg.number_of_nodes()); - for (size_t i = 0; i < ad.number_of_nodes(); ++i) { + for (size_t i = 0; i < wg.number_of_nodes(); ++i) { Obj next = NEW_PLIST(T_PLIST, 0); SET_LEN_PLIST(next, 0); - for (size_t j = 0; j < ad.out_degree(); ++j) { - auto val = ad.target_no_checks(i, j); + for (size_t j = 0; j < wg.out_degree(); ++j) { + auto val = wg.target_no_checks(i, j); if (val != UNDEFINED) { AssPlist(next, j + 1, to_gap()(val + 1)); } diff --git a/tst/standard/attributes/acting.tst b/tst/standard/attributes/acting.tst index 84c9fbd5f..f68508efd 100644 --- a/tst/standard/attributes/acting.tst +++ b/tst/standard/attributes/acting.tst @@ -82,7 +82,7 @@ gap> IdempotentGeneratedSubsemigroup(S); gap> S := InverseSemigroup([PartialPerm([1, 2], [4, 3]), > PartialPerm([1, 2, 5], [1, 2, 4])]);; gap> IdempotentGeneratedSubsemigroup(S); - + # InjectionPrincipalFactor 1/6 gap> D := GreensDClassOfElement( diff --git a/tst/standard/attributes/homomorph.tst b/tst/standard/attributes/homomorph.tst index f86e9ccbb..86f773913 100644 --- a/tst/standard/attributes/homomorph.tst +++ b/tst/standard/attributes/homomorph.tst @@ -720,9 +720,8 @@ false # Test with quotient semigroup gap> S := Semigroup([Transformation([2, 1, 5, 1, 5]), > Transformation([1, 1, 1, 5, 3]), Transformation([2, 5, 3, 5, 3])]);; -gap> cong := SemigroupCongruence(S, [[Transformation([1, 1, 1, 1, 1]), Transformation([1, 1, 1, 3, 3])]]); -<2-sided semigroup congruence over with 1 generating pairs> +gap> cong := SemigroupCongruence(S, [[Transformation([1, 1, 1, 1, 1]), +> Transformation([1, 1, 1, 3, 3])]]);; gap> T := S / cong;; gap> gens := GeneratorsOfSemigroup(S);; gap> images := List(gens, gen -> EquivalenceClassOfElement(cong, gen));; diff --git a/tst/standard/libsemigroups/cong.tst b/tst/standard/libsemigroups/cong.tst index c10059a13..82dc7565d 100644 --- a/tst/standard/libsemigroups/cong.tst +++ b/tst/standard/libsemigroups/cong.tst @@ -86,17 +86,18 @@ gap> C := LeftSemigroupCongruence(S, [[S.1, S.2]]); 1 generating pairs> gap> LibsemigroupsCongruence(C);; +# TODO rm # CongruenceWordToClassIndex -gap> S := FreeBand(2); - -gap> C := LeftSemigroupCongruence(S, [[S.1, S.2]]); - with -1 generating pairs> -gap> CongruenceWordToClassIndex(C, [1, 2, 1, 2, 1, 2, 1, 1, 1, 1]); -1 -gap> CongruenceWordToClassIndex(C, EvaluateWord([S.1, S.2], -> [1, 2, 1, 2, 1, 2, 1, 1, 1, 1])); -1 +# gap> S := FreeBand(2); +# +# gap> C := LeftSemigroupCongruence(S, [[S.1, S.2]]); +# with +# 1 generating pairs> +# gap> CongruenceWordToClassIndex(C, [1, 2, 1, 2, 1, 2, 1, 1, 1, 1]); +# 1 +# gap> CongruenceWordToClassIndex(C, EvaluateWord([S.1, S.2], +# > [1, 2, 1, 2, 1, 2, 1, 1, 1, 1])); +# 1 # CongruenceLessNC gap> S := FreeBand(2); From ac0015819bb100235439ef26244900fb577104b4 Mon Sep 17 00:00:00 2001 From: James Mitchell Date: Fri, 19 Dec 2025 08:42:20 +0100 Subject: [PATCH 12/27] More --- gap/libsemigroups/cong.gi | 23 +++++++++++---------- gapbind14/include/gapbind14/gapbind14.hpp | 13 ++++++++++++ src/pkg.cpp | 25 ++++++++++++++++++++--- 3 files changed, 47 insertions(+), 14 deletions(-) diff --git a/gap/libsemigroups/cong.gi b/gap/libsemigroups/cong.gi index ab3a945eb..d25fa22e2 100644 --- a/gap/libsemigroups/cong.gi +++ b/gap/libsemigroups/cong.gi @@ -292,23 +292,23 @@ InstallMethod(EquivalenceClasses, [CanUseLibsemigroupsCongruence and HasGeneratingPairsOfLeftRightOrTwoSidedCongruence], function(C) - local result, CC, gens, class_index_to_word, rep, i; + local result, CC, gens, i, rep, word; if NrEquivalenceClasses(C) = infinity then ErrorNoReturn("the argument (a congruence) must have a finite ", "number of classes"); fi; - ErrorNoReturn("libsemigroups.Congruence has no member 'class_index_to_word'"); - # result := EmptyPlist(NrEquivalenceClasses(C)); - # CC := LibsemigroupsCongruence(C); - # gens := GeneratorsOfSemigroup(Range(C)); - # class_index_to_word := libsemigroups.Congruence.class_index_to_word; - # for i in [1 .. NrEquivalenceClasses(C)] do - # rep := EvaluateWord(gens, class_index_to_word(CC, i - 1) + 1); - # result[i] := EquivalenceClassOfElementNC(C, rep); - # od; - # return result; + result := EmptyPlist(NrEquivalenceClasses(C)); + CC := LibsemigroupsCongruence(C); + gens := GeneratorsOfSemigroup(Range(C)); + i := 0; + for word in libsemigroups.congruence_normal_forms(CC) do + i := i + 1; + rep := EvaluateWord(gens, word + 1); + result[i] := EquivalenceClassOfElementNC(C, rep); + od; + return result; end); ########################################################################### @@ -338,6 +338,7 @@ function(C) next := next + 1; fi; i := map[word]; + if not IsBound(part[i]) then part[i] := []; fi; diff --git a/gapbind14/include/gapbind14/gapbind14.hpp b/gapbind14/include/gapbind14/gapbind14.hpp index e16e185d5..fee965da1 100644 --- a/gapbind14/include/gapbind14/gapbind14.hpp +++ b/gapbind14/include/gapbind14/gapbind14.hpp @@ -440,6 +440,19 @@ namespace gapbind14 { } return result; } + + template + Obj make_iterator(Range range) { + size_t N = range.size_hint(); + Obj result = NEW_PLIST((N == 0 ? T_PLIST_EMPTY : T_PLIST_HOM), N); + SET_LEN_PLIST(result, N); + size_t i = 1; + while (!range.at_end()) { + AssPlist(result, i++, to_gap()(range.get())); + range.next(); + } + return result; + } } // namespace gapbind14 #endif // INCLUDE_GAPBIND14_GAPBIND14_HPP_ diff --git a/src/pkg.cpp b/src/pkg.cpp index 96eb33f1c..54c75c5ad 100644 --- a/src/pkg.cpp +++ b/src/pkg.cpp @@ -22,9 +22,10 @@ #include "pkg.hpp" -#include // for size_t -#include // for exception -#include // for string +#include // for size_t +#include // for exception +#include // for string +#include #include // for conditional<>::type #include // for unordered_map #include // for swap @@ -132,6 +133,24 @@ GAPBIND14_MODULE(libsemigroups) { libsemigroups::to(c).release()); }); + gapbind14::InstallGlobalFunction( + "congruence_normal_forms", [](Congruence& c) { + using ToddCoxeter = libsemigroups::ToddCoxeter; + using KnuthBendix = libsemigroups::KnuthBendix; + + c.run(); + if (c.has() && c.get()->finished()) { + auto nf = libsemigroups::todd_coxeter::normal_forms( + *c.get()); + return gapbind14::make_iterator(nf); + } else if (c.has() && c.get()->finished()) { + auto nf = libsemigroups::knuth_bendix::normal_forms( + *c.get()); + return gapbind14::make_iterator(nf); + } + throw std::runtime_error("Cannot compute normal forms!"); + }); + gapbind14::InstallGlobalFunction( "congruence_non_trivial_classes", [](Congruence& c, std::vector const& words) { From a65a50156c64d45011ee450152631be8cf6ee241 Mon Sep 17 00:00:00 2001 From: James Mitchell Date: Fri, 19 Dec 2025 09:31:16 +0100 Subject: [PATCH 13/27] Lint --- gap/libsemigroups/cong.gi | 5 ++--- gap/libsemigroups/froidure-pin.gi | 2 +- src/bipart.cpp | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/gap/libsemigroups/cong.gi b/gap/libsemigroups/cong.gi index d25fa22e2..3657255e9 100644 --- a/gap/libsemigroups/cong.gi +++ b/gap/libsemigroups/cong.gi @@ -272,7 +272,7 @@ InstallMethod(\<, 1, # to beat the method in congruences/cong.gi for # IsLeftRightOrTwoSidedCongruenceClass function(class1, class2) - local C, word1, word2, CC; + local C, word1, word2; C := EquivalenceClassRelation(class1); if not CanUseLibsemigroupsCongruence(C) @@ -321,7 +321,7 @@ InstallMethod(EquivalenceRelationPartitionWithSingletons, [CanUseLibsemigroupsCongruence and HasGeneratingPairsOfLeftRightOrTwoSidedCongruence], function(C) - local map, next, part, CC, word, i, x; + local map, next, part, word, i, x; if not IsFinite(Range(C)) then ErrorNoReturn("the argument (a congruence) must have finite range"); @@ -330,7 +330,6 @@ function(C) map := HashMap(); next := 1; part := []; - CC := LibsemigroupsCongruence(C); for x in Range(C) do word := CongruenceReduce(C, x); if not word in map then diff --git a/gap/libsemigroups/froidure-pin.gi b/gap/libsemigroups/froidure-pin.gi index 772cd8e70..71e12952b 100644 --- a/gap/libsemigroups/froidure-pin.gi +++ b/gap/libsemigroups/froidure-pin.gi @@ -320,7 +320,7 @@ InstallMethod(PositionCanonical, "for a semigroup with CanUseLibsemigroupsFroidurePin and mult. element", [IsSemigroup and CanUseLibsemigroupsFroidurePin, IsMultiplicativeElement], function(S, x) - local T, record, word, pos, C; + local T, record, word, pos; if IsPartialPermSemigroup(S) then if DegreeOfPartialPermSemigroup(S) < DegreeOfPartialPerm(x) diff --git a/src/bipart.cpp b/src/bipart.cpp index 79f113b32..52d4b7d98 100644 --- a/src/bipart.cpp +++ b/src/bipart.cpp @@ -1207,7 +1207,7 @@ Obj BLOCKS_INV_RIGHT(Obj self, Obj blocks_gap, Obj x_gap) { continue; } } - if (junk == (uint32_t) -1) { + if (junk == static_cast(-1)) { junk = next; next++; } From 788df04408a0281fcaf8ae25c6e4bebfe2a18c69 Mon Sep 17 00:00:00 2001 From: James Mitchell Date: Sat, 20 Dec 2025 12:20:55 +0100 Subject: [PATCH 14/27] All standard/congs tests run --- gap/libsemigroups/cong.gi | 82 +++++++++++++++----------- gap/libsemigroups/sims1.gi | 43 +++++++------- src/pkg.cpp | 60 ++++++++++++++++++- tst/standard/congruences/congpairs.tst | 8 +-- 4 files changed, 130 insertions(+), 63 deletions(-) diff --git a/gap/libsemigroups/cong.gi b/gap/libsemigroups/cong.gi index 3657255e9..06646c9f5 100644 --- a/gap/libsemigroups/cong.gi +++ b/gap/libsemigroups/cong.gi @@ -73,7 +73,7 @@ InstallTrueMethod(CanUseLibsemigroupsCongruence, BindGlobal("LibsemigroupsCongruence", function(C) - local S, CC, factor, fp, add_generating_pair, pair; + local S, kind, p, CC, Factorize2Args, fp, factor, add_generating_pair, pair; Assert(1, CanUseLibsemigroupsCongruence(C)); @@ -85,49 +85,42 @@ function(C) Unbind(C!.LibsemigroupsCongruence); S := Range(C); + kind := CongruenceHandednessString(C); + if IsFpSemigroup(S) or (HasIsFreeSemigroup(S) and IsFreeSemigroup(S)) or IsFpMonoid(S) or (HasIsFreeMonoid(S) and IsFreeMonoid(S)) then - CC := libsemigroups.Congruence.make( - CongruenceHandednessString(C), - LibsemigroupsPresentation(S)); - factor := Factorization; + p := LibsemigroupsPresentation(S); + if IsLeftMagmaCongruence(C) and not IsRightMagmaCongruence(C) then + p := libsemigroups.Presentation.copy(p); + libsemigroups.presentation_reverse(p); + fi; + CC := libsemigroups.Congruence.make(kind, p); + Factorize2Args := Factorization; elif CanUseLibsemigroupsFroidurePin(S) then fp := LibsemigroupsFroidurePin(S); - if CongruenceHandednessString(C) = "left" then + if kind = "left" then CC := libsemigroups.froidure_pin_to_left_congruence(fp); - elif CongruenceHandednessString(C) = "right" then + elif kind = "right" then CC := libsemigroups.froidure_pin_to_right_congruence(fp); else CC := libsemigroups.froidure_pin_to_2_sided_congruence(fp); fi; - factor := MinimalFactorization; + Factorize2Args := MinimalFactorization; elif CanUseGapFroidurePin(S) then - ErrorNoReturn( - Concatenation( - "constructing LibsemigroupsCongruence from a GAP FroidurePin is not ", - "yet implemented")); - # TODO: Implement when ToddCoxeter_tox_FroidurePin and - # FroidurePin_to_Congruence are implemented. Something like: - # if IsRightMagmaCongruence(C) then - # cayley_digraph := RightCayleyDigraph(S); - # else - # cayley := LeftCayleyDigraph(S); - # fi; - # tc := libsemigroups.ToddCoxeter.make_from_wordgraph( - # CongruenceHandednessString(C), - # cayley_digraph); - # fp := libsemigroups.ToddCoexeter_to_FroidurePin(tc); - # CC := libsemigroups.FroidurePin_to_Congruence( - # CongruenceHandednessString(C), - # fp, - # fp.right_cayley_graph); - # factor := MinimalFactorization; + RUN_FROIDURE_PIN(GapFroidurePin(S), -1, InfoLevel(InfoSemigroups) > 0); + CC := libsemigroups.gap_froidure_pin_to_congruence(kind, GapFroidurePin(S)); + Factorize2Args := MinimalFactorization; else TryNextMethod(); fi; + if IsLeftMagmaCongruence(C) and not IsRightMagmaCongruence(C) then + factor := x -> Reversed(Factorize2Args(S, x) - 1); + else + factor := x -> Factorize2Args(S, x) - 1; + fi; add_generating_pair := libsemigroups.Congruence.add_generating_pair; for pair in GeneratingPairsOfLeftRightOrTwoSidedCongruence(C) do - add_generating_pair(CC, factor(S, pair[1]) - 1, factor(S, pair[2]) - 1); + add_generating_pair(CC, factor(pair[1]), factor(pair[2])); od; C!.LibsemigroupsCongruence := CC; return CC; @@ -174,6 +167,9 @@ InstallMethod(CongruenceReduce, function(C, word) local CC; CC := LibsemigroupsCongruence(C); + if IsLeftMagmaCongruence(C) and not IsRightMagmaCongruence(C) then + word := Reversed(word); + fi; return libsemigroups.Congruence.reduce(CC, word - 1) + 1; end); @@ -211,7 +207,7 @@ InstallMethod(CongruenceTestMembershipNC, function(C, elm1, elm2) local S, pos1, pos2, lookup, word1, word2, CC; - S := Range(C); + S := Range(C); if IsFpSemigroup(S) or (HasIsFreeSemigroup(S) and IsFreeSemigroup(S)) or IsFpMonoid(S) or (HasIsFreeMonoid(S) and IsFreeMonoid(S)) then word1 := Factorization(S, elm1); @@ -230,6 +226,10 @@ function(C, elm1, elm2) # Cannot currently test the next line Assert(0, false); fi; + if IsLeftMagmaCongruence(C) and not IsRightMagmaCongruence(C) then + word1 := Reversed(word1); + word2 := Reversed(word2); + fi; CC := LibsemigroupsCongruence(C); return libsemigroups.Congruence.contains(CC, word1 - 1, word2 - 1); end); @@ -239,19 +239,24 @@ InstallMethod(EquivalenceRelationPartition, [CanUseLibsemigroupsCongruence and HasGeneratingPairsOfLeftRightOrTwoSidedCongruence], function(C) - local S, CC, words, ntc, gens, class, i, j; + local S, CC, reverse, words, ntc, gens, class, i, j; S := Range(C); if not IsFinite(S) or CanUseLibsemigroupsFroidurePin(S) then # TODO this won't work when S is infinite! CC := LibsemigroupsCongruence(C); - words := List(S, x -> Factorization(S, x)) - 1; + if IsLeftMagmaCongruence(C) and not IsRightMagmaCongruence(C) then + reverse := Reversed; + else + reverse := IdFunc; + fi; + words := List(S, x -> reverse(Factorization(S, x) - 1)); ntc := libsemigroups.congruence_non_trivial_classes(CC, words) + 1; gens := GeneratorsOfSemigroup(S); for i in [1 .. Length(ntc)] do class := ntc[i]; for j in [1 .. Length(class)] do - class[j] := EvaluateWord(gens, class[j]); + class[j] := EvaluateWord(gens, reverse(class[j])); od; od; return ntc; @@ -292,7 +297,7 @@ InstallMethod(EquivalenceClasses, [CanUseLibsemigroupsCongruence and HasGeneratingPairsOfLeftRightOrTwoSidedCongruence], function(C) - local result, CC, gens, i, rep, word; + local result, CC, gens, i, reverse, rep, word; if NrEquivalenceClasses(C) = infinity then ErrorNoReturn("the argument (a congruence) must have a finite ", @@ -303,9 +308,14 @@ function(C) CC := LibsemigroupsCongruence(C); gens := GeneratorsOfSemigroup(Range(C)); i := 0; + if IsLeftMagmaCongruence(C) and not IsRightMagmaCongruence(C) then + reverse := Reversed; + else + reverse := IdFunc; + fi; for word in libsemigroups.congruence_normal_forms(CC) do i := i + 1; - rep := EvaluateWord(gens, word + 1); + rep := EvaluateWord(gens, reverse(word + 1)); result[i] := EquivalenceClassOfElementNC(C, rep); od; return result; @@ -321,7 +331,7 @@ InstallMethod(EquivalenceRelationPartitionWithSingletons, [CanUseLibsemigroupsCongruence and HasGeneratingPairsOfLeftRightOrTwoSidedCongruence], function(C) - local map, next, part, word, i, x; + local map, next, part, CC, word, i, x; if not IsFinite(Range(C)) then ErrorNoReturn("the argument (a congruence) must have finite range"); diff --git a/gap/libsemigroups/sims1.gi b/gap/libsemigroups/sims1.gi index 57ed12f99..026130b1a 100644 --- a/gap/libsemigroups/sims1.gi +++ b/gap/libsemigroups/sims1.gi @@ -14,7 +14,7 @@ DeclareOperation("LibsemigroupsSims1", InstallMethod(LibsemigroupsSims1, [IsSemigroup, IsPosInt, IsList, IsString], function(S, n, extra, kind) - local P, rules, sims1, Q, pair, r; + local rules, reverse, P, sims1, r, pair; Assert(1, CanUseFroidurePin(S) @@ -43,9 +43,19 @@ function(S, n, extra, kind) rules := RulesOfSemigroup(S); fi; + if kind = "left" then + reverse := Reversed; + else + reverse := IdFunc; + fi; + P := libsemigroups.Presentation.make(); + libsemigroups.Presentation.contains_empty_word( + P, IsFpMonoid(S) or (HasIsFreeMonoid(S) and IsFreeMonoid(S))); + for r in rules do - libsemigroups.presentation_add_rule(P, r[1] - 1, r[2] - 1); + libsemigroups.presentation_add_rule_no_checks( + P, reverse(r[1] - 1), reverse(r[2] - 1)); od; if not IsEmpty(rules) then @@ -57,33 +67,26 @@ function(S, n, extra, kind) libsemigroups.Presentation.alphabet( P, [0 .. Size(GeneratorsOfSemigroup(S)) - 1]); fi; - libsemigroups.Presentation.validate(P); - # RulesOfSemigroup always returns the rules of an isomorphic fp semigroup - libsemigroups.Presentation.contains_empty_word( - P, IsFpMonoid(S) or (HasIsFreeMonoid(S) and IsFreeMonoid(S))); - - sims1 := libsemigroups.Sims1.make(kind); - libsemigroups.Sims1.short_rules(sims1, P); if not IsEmpty(extra) then - Q := libsemigroups.Presentation.make(); - libsemigroups.Presentation.contains_empty_word(Q, IsMonoid(S)); - libsemigroups.Presentation.alphabet(Q, - libsemigroups.Presentation.alphabet(P)); - for pair in extra do libsemigroups.presentation_add_rule( - Q, - MinimalFactorization(S, pair[1]) - 1, - MinimalFactorization(S, pair[2]) - 1); + P, + reverse(MinimalFactorization(S, pair[1]) - 1), + reverse(MinimalFactorization(S, pair[2]) - 1)); od; - libsemigroups.Presentation.validate(Q); - libsemigroups.Sims1.extra(sims1, Q); fi; + + libsemigroups.Presentation.throw_if_bad_alphabet_or_rules(P); + + sims1 := libsemigroups.Sims1.make(P); + libsemigroups.Sims1.cbegin_long_rules(sims1, 2 * Length(rules)); + if n > 64 and libsemigroups.hardware_concurrency() > 2 then libsemigroups.Sims1.number_of_threads( sims1, libsemigroups.hardware_concurrency() - 2); fi; + return sims1; end); @@ -182,7 +185,7 @@ function(S) libsemigroups.Presentation.alphabet_from_rules(P); libsemigroups.Presentation.contains_empty_word(P, true); - libsemigroups.Presentation.validate(P); + libsemigroups.Presentation.throw_if_bad_alphabet_or_rules(P); ro := libsemigroups.RepOrc.make(); libsemigroups.RepOrc.short_rules(ro, P); diff --git a/src/pkg.cpp b/src/pkg.cpp index 54c75c5ad..6e037b477 100644 --- a/src/pkg.cpp +++ b/src/pkg.cpp @@ -25,14 +25,13 @@ #include // for size_t #include // for exception #include // for string +#include // for set #include #include // for conditional<>::type #include // for unordered_map #include // for swap #include // for vector -#include // for set - // GAP headers #include "gap_all.h" @@ -177,6 +176,45 @@ GAPBIND14_MODULE(libsemigroups) { congruence_kind::twosided, fpb, fpb.right_cayley_graph()); }); + gapbind14::InstallGlobalFunction( + "gap_froidure_pin_to_congruence", [](Obj kind_str_obj, Obj gap_fp) { + std::string kind_str(CSTR_STRING(kind_str_obj)); + Obj gap_wg; + if (kind_str == "left") { + gap_wg = ElmPRec(gap_fp, RNamName("left")); + } else { + gap_wg = ElmPRec(gap_fp, RNamName("right")); + } + + SEMIGROUPS_ASSERT(IS_PLIST(gap_wg)); + SEMIGROUPS_ASSERT(LEN_PLIST(gap_wg) > 0); + + WordGraph wg(LEN_PLIST(gap_wg) + 1, + LEN_PLIST(ELM_PLIST(gap_wg, 1))); + + Obj genslookup = ElmPRec(gap_fp, RNamName("genslookup")); + SEMIGROUPS_ASSERT(IS_PLIST(genslookup)); + SEMIGROUPS_ASSERT(LEN_PLIST(genslookup) == wg.out_degree()); + + for (uint32_t a = 0; a < wg.out_degree(); ++a) { + wg.target_no_checks(0, a, INT_INTOBJ(ELM_PLIST(genslookup, a + 1))); + } + + for (uint32_t n = 0; n < wg.number_of_nodes() - 1; ++n) { + SEMIGROUPS_ASSERT(IS_PLIST(ELM_PLIST(wg, n))); + SEMIGROUPS_ASSERT(LEN_PLIST(ELM_PLIST(wg, n)) == wg.out_degree()); + for (uint32_t a = 0; a < wg.out_degree(); ++a) { + wg.target_no_checks( + n + 1, + a, + INT_INTOBJ(ELM_PLIST(ELM_PLIST(gap_wg, n + 1), a + 1))); + } + } + // TODO std::move wg + return libsemigroups::to>( + gapbind14::to_cpp{}(kind_str_obj), wg); + }); + // TODO: Add the to<> functions //////////////////////////////////////////////////////////////////////// @@ -212,6 +250,8 @@ GAPBIND14_MODULE(libsemigroups) { gapbind14::class_>("Presentation") .def(gapbind14::init<>{}, "make") + .def("copy", + [](Presentation& thing) { return Presentation(thing); }) .def("alphabet", [](Presentation& thing) { return thing.alphabet(); }) .def("set_alphabet", @@ -237,6 +277,13 @@ GAPBIND14_MODULE(libsemigroups) { return thing.rules.size(); }); + gapbind14::InstallGlobalFunction( + "presentation_add_rule_no_checks", + gapbind14::overload_cast&, + word_type const&, + word_type const&>( + &libsemigroups::presentation::add_rule_no_checks)); + gapbind14::InstallGlobalFunction( "presentation_add_rule", gapbind14::overload_cast&, @@ -249,6 +296,11 @@ GAPBIND14_MODULE(libsemigroups) { gapbind14::overload_cast&, size_t>( &libsemigroups::presentation::add_identity_rules)); + gapbind14::InstallGlobalFunction( + "presentation_reverse", + gapbind14::overload_cast&>( + &libsemigroups::presentation::reverse)); + //////////////////////////////////////////////////////////////////////// // Sims //////////////////////////////////////////////////////////////////////// @@ -262,7 +314,9 @@ GAPBIND14_MODULE(libsemigroups) { .def("number_of_threads", [](Sims1& s, size_t val) { s.number_of_threads(val); }) .def("number_of_congruences", &Sims1::number_of_congruences) - .def("cbegin", &Sims1::cbegin); + .def("cbegin", &Sims1::cbegin) + .def("cbegin_long_rules", + [](Sims1& s, size_t pos) { s.cbegin_long_rules(pos); }); gapbind14::class_("RepOrc") .def(gapbind14::init<>{}, "make") diff --git a/tst/standard/congruences/congpairs.tst b/tst/standard/congruences/congpairs.tst index a62dbe6e1..c6098d053 100644 --- a/tst/standard/congruences/congpairs.tst +++ b/tst/standard/congruences/congpairs.tst @@ -749,8 +749,8 @@ gap> class1 = class2; false gap> enum := Enumerator(class1);; gap> AsSSortedList(enum); -[ m2, m1^2, m1*m2, m2*m1, m1^2*m2, m1*m2*m1, m2*m1*m2, m1^2*m2*m1, (m1*m2)^2, - (m2*m1)^2, (m1*m2)^2*m1 ] +[ m1^2, m1^2*m2, m1^2*m2*m1, m1*m2, m1*m2*m1, (m1*m2)^2, (m1*m2)^2*m1, m2, + m2*m1, m2*m1*m2, (m2*m1)^2 ] gap> Size(enum); 11 gap> class1 * class2 = EquivalenceClassOfElement(cong, gens[2] ^ 20 * gens[1] ^ 42); @@ -794,7 +794,7 @@ gap> Size(part[1]); gap> EquivalenceRelationCanonicalLookup(cong); [ 1, 2, 3, 2, 4, 2, 2, 2, 5, 2, 2, 6, 2, 7, 2, 2, 8, 2, 2, 2, 9, 2, 2, 10, 2, 2, 2, 2, 11, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 ] -gap> Set(ImagesElm(cong, M.1)) = part[1]; +gap> Set(ImagesElm(cong, M.1)) = Set(part[1]); true gap> ImagesElm(cong, One(M)); [ ] @@ -858,7 +858,7 @@ gap> EquivalenceRelationCanonicalLookup(cong); [ 1, 2, 3, 4, 2, 5, 2, 6, 7, 4, 8, 9, 4, 2, 6, 6, 7, 10, 11, 6, 7, 4, 2, 2, 2, 6, 12, 6, 7, 4, 4, 2, 13, 2, 6, 6, 4, 2, 2, 4 ] gap> part1 := First(part, l -> M.1 in l);; -gap> Set(ImagesElm(cong, M.1)) = part1; +gap> Set(ImagesElm(cong, M.1)) = Set(part1); true gap> NrEquivalenceClasses(cong); 13 From 69f1b2722c633aa779e3b7639a7e36af506a4915 Mon Sep 17 00:00:00 2001 From: James Mitchell Date: Sat, 20 Dec 2025 18:12:21 +0100 Subject: [PATCH 15/27] more working --- gap/libsemigroups/cong.gi | 18 +++++++-- gap/libsemigroups/sims1.gi | 22 ++++++----- src/pkg.cpp | 26 ++++++++++++ tst/standard/libsemigroups/cong.tst | 23 +++-------- tst/standard/libsemigroups/froidure-pin.tst | 44 ++++++++++----------- 5 files changed, 79 insertions(+), 54 deletions(-) diff --git a/gap/libsemigroups/cong.gi b/gap/libsemigroups/cong.gi index 06646c9f5..b14cf1533 100644 --- a/gap/libsemigroups/cong.gi +++ b/gap/libsemigroups/cong.gi @@ -96,7 +96,11 @@ function(C) fi; CC := libsemigroups.Congruence.make(kind, p); Factorize2Args := Factorization; + # TODO needed + #elif IsQuotientSemigroup(S) then + # return QuotientSemigroupCongruence(S)!.LibsemigroupsCongruence; elif CanUseLibsemigroupsFroidurePin(S) then + Enumerate(S); fp := LibsemigroupsFroidurePin(S); if kind = "left" then CC := libsemigroups.froidure_pin_to_left_congruence(fp); @@ -239,19 +243,25 @@ InstallMethod(EquivalenceRelationPartition, [CanUseLibsemigroupsCongruence and HasGeneratingPairsOfLeftRightOrTwoSidedCongruence], function(C) - local S, CC, reverse, words, ntc, gens, class, i, j; + local S, CC, reverse, words, ntc, super, gens, class, i, j; S := Range(C); if not IsFinite(S) or CanUseLibsemigroupsFroidurePin(S) then - # TODO this won't work when S is infinite! CC := LibsemigroupsCongruence(C); if IsLeftMagmaCongruence(C) and not IsRightMagmaCongruence(C) then reverse := Reversed; else reverse := IdFunc; fi; - words := List(S, x -> reverse(Factorization(S, x) - 1)); - ntc := libsemigroups.congruence_non_trivial_classes(CC, words) + 1; + if IsFinite(S) then + words := List(S, x -> reverse(Factorization(S, x) - 1)); + ntc := libsemigroups.congruence_non_trivial_classes(CC, words) + 1; + elif IsFpSemigroup(S) or IsFreeSemigroup(S) or IsFpMonoid(S) or IsFreeMonoid(S) then + super := LibsemigroupsCongruence(UnderlyingCongruence(S)); + ntc := libsemigroups.infinite_congruence_non_trivial_classes(super, CC) + 1; + else + TryNextMethod(); + fi; gens := GeneratorsOfSemigroup(S); for i in [1 .. Length(ntc)] do class := ntc[i]; diff --git a/gap/libsemigroups/sims1.gi b/gap/libsemigroups/sims1.gi index 026130b1a..c0cc50b09 100644 --- a/gap/libsemigroups/sims1.gi +++ b/gap/libsemigroups/sims1.gi @@ -11,6 +11,7 @@ DeclareOperation("LibsemigroupsSims1", [IsSemigroup, IsPosInt, IsList, IsString]); +# TODO rename extra -> included InstallMethod(LibsemigroupsSims1, [IsSemigroup, IsPosInt, IsList, IsString], function(S, n, extra, kind) @@ -60,26 +61,26 @@ function(S, n, extra, kind) if not IsEmpty(rules) then libsemigroups.Presentation.alphabet_from_rules(P); + libsemigroups.presentation_normalize_alphabet(P); elif (HasIsFreeMonoid(S) and IsFreeMonoid(S)) or IsFpMonoid(S) then - libsemigroups.Presentation.alphabet( + libsemigroups.Presentation.set_alphabet( P, [0 .. Size(GeneratorsOfMonoid(S)) - 1]); elif (HasIsFreeSemigroup(S) and IsFreeSemigroup(S)) or IsFpSemigroup(S) then - libsemigroups.Presentation.alphabet( + libsemigroups.Presentation.set_alphabet( P, [0 .. Size(GeneratorsOfSemigroup(S)) - 1]); fi; + libsemigroups.Presentation.throw_if_bad_alphabet_or_rules(P); + sims1 := libsemigroups.Sims1.make(P); + if not IsEmpty(extra) then for pair in extra do - libsemigroups.presentation_add_rule( - P, + libsemigroups.sims1_add_included_pair(sims1, reverse(MinimalFactorization(S, pair[1]) - 1), reverse(MinimalFactorization(S, pair[2]) - 1)); od; fi; - libsemigroups.Presentation.throw_if_bad_alphabet_or_rules(P); - - sims1 := libsemigroups.Sims1.make(P); libsemigroups.Sims1.cbegin_long_rules(sims1, 2 * Length(rules)); if n > 64 and libsemigroups.hardware_concurrency() > 2 then @@ -180,15 +181,16 @@ function(S) P := libsemigroups.Presentation.make(); for r in RelationsOfFpSemigroup(S) do r := List(r, x -> SEMIGROUPS.ExtRepObjToWord(ExtRepOfObj(x))); - libsemigroups.presentation_add_rule(P, r[1] - 1, r[2] - 1); + libsemigroups.presentation_add_rule_no_checks(P, r[1] - 1, r[2] - 1); od; libsemigroups.Presentation.alphabet_from_rules(P); + libsemigroups.presentation_normalize_alphabet(P); libsemigroups.Presentation.contains_empty_word(P, true); libsemigroups.Presentation.throw_if_bad_alphabet_or_rules(P); ro := libsemigroups.RepOrc.make(); - libsemigroups.RepOrc.short_rules(ro, P); + libsemigroups.RepOrc.presentation(ro, P); libsemigroups.RepOrc.min_nodes(ro, 1); if HasIsomorphismTransformationSemigroup(S) or IsTransformationSemigroup(S) then @@ -205,7 +207,7 @@ function(S) ro, libsemigroups.hardware_concurrency() - 2); fi; - D := libsemigroups.RepOrc.digraph(ro); + D := libsemigroups.RepOrc.word_graph(ro); deg := Length(D); if deg = 0 then diff --git a/src/pkg.cpp b/src/pkg.cpp index 6e037b477..84a2068b5 100644 --- a/src/pkg.cpp +++ b/src/pkg.cpp @@ -158,6 +158,15 @@ GAPBIND14_MODULE(libsemigroups) { return gapbind14::make_iterator(ntc.begin(), ntc.end()); }); + gapbind14::InstallGlobalFunction( + "infinite_congruence_non_trivial_classes", + [](Congruence& super, Congruence& sub) { + auto ntc = libsemigroups::knuth_bendix::non_trivial_classes( + *super.get>(), + *sub.get>()); + return gapbind14::make_iterator(ntc.begin(), ntc.end()); + }); + gapbind14::InstallGlobalFunction( "froidure_pin_to_left_congruence", [](FroidurePinBase& fpb) { return libsemigroups::to>( @@ -301,6 +310,11 @@ GAPBIND14_MODULE(libsemigroups) { gapbind14::overload_cast&>( &libsemigroups::presentation::reverse)); + gapbind14::InstallGlobalFunction( + "presentation_normalize_alphabet", + gapbind14::overload_cast&>( + &libsemigroups::presentation::normalize_alphabet)); + //////////////////////////////////////////////////////////////////////// // Sims //////////////////////////////////////////////////////////////////////// @@ -318,10 +332,20 @@ GAPBIND14_MODULE(libsemigroups) { .def("cbegin_long_rules", [](Sims1& s, size_t pos) { s.cbegin_long_rules(pos); }); + gapbind14::InstallGlobalFunction( + "sims1_add_included_pair", + [](Sims1& sims1, word_type const& u, word_type const& v) { + libsemigroups::sims::add_included_pair(sims1, u, v); + }); + gapbind14::class_("RepOrc") .def(gapbind14::init<>{}, "make") .def("number_of_threads", [](RepOrc& ro, size_t val) { ro.number_of_threads(val); }) + .def("presentation", + [](RepOrc& ro, Presentation const& p) { + ro.presentation(p); + }) .def("max_nodes", [](RepOrc& ro, size_t val) { ro.max_nodes(val); }) .def("min_nodes", [](RepOrc& ro, size_t val) { ro.min_nodes(val); }) .def("target_size", [](RepOrc& ro, size_t val) { ro.target_size(val); }) @@ -348,6 +372,8 @@ GAPBIND14_MODULE(libsemigroups) { [](Congruence& self, word_type const& u, word_type const& v) { + // FIXME the following is a hack to make one test file work + self.run_for(std::chrono::milliseconds(10)); return libsemigroups::congruence::contains(self, u, v); }) .def("reduce", [](Congruence& self, word_type const& u) { diff --git a/tst/standard/libsemigroups/cong.tst b/tst/standard/libsemigroups/cong.tst index 82dc7565d..35c79ac65 100644 --- a/tst/standard/libsemigroups/cong.tst +++ b/tst/standard/libsemigroups/cong.tst @@ -86,19 +86,6 @@ gap> C := LeftSemigroupCongruence(S, [[S.1, S.2]]); 1 generating pairs> gap> LibsemigroupsCongruence(C);; -# TODO rm -# CongruenceWordToClassIndex -# gap> S := FreeBand(2); -# -# gap> C := LeftSemigroupCongruence(S, [[S.1, S.2]]); -# with -# 1 generating pairs> -# gap> CongruenceWordToClassIndex(C, [1, 2, 1, 2, 1, 2, 1, 1, 1, 1]); -# 1 -# gap> CongruenceWordToClassIndex(C, EvaluateWord([S.1, S.2], -# > [1, 2, 1, 2, 1, 2, 1, 1, 1, 1])); -# 1 - # CongruenceLessNC gap> S := FreeBand(2); @@ -207,9 +194,9 @@ gap> C := LeftSemigroupCongruence(S, [[S.1, S.1 ^ 10]]); with 0 generating pairs> gap> AsSSortedList(EquivalenceClasses(C)); -[ , , - , , - , ] +[ , , + , , + , ] gap> D := LeftSemigroupCongruence(S, [[S.1, S.2 ^ 10]]); with 1 generating pairs> @@ -230,7 +217,7 @@ gap> u := Image(hom, Transformation([1, 1, 1, 1])); <2-sided congruence class of Transformation( [ 1, 2, 2, 2 ] )> gap> t := Image(hom, Transformation([2, 1, 2, 3])); <2-sided congruence class of Transformation( [ 2, 1, 2, 3 ] )> -gap> u < t; +gap> t < u; true # EquivalenceClasses for a congruence with infinitely many classes @@ -278,7 +265,7 @@ gap> C := SemigroupCongruence(S, [[S.1, S.2]]); <2-sided semigroup congruence over with 1 generating pairs> gap> ImagesElm(C, S.1); -[ s1, s2, s1*s2, s2^2, s1*s2^2 ] +[ s2, s1*s2, s2^2, s1*s2^2, s1 ] gap> ImagesElm(C, S.3); [ s3 ] diff --git a/tst/standard/libsemigroups/froidure-pin.tst b/tst/standard/libsemigroups/froidure-pin.tst index 350134f09..4269d3586 100644 --- a/tst/standard/libsemigroups/froidure-pin.tst +++ b/tst/standard/libsemigroups/froidure-pin.tst @@ -40,13 +40,13 @@ rec( add_generator := function( arg1, arg2 ) ... end, number_of_generators := function( arg1 ) ... end, number_of_idempotents := function( arg1 ) ... end, position := function( arg1, arg2 ) ... end, - position_to_sorted_position := function( arg1, arg2 ) ... end, prefix := function( arg1, arg2 ) ... end, right_cayley_graph := function( arg1 ) ... end, rules := function( arg1 ) ... end, size := function( arg1 ) ... end, sorted_at := function( arg1, arg2 ) ... end, sorted_position := function( arg1, arg2 ) ... end, - suffix := function( arg1, arg2 ) ... end ) + suffix := function( arg1, arg2 ) ... end, + to_sorted_position := function( arg1, arg2 ) ... end ) gap> FroidurePinMemFnRec(Semigroup(ConstantTransformation(17, 1))); rec( add_generator := function( arg1, arg2 ) ... end, at := function( arg1, arg2 ) ... end, @@ -65,13 +65,13 @@ rec( add_generator := function( arg1, arg2 ) ... end, number_of_generators := function( arg1 ) ... end, number_of_idempotents := function( arg1 ) ... end, position := function( arg1, arg2 ) ... end, - position_to_sorted_position := function( arg1, arg2 ) ... end, prefix := function( arg1, arg2 ) ... end, right_cayley_graph := function( arg1 ) ... end, rules := function( arg1 ) ... end, size := function( arg1 ) ... end, sorted_at := function( arg1, arg2 ) ... end, sorted_position := function( arg1, arg2 ) ... end, - suffix := function( arg1, arg2 ) ... end ) + suffix := function( arg1, arg2 ) ... end, + to_sorted_position := function( arg1, arg2 ) ... end ) gap> FroidurePinMemFnRec(Semigroup(ConstantTransformation(65537, 1))); rec( add_generator := function( arg1, arg2 ) ... end, at := function( arg1, arg2 ) ... end, @@ -90,13 +90,13 @@ rec( add_generator := function( arg1, arg2 ) ... end, number_of_generators := function( arg1 ) ... end, number_of_idempotents := function( arg1 ) ... end, position := function( arg1, arg2 ) ... end, - position_to_sorted_position := function( arg1, arg2 ) ... end, prefix := function( arg1, arg2 ) ... end, right_cayley_graph := function( arg1 ) ... end, rules := function( arg1 ) ... end, size := function( arg1 ) ... end, sorted_at := function( arg1, arg2 ) ... end, sorted_position := function( arg1, arg2 ) ... end, - suffix := function( arg1, arg2 ) ... end ) + suffix := function( arg1, arg2 ) ... end, + to_sorted_position := function( arg1, arg2 ) ... end ) gap> FroidurePinMemFnRec(SymmetricInverseMonoid(1)); rec( add_generator := function( arg1, arg2 ) ... end, at := function( arg1, arg2 ) ... end, @@ -115,13 +115,13 @@ rec( add_generator := function( arg1, arg2 ) ... end, number_of_generators := function( arg1 ) ... end, number_of_idempotents := function( arg1 ) ... end, position := function( arg1, arg2 ) ... end, - position_to_sorted_position := function( arg1, arg2 ) ... end, prefix := function( arg1, arg2 ) ... end, right_cayley_graph := function( arg1 ) ... end, rules := function( arg1 ) ... end, size := function( arg1 ) ... end, sorted_at := function( arg1, arg2 ) ... end, sorted_position := function( arg1, arg2 ) ... end, - suffix := function( arg1, arg2 ) ... end ) + suffix := function( arg1, arg2 ) ... end, + to_sorted_position := function( arg1, arg2 ) ... end ) gap> FroidurePinMemFnRec(SymmetricInverseMonoid(17)); rec( add_generator := function( arg1, arg2 ) ... end, at := function( arg1, arg2 ) ... end, @@ -140,13 +140,13 @@ rec( add_generator := function( arg1, arg2 ) ... end, number_of_generators := function( arg1 ) ... end, number_of_idempotents := function( arg1 ) ... end, position := function( arg1, arg2 ) ... end, - position_to_sorted_position := function( arg1, arg2 ) ... end, prefix := function( arg1, arg2 ) ... end, right_cayley_graph := function( arg1 ) ... end, rules := function( arg1 ) ... end, size := function( arg1 ) ... end, sorted_at := function( arg1, arg2 ) ... end, sorted_position := function( arg1, arg2 ) ... end, - suffix := function( arg1, arg2 ) ... end ) + suffix := function( arg1, arg2 ) ... end, + to_sorted_position := function( arg1, arg2 ) ... end ) gap> FroidurePinMemFnRec(SymmetricInverseMonoid(65537)); rec( add_generator := function( arg1, arg2 ) ... end, at := function( arg1, arg2 ) ... end, @@ -165,13 +165,13 @@ rec( add_generator := function( arg1, arg2 ) ... end, number_of_generators := function( arg1 ) ... end, number_of_idempotents := function( arg1 ) ... end, position := function( arg1, arg2 ) ... end, - position_to_sorted_position := function( arg1, arg2 ) ... end, prefix := function( arg1, arg2 ) ... end, right_cayley_graph := function( arg1 ) ... end, rules := function( arg1 ) ... end, size := function( arg1 ) ... end, sorted_at := function( arg1, arg2 ) ... end, sorted_position := function( arg1, arg2 ) ... end, - suffix := function( arg1, arg2 ) ... end ) + suffix := function( arg1, arg2 ) ... end, + to_sorted_position := function( arg1, arg2 ) ... end ) gap> FroidurePinMemFnRec(FullBooleanMatMonoid(2)); rec( add_generator := function( arg1, arg2 ) ... end, at := function( arg1, arg2 ) ... end, @@ -190,13 +190,13 @@ rec( add_generator := function( arg1, arg2 ) ... end, number_of_generators := function( arg1 ) ... end, number_of_idempotents := function( arg1 ) ... end, position := function( arg1, arg2 ) ... end, - position_to_sorted_position := function( arg1, arg2 ) ... end, prefix := function( arg1, arg2 ) ... end, right_cayley_graph := function( arg1 ) ... end, rules := function( arg1 ) ... end, size := function( arg1 ) ... end, sorted_at := function( arg1, arg2 ) ... end, sorted_position := function( arg1, arg2 ) ... end, - suffix := function( arg1, arg2 ) ... end ) + suffix := function( arg1, arg2 ) ... end, + to_sorted_position := function( arg1, arg2 ) ... end ) gap> FroidurePinMemFnRec(RegularBooleanMatMonoid(9)); rec( add_generator := function( arg1, arg2 ) ... end, at := function( arg1, arg2 ) ... end, @@ -215,13 +215,13 @@ rec( add_generator := function( arg1, arg2 ) ... end, number_of_generators := function( arg1 ) ... end, number_of_idempotents := function( arg1 ) ... end, position := function( arg1, arg2 ) ... end, - position_to_sorted_position := function( arg1, arg2 ) ... end, prefix := function( arg1, arg2 ) ... end, right_cayley_graph := function( arg1 ) ... end, rules := function( arg1 ) ... end, size := function( arg1 ) ... end, sorted_at := function( arg1, arg2 ) ... end, sorted_position := function( arg1, arg2 ) ... end, - suffix := function( arg1, arg2 ) ... end ) + suffix := function( arg1, arg2 ) ... end, + to_sorted_position := function( arg1, arg2 ) ... end ) gap> FroidurePinMemFnRec(FullTropicalMinPlusMonoid(2, 2)); rec( add_generator := function( arg1, arg2 ) ... end, at := function( arg1, arg2 ) ... end, @@ -240,13 +240,13 @@ rec( add_generator := function( arg1, arg2 ) ... end, number_of_generators := function( arg1 ) ... end, number_of_idempotents := function( arg1 ) ... end, position := function( arg1, arg2 ) ... end, - position_to_sorted_position := function( arg1, arg2 ) ... end, prefix := function( arg1, arg2 ) ... end, right_cayley_graph := function( arg1 ) ... end, rules := function( arg1 ) ... end, size := function( arg1 ) ... end, sorted_at := function( arg1, arg2 ) ... end, sorted_position := function( arg1, arg2 ) ... end, - suffix := function( arg1, arg2 ) ... end ) + suffix := function( arg1, arg2 ) ... end, + to_sorted_position := function( arg1, arg2 ) ... end ) gap> FroidurePinMemFnRec(FullTropicalMaxPlusMonoid(2, 2)); rec( add_generator := function( arg1, arg2 ) ... end, at := function( arg1, arg2 ) ... end, @@ -265,13 +265,13 @@ rec( add_generator := function( arg1, arg2 ) ... end, number_of_generators := function( arg1 ) ... end, number_of_idempotents := function( arg1 ) ... end, position := function( arg1, arg2 ) ... end, - position_to_sorted_position := function( arg1, arg2 ) ... end, prefix := function( arg1, arg2 ) ... end, right_cayley_graph := function( arg1 ) ... end, rules := function( arg1 ) ... end, size := function( arg1 ) ... end, sorted_at := function( arg1, arg2 ) ... end, sorted_position := function( arg1, arg2 ) ... end, - suffix := function( arg1, arg2 ) ... end ) + suffix := function( arg1, arg2 ) ... end, + to_sorted_position := function( arg1, arg2 ) ... end ) gap> FroidurePinMemFnRec(Semigroup(Matrix(IsProjectiveMaxPlusMatrix, [[1]]))); rec( add_generator := function( arg1, arg2 ) ... end, at := function( arg1, arg2 ) ... end, @@ -290,13 +290,13 @@ rec( add_generator := function( arg1, arg2 ) ... end, number_of_generators := function( arg1 ) ... end, number_of_idempotents := function( arg1 ) ... end, position := function( arg1, arg2 ) ... end, - position_to_sorted_position := function( arg1, arg2 ) ... end, prefix := function( arg1, arg2 ) ... end, right_cayley_graph := function( arg1 ) ... end, rules := function( arg1 ) ... end, size := function( arg1 ) ... end, sorted_at := function( arg1, arg2 ) ... end, sorted_position := function( arg1, arg2 ) ... end, - suffix := function( arg1, arg2 ) ... end ) + suffix := function( arg1, arg2 ) ... end, + to_sorted_position := function( arg1, arg2 ) ... end ) # HasLibsemigroupsFroidurePin gap> S := FullTransformationMonoid(2); From fa976b9270186c1983da508bc970254d61032ee3 Mon Sep 17 00:00:00 2001 From: James Mitchell Date: Sat, 20 Dec 2025 18:22:25 +0100 Subject: [PATCH 16/27] TMP --- gap/libsemigroups/cong.gi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gap/libsemigroups/cong.gi b/gap/libsemigroups/cong.gi index b14cf1533..bb7eab220 100644 --- a/gap/libsemigroups/cong.gi +++ b/gap/libsemigroups/cong.gi @@ -96,7 +96,7 @@ function(C) fi; CC := libsemigroups.Congruence.make(kind, p); Factorize2Args := Factorization; - # TODO needed + # TODO needed? #elif IsQuotientSemigroup(S) then # return QuotientSemigroupCongruence(S)!.LibsemigroupsCongruence; elif CanUseLibsemigroupsFroidurePin(S) then From 22b35bc23d3250345d4aa64c0af7319e73173749 Mon Sep 17 00:00:00 2001 From: James Mitchell Date: Sat, 27 Dec 2025 13:46:31 +0000 Subject: [PATCH 17/27] TMP --- gap/libsemigroups/cong.gi | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/gap/libsemigroups/cong.gi b/gap/libsemigroups/cong.gi index bb7eab220..8438793fa 100644 --- a/gap/libsemigroups/cong.gi +++ b/gap/libsemigroups/cong.gi @@ -84,7 +84,7 @@ function(C) fi; Unbind(C!.LibsemigroupsCongruence); - S := Range(C); + S := Range(C); kind := CongruenceHandednessString(C); if IsFpSemigroup(S) or (HasIsFreeSemigroup(S) and IsFreeSemigroup(S)) @@ -96,9 +96,6 @@ function(C) fi; CC := libsemigroups.Congruence.make(kind, p); Factorize2Args := Factorization; - # TODO needed? - #elif IsQuotientSemigroup(S) then - # return QuotientSemigroupCongruence(S)!.LibsemigroupsCongruence; elif CanUseLibsemigroupsFroidurePin(S) then Enumerate(S); fp := LibsemigroupsFroidurePin(S); @@ -107,6 +104,7 @@ function(C) elif kind = "right" then CC := libsemigroups.froidure_pin_to_right_congruence(fp); else + Error(); CC := libsemigroups.froidure_pin_to_2_sided_congruence(fp); fi; Factorize2Args := MinimalFactorization; From 6f7150410a258e5ed945c82ff73c4ff896b26846 Mon Sep 17 00:00:00 2001 From: James Mitchell Date: Sat, 27 Dec 2025 14:24:44 +0000 Subject: [PATCH 18/27] Standard tests pass --- gap/libsemigroups/cong.gi | 16 ++++++++++++++-- src/pkg.cpp | 7 +++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/gap/libsemigroups/cong.gi b/gap/libsemigroups/cong.gi index 8438793fa..28c5cc635 100644 --- a/gap/libsemigroups/cong.gi +++ b/gap/libsemigroups/cong.gi @@ -96,7 +96,7 @@ function(C) fi; CC := libsemigroups.Congruence.make(kind, p); Factorize2Args := Factorization; - elif CanUseLibsemigroupsFroidurePin(S) then + elif CanUseLibsemigroupsFroidurePin(S) and not IsQuotientSemigroup(S) then Enumerate(S); fp := LibsemigroupsFroidurePin(S); if kind = "left" then @@ -104,10 +104,22 @@ function(C) elif kind = "right" then CC := libsemigroups.froidure_pin_to_right_congruence(fp); else - Error(); CC := libsemigroups.froidure_pin_to_2_sided_congruence(fp); fi; Factorize2Args := MinimalFactorization; + elif CanUseLibsemigroupsFroidurePin(S) and IsQuotientSemigroup(S) then + Enumerate(S); + fp := LibsemigroupsFroidurePin(S); + if kind = "left" then + # TODO impl + CC := libsemigroups.shared_ptr_froidure_pin_to_left_congruence(fp); + elif kind = "right" then + # TODO impl + CC := libsemigroups.shared_ptr_froidure_pin_to_right_congruence(fp); + else + CC := libsemigroups.shared_ptr_froidure_pin_to_2_sided_congruence(fp); + fi; + Factorize2Args := MinimalFactorization; elif CanUseGapFroidurePin(S) then RUN_FROIDURE_PIN(GapFroidurePin(S), -1, InfoLevel(InfoSemigroups) > 0); CC := libsemigroups.gap_froidure_pin_to_congruence(kind, GapFroidurePin(S)); diff --git a/src/pkg.cpp b/src/pkg.cpp index 84a2068b5..e96f06ff8 100644 --- a/src/pkg.cpp +++ b/src/pkg.cpp @@ -185,6 +185,13 @@ GAPBIND14_MODULE(libsemigroups) { congruence_kind::twosided, fpb, fpb.right_cayley_graph()); }); + gapbind14::InstallGlobalFunction( + "shared_ptr_froidure_pin_to_2_sided_congruence", + [](std::shared_ptr& fpb) { + return libsemigroups::to>( + congruence_kind::twosided, *fpb, fpb->right_cayley_graph()); + }); + gapbind14::InstallGlobalFunction( "gap_froidure_pin_to_congruence", [](Obj kind_str_obj, Obj gap_fp) { std::string kind_str(CSTR_STRING(kind_str_obj)); From 8cbd59c9e62a0da42eb75e17ceeb052f6245e459 Mon Sep 17 00:00:00 2001 From: James Mitchell Date: Sat, 27 Dec 2025 14:57:03 +0000 Subject: [PATCH 19/27] Linting --- gap/libsemigroups/cong.gi | 12 +++++++----- src/pkg.cpp | 2 ++ src/to_gap.hpp | 1 + 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/gap/libsemigroups/cong.gi b/gap/libsemigroups/cong.gi index 28c5cc635..9febd011e 100644 --- a/gap/libsemigroups/cong.gi +++ b/gap/libsemigroups/cong.gi @@ -111,10 +111,10 @@ function(C) Enumerate(S); fp := LibsemigroupsFroidurePin(S); if kind = "left" then - # TODO impl + # TODO impl CC := libsemigroups.shared_ptr_froidure_pin_to_left_congruence(fp); elif kind = "right" then - # TODO impl + # TODO impl CC := libsemigroups.shared_ptr_froidure_pin_to_right_congruence(fp); else CC := libsemigroups.shared_ptr_froidure_pin_to_2_sided_congruence(fp); @@ -266,9 +266,11 @@ function(C) if IsFinite(S) then words := List(S, x -> reverse(Factorization(S, x) - 1)); ntc := libsemigroups.congruence_non_trivial_classes(CC, words) + 1; - elif IsFpSemigroup(S) or IsFreeSemigroup(S) or IsFpMonoid(S) or IsFreeMonoid(S) then + elif IsFpSemigroup(S) or IsFreeSemigroup(S) + or IsFpMonoid(S) or IsFreeMonoid(S) then super := LibsemigroupsCongruence(UnderlyingCongruence(S)); - ntc := libsemigroups.infinite_congruence_non_trivial_classes(super, CC) + 1; + ntc := libsemigroups.infinite_congruence_non_trivial_classes( + super, CC) + 1; else TryNextMethod(); fi; @@ -351,7 +353,7 @@ InstallMethod(EquivalenceRelationPartitionWithSingletons, [CanUseLibsemigroupsCongruence and HasGeneratingPairsOfLeftRightOrTwoSidedCongruence], function(C) - local map, next, part, CC, word, i, x; + local map, next, part, word, i, x; if not IsFinite(Range(C)) then ErrorNoReturn("the argument (a congruence) must have finite range"); diff --git a/src/pkg.cpp b/src/pkg.cpp index e96f06ff8..ec1c6f67b 100644 --- a/src/pkg.cpp +++ b/src/pkg.cpp @@ -25,8 +25,10 @@ #include // for size_t #include // for exception #include // for string +#include // for shared_ptr #include // for set #include +#include // for string #include // for conditional<>::type #include // for unordered_map #include // for swap diff --git a/src/to_gap.hpp b/src/to_gap.hpp index 86f5fed3f..85161c069 100644 --- a/src/to_gap.hpp +++ b/src/to_gap.hpp @@ -28,6 +28,7 @@ #include // for size_t #include // for uint32_t #include // for numeric_limits +#include // for string #include // for enable_if_t, decay_t, is_same #include // for vector From 4b6dfc72280008405d7c386ad1c43922a88d5643 Mon Sep 17 00:00:00 2001 From: James Mitchell Date: Wed, 14 Jan 2026 13:48:03 +0000 Subject: [PATCH 20/27] Fix testinstall.tst --- doc/cong.xml | 8 ++++---- gap/tools/utils.gi | 1 + tst/testinstall.tst | 14 +++++++------- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/doc/cong.xml b/doc/cong.xml index 0f2219bd1..68574cac7 100644 --- a/doc/cong.xml +++ b/doc/cong.xml @@ -556,10 +556,10 @@ gap> cong := SemigroupCongruence(S, [Transformation([1, 2, 1]), > Transformation([2, 1, 2])]);; gap> classes := NonTrivialEquivalenceClasses(cong);; gap> Set(classes); -[ <2-sided congruence class of Transformation( [ 1, 2, 2 ] )>, - <2-sided congruence class of Transformation( [ 3, 1, 3 ] )>, - <2-sided congruence class of Transformation( [ 3, 1, 1 ] )>, - <2-sided congruence class of Transformation( [ 2, 1, 2 ] )>, +[ <2-sided congruence class of Transformation( [ 1, 2, 2 ] )>, + <2-sided congruence class of Transformation( [ 3, 1, 1 ] )>, + <2-sided congruence class of Transformation( [ 3, 1, 3 ] )>, + <2-sided congruence class of Transformation( [ 2, 1, 2 ] )>, <2-sided congruence class of Transformation( [ 3, 3, 3 ] )> ] gap> cong := RightSemigroupCongruence(S, [Transformation([1, 2, 1]), > Transformation([2, 1, 2])]);; diff --git a/gap/tools/utils.gi b/gap/tools/utils.gi index 419e9e953..283b17a2e 100644 --- a/gap/tools/utils.gi +++ b/gap/tools/utils.gi @@ -242,6 +242,7 @@ function(arg...) elif IsRecord(arg[1]) and not IsBound(arg[1].showProgress) then arg[1].showProgress := "some"; fi; + arg[1].compareFunction := "uptowhitespace"; return SEMIGROUPS.TestDir(DirectoriesPackageLibrary("semigroups", "tst/extreme/"), arg); diff --git a/tst/testinstall.tst b/tst/testinstall.tst index bd31a9d64..2cfb812ac 100644 --- a/tst/testinstall.tst +++ b/tst/testinstall.tst @@ -910,43 +910,43 @@ gap> x := ReesZeroMatrixSemigroupElement(R, 1, (1, 3), 1);; gap> y := ReesZeroMatrixSemigroupElement(R, 1, (), 1);; gap> cong := SemigroupCongruenceByGeneratingPairs(R, [[x, y]]);; gap> c := Set(EquivalenceClasses(cong)); -[ <2-sided congruence class of (1,(),1)>, +[ <2-sided congruence class of (1,(),1)>, <2-sided congruence class of 0>, <2-sided congruence class of (1,(),2)>, <2-sided congruence class of (1,(),3)>, <2-sided congruence class of (1,(),4)>, <2-sided congruence class of (1,(),5)>, <2-sided congruence class of (1,(),6)>, <2-sided congruence class of (2,(),1)>, - <2-sided congruence class of (3,(),1)>, - <2-sided congruence class of (4,(),1)>, - <2-sided congruence class of (5,(),1)>, - <2-sided congruence class of (6,(),1)>, - <2-sided congruence class of (7,(),1)>, <2-sided congruence class of 0>, <2-sided congruence class of (2,(),2)>, <2-sided congruence class of (2,(),3)>, <2-sided congruence class of (2,(),4)>, <2-sided congruence class of (2,(),5)>, <2-sided congruence class of (2,(),6)>, + <2-sided congruence class of (3,(),1)>, <2-sided congruence class of (3,(),2)>, <2-sided congruence class of (3,(),3)>, <2-sided congruence class of (3,(),4)>, <2-sided congruence class of (3,(),5)>, <2-sided congruence class of (3,(),6)>, + <2-sided congruence class of (4,(),1)>, <2-sided congruence class of (4,(),2)>, <2-sided congruence class of (4,(),3)>, <2-sided congruence class of (4,(),4)>, <2-sided congruence class of (4,(),5)>, <2-sided congruence class of (4,(),6)>, + <2-sided congruence class of (5,(),1)>, <2-sided congruence class of (5,(),2)>, <2-sided congruence class of (5,(),3)>, <2-sided congruence class of (5,(),4)>, <2-sided congruence class of (5,(),5)>, <2-sided congruence class of (5,(),6)>, + <2-sided congruence class of (6,(),1)>, <2-sided congruence class of (6,(),2)>, <2-sided congruence class of (6,(),3)>, <2-sided congruence class of (6,(),4)>, <2-sided congruence class of (6,(),5)>, <2-sided congruence class of (6,(),6)>, + <2-sided congruence class of (7,(),1)>, <2-sided congruence class of (7,(),2)>, <2-sided congruence class of (7,(),3)>, <2-sided congruence class of (7,(),4)>, @@ -1580,7 +1580,7 @@ gap> s := f / rel;; gap> sgns := GeneratorsOfSemigroup(s);; gap> c := SemigroupCongruenceByGeneratingPairs(s, [[sgns[1], sgns[2]]]);; gap> EquivalenceRelationPartition(c); -[ [ s1, s2, s1*s2, s2^2, s1*s2^2 ] ] +[ [ s2, s1*s2, s2^2, s1*s2^2, s1 ] ] gap> ## gap> ## Check to see if elements are in the partition gap> ## true and false From cf5a19585c4a661e0b63cf5cf4195267e436cb3d Mon Sep 17 00:00:00 2001 From: James Mitchell Date: Thu, 15 Jan 2026 10:35:22 +0000 Subject: [PATCH 21/27] Remove docs and test for IteratorOfLClassReps which no longer exists --- doc/greens-generic.xml | 1 - tst/extreme/inverse.tst | 7 ------- 2 files changed, 8 deletions(-) diff --git a/doc/greens-generic.xml b/doc/greens-generic.xml index a13f50794..86d8d9327 100644 --- a/doc/greens-generic.xml +++ b/doc/greens-generic.xml @@ -626,7 +626,6 @@ false]]> IteratorOfXClassReps - An iterator. diff --git a/tst/extreme/inverse.tst b/tst/extreme/inverse.tst index 2bd0112ac..b01d727a4 100644 --- a/tst/extreme/inverse.tst +++ b/tst/extreme/inverse.tst @@ -851,14 +851,7 @@ gap> s := RandomInverseSemigroup(IsPartialPermSemigroup, 2, 20);; gap> iter := IteratorOfDClassReps(s); gap> s := RandomInverseSemigroup(IsPartialPermSemigroup, 2, 100);; -gap> iter := IteratorOfLClassReps(s); -Error, Variable: 'IteratorOfLClassReps' must have a value -gap> for i in [1 .. 10000] do NextIterator(iter); od; -Error, is exhausted gap> s := RandomInverseSemigroup(IsPartialPermSemigroup, 2, 10);; -gap> iter := IteratorOfLClassReps(s); -Error, Variable: 'IteratorOfLClassReps' must have a value -gap> for i in iter do od; gap> iter := IteratorOfDClassReps(s); gap> for i in iter do od; From 0dc93ae2e5a3741cdc630559f4333d014e24168b Mon Sep 17 00:00:00 2001 From: James Mitchell Date: Thu, 15 Jan 2026 10:45:30 +0000 Subject: [PATCH 22/27] Fix extreme tests --- tst/extreme/maximal.tst | 2 +- tst/extreme/semigroups.tst | 17 +++++++++-------- tst/extreme/semirms.tst | 18 +++++++++--------- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/tst/extreme/maximal.tst b/tst/extreme/maximal.tst index 0a675ca96..9154d9220 100644 --- a/tst/extreme/maximal.tst +++ b/tst/extreme/maximal.tst @@ -795,7 +795,7 @@ gap> correct := [ > Bipartition([[1, -1], [2, -5], [3, -2], [4, 5, 7, -3, -6, -7], [6, -4]]), > Bipartition([[1, -6], [2, -2], [3, 6, 7, -1, -5, -7], [4, -4], > [5, -3]])])];; -gap> max = correct; +gap> Set(max) = Set(correct); true # MaximalTest10: MaximalSubsemigroups for a transformation semigroup ideal diff --git a/tst/extreme/semigroups.tst b/tst/extreme/semigroups.tst index f23a8dee0..460cfcb29 100644 --- a/tst/extreme/semigroups.tst +++ b/tst/extreme/semigroups.tst @@ -9,7 +9,7 @@ ## -#@local S, acting, f, g, gens, i, inv, iso, s, small, u, x, y, z +#@local S, acting, f, g, gens, i, inv, iso, s, small, u, x, y, z, map, h gap> START_TEST("Semigroups package: extreme/semigroups.tst"); gap> LoadPackage("semigroups", false);; @@ -181,18 +181,19 @@ gap> GeneratorsOfMonoid(s); [4,10][7,1,3](2)(5)(9) ] # SemigroupsTest3: Dihedral (perm) group to a partial perm semigroup -gap> g := DihedralGroup(8);; -gap> g := Range(IsomorphismPermGroup(g)); -Group([ (1,2)(3,8)(4,6)(5,7), (1,3,4,7)(2,5,6,8), (1,4)(2,6)(3,7)(5,8) ]) +gap> h := DihedralGroup(8);; +gap> map := IsomorphismPermGroup(h);; +gap> g := Range(map); +Group([ (2,4), (1,2,3,4), (1,3)(2,4) ]) gap> iso := IsomorphismPartialPermSemigroup(g);; gap> Range(iso); - + gap> inv := InverseGeneralMapping(iso);; -gap> f := (1, 5)(2, 3)(4, 8)(6, 7);; +gap> f := (h.1) ^ map;; gap> f ^ iso; -(1,5)(2,3)(4,8)(6,7) +(1)(2,4)(3) gap> (f ^ iso) ^ inv; -(1,5)(2,3)(4,8)(6,7) +(2,4) gap> ForAll(g, f -> (f ^ iso) ^ inv = f); true gap> Size(Range(iso)); diff --git a/tst/extreme/semirms.tst b/tst/extreme/semirms.tst index 63dd39466..8aedcb595 100644 --- a/tst/extreme/semirms.tst +++ b/tst/extreme/semirms.tst @@ -84,10 +84,10 @@ gap> R := PrincipalFactor(DClasses(S)[40]); gap> U := MaximalSubsemigroups(R){[31 .. 36]}; [ , , - , - , , - ] + , + , + ] gap> V := Semigroup(MultiplicativeZero(R), > RMSElement(R, 13, (1, 6)(5, 8), 3), > RMSElement(R, 1, (1, 6), 3), @@ -172,10 +172,10 @@ gap> iso := IsomorphismPermGroup(T);; gap> Source(iso) = T; true gap> Range(iso); -Group([ (1,5,3,7)(2,8,4,6), (1,2,3,4)(5,6,7,8) ]) +Group([ (1,3,6,8)(2,5,7,4), (1,4,6,5)(2,3,7,8) ]) gap> inv := InverseGeneralMapping(iso);; gap> Source(inv); -Group([ (1,5,3,7)(2,8,4,6), (1,2,3,4)(5,6,7,8) ]) +Group([ (1,3,6,8)(2,5,7,4), (1,4,6,5)(2,3,7,8) ]) gap> Range(inv) = T; true gap> ForAll(T, x -> (x ^ iso) ^ inv = x); @@ -248,9 +248,9 @@ gap> e = h; false gap> ForAll(H, x -> x * e = x and e * x = x); true -gap> h := RMSElement(ParentAttr(U[5]), 21, (1, 9, 6)(5, 8), 5); +gap> h := RMSElement(ParentAttr(U[3]), 21, (1, 9, 6)(5, 8), 5); (21,(1,9,6)(5,8),5) -gap> H := GreensHClassOfElement(U[5], h); +gap> H := GreensHClassOfElement(U[3], h); Error, the element does not belong to the semigroup gap> IsRegularGreensClass(H); true @@ -617,8 +617,8 @@ gap> AutomorphismGroup(R); gap> Size(last); 12 gap> G := PcGroupCode(38841464902638102313468315, 256);; # SmallGroup(256, 4) -gap> AssignGeneratorsVariables(G); -gap> y := f2 * f3 * f4 * f5 * f6 * f7;; +gap> AssignGeneratorVariables(G); +gap> y := G.2 * G.3 * G.4 * G.5 * G.6 * G.7;; gap> iso := IsomorphismPermGroup(G);; gap> G := Range(iso);; gap> y := y ^ iso;; From 767378ded05ea48c226a9dd9759a497520b2c8d4 Mon Sep 17 00:00:00 2001 From: James Mitchell Date: Thu, 15 Jan 2026 10:45:40 +0000 Subject: [PATCH 23/27] TODO -> DONE --- gap/libsemigroups/sims1.gi | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/gap/libsemigroups/sims1.gi b/gap/libsemigroups/sims1.gi index c0cc50b09..5c5c14d88 100644 --- a/gap/libsemigroups/sims1.gi +++ b/gap/libsemigroups/sims1.gi @@ -11,10 +11,9 @@ DeclareOperation("LibsemigroupsSims1", [IsSemigroup, IsPosInt, IsList, IsString]); -# TODO rename extra -> included InstallMethod(LibsemigroupsSims1, [IsSemigroup, IsPosInt, IsList, IsString], -function(S, n, extra, kind) +function(S, n, included, kind) local rules, reverse, P, sims1, r, pair; Assert(1, @@ -24,7 +23,8 @@ function(S, n, extra, kind) or (HasIsFreeSemigroup(S) and IsFreeSemigroup(S)) or (HasIsFreeMonoid(S) and IsFreeMonoid(S))); - Assert(1, IsEmpty(extra) or IsMultiplicativeElementCollColl(extra)); + Assert(1, IsEmpty(included) or + IsMultiplicativeElementCollColl(included)); Assert(1, kind in ["left", "right"]); @@ -73,13 +73,11 @@ function(S, n, extra, kind) libsemigroups.Presentation.throw_if_bad_alphabet_or_rules(P); sims1 := libsemigroups.Sims1.make(P); - if not IsEmpty(extra) then - for pair in extra do + for pair in included do libsemigroups.sims1_add_included_pair(sims1, - reverse(MinimalFactorization(S, pair[1]) - 1), - reverse(MinimalFactorization(S, pair[2]) - 1)); - od; - fi; + reverse(MinimalFactorization(S, pair[1]) - 1), + reverse(MinimalFactorization(S, pair[2]) - 1)); + od; libsemigroups.Sims1.cbegin_long_rules(sims1, 2 * Length(rules)); From 80fb5bfd213977fc0d2e7558fea1a31bf8c6895a Mon Sep 17 00:00:00 2001 From: James Mitchell Date: Thu, 15 Jan 2026 11:13:57 +0000 Subject: [PATCH 24/27] Format --- GNUmakefile.in | 2 +- src/conglatt.cpp | 7 +++---- src/pkg.cpp | 6 ++++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/GNUmakefile.in b/GNUmakefile.in index 2015db247..03d65ea9a 100644 --- a/GNUmakefile.in +++ b/GNUmakefile.in @@ -103,7 +103,7 @@ lint: etc/cpplint.sh format: - clang-format -i src/*.[hc] + clang-format -i src/*.*pp .PHONY: lint format diff --git a/src/conglatt.cpp b/src/conglatt.cpp index 1a7c74991..ef1ada48a 100644 --- a/src/conglatt.cpp +++ b/src/conglatt.cpp @@ -41,10 +41,9 @@ #include "semigroups-debug.hpp" // for SEMIGROUPS_ASSERT // libsemigroups headers -#include "libsemigroups/adapters.hpp" // for Hash -#include "libsemigroups/detail/report.hpp" // for should_report -#include "libsemigroups/detail/string.hpp" // for group_digits - // +#include "libsemigroups/adapters.hpp" // for Hash +#include "libsemigroups/detail/report.hpp" // for should_report +#include "libsemigroups/detail/string.hpp" // for group_digits namespace semigroups { namespace { diff --git a/src/pkg.cpp b/src/pkg.cpp index ec1c6f67b..7fbb821f5 100644 --- a/src/pkg.cpp +++ b/src/pkg.cpp @@ -591,8 +591,10 @@ static StructGVarFilt GVarFilts[] = { /*****************************************************************************/ -#define GVAR_ENTRY(srcfile, name, nparam, params) \ - {#name, nparam, params, (ObjFunc) name, srcfile ":Func" #name} +#define GVAR_ENTRY(srcfile, name, nparam, params) \ + { \ +#name, nparam, params, (ObjFunc) name, srcfile ":Func" #name \ + } // Table of functions to export From d2363a3e70de4f452f5e06b8c97921c70199e945 Mon Sep 17 00:00:00 2001 From: James Mitchell Date: Thu, 15 Jan 2026 11:14:36 +0000 Subject: [PATCH 25/27] Bump libsemigroups -> v3.4.0 --- .LIBSEMIGROUPS_VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.LIBSEMIGROUPS_VERSION b/.LIBSEMIGROUPS_VERSION index a4dd9dba4..18091983f 100644 --- a/.LIBSEMIGROUPS_VERSION +++ b/.LIBSEMIGROUPS_VERSION @@ -1 +1 @@ -2.7.4 +3.4.0 From 4ab54959a05e168f1e1aab32393f439e8107d1a4 Mon Sep 17 00:00:00 2001 From: James Mitchell Date: Thu, 15 Jan 2026 11:16:56 +0000 Subject: [PATCH 26/27] Bump libsemigroups -> v3.4.0 in environment.yml --- environment.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/environment.yml b/environment.yml index 2decd4872..622ebc1b9 100644 --- a/environment.yml +++ b/environment.yml @@ -4,4 +4,4 @@ channels: - conda-forge dependencies: - - libsemigroups==2.7.4 + - libsemigroups==3.4.0 From c7338dcb9258d15e58663d59a1458a5f80449418 Mon Sep 17 00:00:00 2001 From: Joseph Edwards Date: Thu, 22 Jan 2026 03:16:05 +0000 Subject: [PATCH 27/27] Fix overload_cast issue --- src/pkg.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/pkg.cpp b/src/pkg.cpp index 7fbb821f5..e5998595d 100644 --- a/src/pkg.cpp +++ b/src/pkg.cpp @@ -314,10 +314,12 @@ GAPBIND14_MODULE(libsemigroups) { gapbind14::overload_cast&, size_t>( &libsemigroups::presentation::add_identity_rules)); + // Because reverse has two overloads (one for rvalue reference, and one for + // lvalue reference) we use a lambda here instead of overload_cast. gapbind14::InstallGlobalFunction( - "presentation_reverse", - gapbind14::overload_cast&>( - &libsemigroups::presentation::reverse)); + "presentation_reverse", [](Presentation& thing) -> void { + libsemigroups::presentation::reverse(thing); + }); gapbind14::InstallGlobalFunction( "presentation_normalize_alphabet",