libsemigroups  v3.5.5
C++ library for semigroups and monoids
Loading...
Searching...
No Matches
schreier-sims.hpp
1//
2// libsemigroups - C++ library for semigroups and monoids
3// Copyright (C) 2019-2026 James D. Mitchell
4//
5// This program is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with this program. If not, see <http://www.gnu.org/licenses/>.
17//
18
19// TODO(later)
20//
21// 0. check use of:
22// * element_type []
23// * const_value_type []
24// * reference []
25// * const_element_reference []
26// * internal_element_type []
27// * internal_const_element_type []
28// * internal_reference []
29// * internal_const_reference []
30// 1. iterator to the elements (Finn requires this)
31// 2. stabilizer member func
32// 3. change base
33// 4. random version
34// 5. try it with Digraphs
35
36// TODO:
37// * code coverage (can't do it on MacBook Pro at present)
38
39#ifndef LIBSEMIGROUPS_SCHREIER_SIMS_HPP_
40#define LIBSEMIGROUPS_SCHREIER_SIMS_HPP_
41
42#include <array> // for array
43#include <cstddef> // for size_t
44#include <cstdint> // for uint64_t
45#include <iterator> // for distance
46#include <limits> // for numeric_limits
47#include <memory> // for make_unique
48#include <string> // for operator+, basic_string
49#include <string_view> // for string_view
50#include <type_traits> // for is_same
51#include <unordered_set> // for unordered_set
52
53#include "adapters.hpp" // for action, degree, inverse
54#include "config.hpp" // for LIBSEMIGROUPS_HPCOMBI_ENABLED
55#include "debug.hpp" // for LIBSEMIGROUPS_ASSERT
56#include "exception.hpp" // for LIBSEMIGROUPS_EXCEPTION
57#include "transf.hpp" // for Perm
58#include "types.hpp" // for SmallestInteger
59
60#include "detail/bruidhinn-traits.hpp" // for BruidhinnTraits
61#include "detail/containers.hpp" // for Array2, StaticTriVector2
62#include "detail/fmt.hpp" // for format
63#include "detail/int-range.hpp" // for IntRange
64#include "detail/stl.hpp" // for has_call_operator_size_t_v
65
66namespace libsemigroups {
67
82
99 template <size_t N, typename Point, typename Element>
143
173 template <size_t N,
174 typename Point = typename SmallestInteger<N>::type,
175 typename Element = LeastPerm<N>,
177 class SchreierSims : private detail::BruidhinnTraits<Element> {
178 static_assert(std::is_same_v<Point, typename Traits::point_type>,
179 "incompatible point types, Traits::point_type and Point "
180 "must be the same");
181 static_assert(
182 std::is_same_v<Element, typename Traits::element_type>,
183 "incompatible element types, Traits::element_type and Element "
184 "must be the same");
185 static_assert(N <= std::numeric_limits<Point>::max(),
186 "the first template parameter N cannot be expressed as value "
187 "of type equal to the second template parameter Point");
188
189 using internal_element_type =
190 typename detail::BruidhinnTraits<Element>::internal_value_type;
191 using internal_reference =
192 typename detail::BruidhinnTraits<Element>::internal_reference;
193 using internal_const_element_type =
194 typename detail::BruidhinnTraits<Element>::internal_const_value_type;
195
196 public:
202 using element_type = typename detail::BruidhinnTraits<Element>::value_type;
203
209 typename detail::BruidhinnTraits<Element>::const_reference;
210
215 typename detail::BruidhinnTraits<Element>::reference;
216
220 using point_type = Point;
221
225 using domain_type = typename Traits::domain_type;
226
230 using index_type = typename Traits::index_type;
231
235 using Action = typename Traits::Action;
236
238 using Degree = typename Traits::Degree;
239
241 using EqualTo = typename Traits::EqualTo;
242
244 using Inverse = typename Traits::Inverse;
245
247 using One = typename Traits::One;
248
250 using Product = typename Traits::Product;
251
253 using Swap = typename Traits::Swap;
254
255 static_assert(detail::has_call_operator_size_t_v<One>,
256 "Traits::One must implement operator()(size_t)!");
257
258 private:
259 // TODO replace Array2 by TriArray2 everywhere below
261 index_type _base_size;
262 domain_type _domain;
263 bool _finished;
264 internal_element_type _one;
265 detail::StaticTriVector2<point_type, N> _orbits;
266 detail::Array2<bool, N> _orbits_lookup;
267 detail::StaticTriVector2<internal_element_type, N> _strong_gens;
268 mutable internal_element_type _tmp_element1;
269 mutable internal_element_type _tmp_element2;
270 detail::Array2<internal_element_type, N> _transversal;
271 detail::Array2<internal_element_type, N> _inversal;
272
273 public:
284
296
298
303
307 // TODO this requires more tests
309
313 // TODO fix in the same way as the move constructor above
315
319 // TODO this requires more tests
321
342
361
376 // Not noexcept because strong_generator isn't
377 [[nodiscard]] const_element_reference generator(index_type index) const {
378 return strong_generator(0, index);
379 }
380
395 // Not noexcept because strong_generator_no_checks isn't
396 [[nodiscard]] const_element_reference
398 return strong_generator_no_checks(0, index);
399 }
400
413 // Not noexcept because number_of_strong_generators_no_checks is not.
414 [[nodiscard]] size_t number_of_generators() const;
415
430 [[nodiscard]] size_t number_of_strong_generators(index_type depth) const {
431 throw_if_bad_depth(depth);
433 }
434
453 // Not noexcept because StaticTriVector2::size(size_t) is not.
454 [[nodiscard]] size_t
456 return _strong_gens.size(depth);
457 }
458
475 [[nodiscard]] const_element_reference
477
498 // not noexcept because StaticTriVector2 isn't
499 [[nodiscard]] const_element_reference
501 return this->to_external_const(_strong_gens.at(depth, index));
502 }
503
524 // not noexcept because Array2::operator[] isn't
525 [[nodiscard]] const_element_reference
527 return this->to_external_const(_transversal[depth][pt]);
528 }
529
547 // Not noexcept because throws
548 [[nodiscard]] const_element_reference
550
571 // Not noexcept because std::array::operator[] isn't
572 [[nodiscard]] const_element_reference
574 point_type pt) const {
575 return this->to_external_const(_inversal[depth][pt]);
576 }
577
596 [[nodiscard]] const_element_reference
598
619 // Not noexcept because std::array::operator[] isn't
620 [[nodiscard]] bool orbit_lookup_no_checks(index_type depth,
621 point_type pt) const {
622 return _orbits_lookup[depth][pt];
623 }
624
641 [[nodiscard]] bool orbit_lookup(index_type depth, point_type pt) const {
642 throw_if_bad_depth(depth);
643 throw_if_point_gt_degree(pt);
644 return orbit_lookup_no_checks(depth, pt);
645 }
646
659 // Not noexcept because StaticTriVector2::size isn't
660 [[nodiscard]] bool empty() const {
661 return _strong_gens.size(0) == 0;
662 }
663
672 // not noexcept because run isn't
673 [[nodiscard]] uint64_t size();
674
685 [[nodiscard]] uint64_t current_size() const;
686
696 // TODO tests
697 // not noexcept because internal_sift isn't
699 // changes x in place, and uses _tmp_element1
700 internal_sift(this->to_internal(x));
701 }
702
712 // TODO tests
713 // not noexcept because can throw
715 throw_if_bad_degree(x);
717 }
718
730 // not noexcept because internal_sift isn't
732 this->to_external(_tmp_element2) = x;
733 // changes _tmp_element2 in place, and uses _tmp_element1
734 internal_sift(_tmp_element2);
735 return this->to_external_const(_tmp_element2);
736 }
737
749 // not noexcept because can throw
750 [[nodiscard]] const_element_reference
752 throw_if_bad_degree(x);
753 return sift_no_checks(x);
754 }
755
770 // At present JDM thinks this will always return false if finished()
771 // isn't true, because we either run to the end or haven't done anything at
772 // all.
773 // not noexcept because sift_no_checks isn't
774 [[nodiscard]] bool currently_contains(const_element_reference x) const;
775
790 [[nodiscard]] bool contains(const_element_reference x);
791
800 [[nodiscard]] const_element_reference one() const
801 noexcept(noexcept(this->to_external_const(_one))) {
802 return this->to_external_const(_one);
803 }
804
818 [[nodiscard]] bool finished() const noexcept {
819 return _finished;
820 }
821
834 // TODO rename to add_basepoint?
835 // not noexcept because can throw
837
854 // Not noexcept because std::array::operator[] isn't
855 [[nodiscard]] point_type base_no_checks(index_type index) const {
856 return _base[index];
857 }
858
871 // not noexcept because can throw
872 [[nodiscard]] point_type base(index_type index) const {
873 throw_if_bad_depth(index);
874 return base_no_checks(index);
875 }
876
889 [[nodiscard]] size_t base_size() const noexcept {
890 return _base_size;
891 }
892
904 // not noexcept because it can call mem fns that aren't
905 void run();
906
907 private:
909 // SchreierSims - validation - private
911
912 bool is_valid_degree(point_type x) const noexcept {
913 return
914#ifdef LIBSEMIGROUPS_HPCOMBI_ENABLED
915 std::is_same_v<HPCombi::Perm16, element_type> ||
916#endif
917 x == N;
918 }
919
920 void throw_if_bad_degree(const_element_reference x,
921 std::string_view arg_pos = "1st") const;
922
923 void throw_if_bad_depth(size_t depth,
924 std::string_view arg_pos = "1st") const;
925
926 void throw_if_point_gt_degree(point_type pt,
927 std::string_view arg_pos = "1st") const;
928
929 void throw_if_point_not_in_orbit(index_type depth,
930 point_type pt,
931 std::string_view depth_arg_pos = "1st",
932 std::string_view pt_arg_pos = "2nd") const;
933
935 // SchreierSims - member functions - private
937
938 bool internal_equal_to(internal_const_element_type x,
939 internal_const_element_type y) const
940 noexcept(noexcept(
941 EqualTo()(this->to_external_const(x),
942 this->to_external_const(
943 y)) && noexcept(this->to_external_const(x)))) {
944 return EqualTo()(this->to_external_const(x), this->to_external_const(y));
945 }
946
947 // Used by copy constructor and assignment operator.
948 void init_strong_gens_traversal_inversal(SchreierSims const& that);
949 void free_strong_gens_traversal_inversal();
950
951 void internal_add_base_point(point_type pt);
952 void orbit_enumerate(index_type depth, index_type first = 0);
953 void orbit_add_gen(index_type depth, internal_element_type gen);
954 void orbit_add_point(index_type depth,
955 internal_element_type x,
956 point_type pt);
957 // Changes _tmp_element2 in-place, and returns the depth reached in the
958 // sifting.
959 index_type internal_sift(internal_reference x) const;
960
961 typename domain_type::const_iterator
962 first_non_fixed_point(internal_const_element_type x) const {
963 auto const& y = this->to_external_const(x);
964 return std::find_if(_domain.cbegin(), _domain.cend(), [&y](auto pt) {
965 return pt != Action()(pt, y);
966 });
967 }
968 };
969
974 namespace schreier_sims {
975
990 // TODO(later) example
991 // TODO (from RC):
992 // 1. Implement orbit refinement heuristic for intersection.
993 // 2. Make the Screier-Sims object during runtime, since we compute the
994 // stabilizers of the intersection already.
995 // 3. Refactor for more generality (i.e. so the template parameters N don't
996 // all have to be the same.
997 //
998 // TODO (from JDM):
999 // * use the no_checks mem fns of SchreierSims now that they exist
1000 template <size_t N, typename Point, typename Element, typename Traits>
1004 } // namespace schreier_sims
1005
1019 template <size_t N, typename Point, typename Element, typename Traits>
1020 [[nodiscard]] std::string
1022 size_t max_width = 72);
1023} // namespace libsemigroups
1024
1025#include "schreier-sims.tpp"
1026
1027#endif // LIBSEMIGROUPS_SCHREIER_SIMS_HPP_
A deterministic version of the Schreier-Sims algorithm acting on a small number of points.
Definition schreier-sims.hpp:177
SchreierSims(SchreierSims &&)
Default move constructor.
const_element_reference strong_generator_no_checks(index_type depth, index_type index) const
Get a strong generator.
Definition schreier-sims.hpp:500
bool orbit_lookup(index_type depth, point_type pt) const
Check if a point is in the orbit of a basepoint.
Definition schreier-sims.hpp:641
bool currently_contains(const_element_reference x) const
Test membership of an element without running.
void run()
Run the Schreier-Sims algorithm.
const_element_reference generator(index_type index) const
Get a generator.
Definition schreier-sims.hpp:377
const_element_reference sift(const_element_reference x) const
Sift an element through the stabiliser chain.
Definition schreier-sims.hpp:751
const_element_reference transversal_element_no_checks(index_type depth, point_type pt) const
Get a transversal element.
Definition schreier-sims.hpp:526
typename Traits::EqualTo EqualTo
Adapter for testing equality.
Definition schreier-sims.hpp:241
void sift_inplace(element_reference x) const
Sift an element through the stabiliser chain in-place.
Definition schreier-sims.hpp:714
size_t base_size() const noexcept
Get the size of the current base.
Definition schreier-sims.hpp:889
size_t number_of_strong_generators_no_checks(index_type depth) const
The number of strong generators at a given depth.
Definition schreier-sims.hpp:455
size_t number_of_strong_generators(index_type depth) const
The number of strong generators at a given depth.
Definition schreier-sims.hpp:430
typename detail::BruidhinnTraits< Element >::reference element_reference
Type of a reference to the elements.
Definition schreier-sims.hpp:214
bool finished() const noexcept
Check if the stabiliser chain is fully enumerated.
Definition schreier-sims.hpp:818
typename Traits::One One
Adapter for the identity element of the given type.
Definition schreier-sims.hpp:247
point_type base(index_type index) const
Get a base point.
Definition schreier-sims.hpp:872
const_element_reference generator_no_checks(index_type index) const
Get a generator.
Definition schreier-sims.hpp:397
typename Traits::Degree Degree
Adapter for the degree of an element.
Definition schreier-sims.hpp:238
typename detail::BruidhinnTraits< Element >::value_type element_type
Type of the elements.
Definition schreier-sims.hpp:202
void sift_inplace_no_checks(element_reference x) const
Sift an element through the stabiliser chain in-place.
Definition schreier-sims.hpp:698
typename Traits::index_type index_type
Type of indices.
Definition schreier-sims.hpp:230
uint64_t size()
Returns the size of the group represented by this.
typename Traits::Inverse Inverse
Adapter for the inverse of an element.
Definition schreier-sims.hpp:244
bool empty() const
Check if any generators have been added so far.
Definition schreier-sims.hpp:660
const_element_reference inverse_transversal_element_no_checks(index_type depth, point_type pt) const
Get an inverse of a transversal element.
Definition schreier-sims.hpp:573
typename Traits::Action Action
Alias for Traits::Action.
Definition schreier-sims.hpp:235
const_element_reference transversal_element(index_type depth, point_type pt) const
Get a transversal element.
const_element_reference sift_no_checks(const_element_reference x) const
Sift an element through the stabiliser chain.
Definition schreier-sims.hpp:731
Point point_type
Type of the points acted on.
Definition schreier-sims.hpp:220
SchreierSims()
Default constructor.
bool add_generator(const_element_reference x)
Add a generator.
const_element_reference one() const noexcept(noexcept(this->to_external_const(_one)))
Returns a const reference to the identity.
Definition schreier-sims.hpp:800
typename Traits::domain_type domain_type
Type of the object containing all points acted on.
Definition schreier-sims.hpp:225
bool add_generator_no_checks(const_element_reference x)
Add a generator.
point_type base_no_checks(index_type index) const
Get a base point.
Definition schreier-sims.hpp:855
SchreierSims & add_base_point(point_type pt)
Add a base point to the stabiliser chain.
typename Traits::Swap Swap
Adapter for swapping.
Definition schreier-sims.hpp:253
size_t number_of_generators() const
The number of generators.
bool orbit_lookup_no_checks(index_type depth, point_type pt) const
Check if a point is in the orbit of a basepoint.
Definition schreier-sims.hpp:620
typename Traits::Product Product
Adapter for the product of two elements.
Definition schreier-sims.hpp:250
bool contains(const_element_reference x)
Test membership of an element.
uint64_t current_size() const
Returns the size of the group represented by this, without running.
SchreierSims & init()
Reset to the trivial group.
typename detail::BruidhinnTraits< Element >::const_reference const_element_reference
Type of a const reference to the elements.
Definition schreier-sims.hpp:208
SchreierSims & operator=(SchreierSims const &that)
Default copy assignment.
const_element_reference strong_generator(index_type depth, index_type index) const
Get a strong generator.
SchreierSims(SchreierSims const &that)
Default copy constructor.
const_element_reference inverse_transversal_element(index_type depth, point_type pt) const
Get an inverse of a transversal element.
SchreierSims & operator=(SchreierSims &&)
Default move assignment.
T find_if(T... args)
std::string to_human_readable_repr(Action< Element, Point, Func, Traits, LeftOrRight > const &action)
Return a human readable representation of an Action object.
typename detail::LeastPermHelper< N >::type LeastPerm
Type of perms using the least memory for a given degree.
Definition transf.hpp:2413
Namespace for SchreierSims helper functions.
Definition schreier-sims.hpp:974
void intersection(SchreierSims< N, Point, Element, Traits > &T, SchreierSims< N, Point, Element, Traits > &S1, SchreierSims< N, Point, Element, Traits > &S2)
Find the intersection of two permutation groups.
Namespace for everything in the libsemigroups library.
Definition action.hpp:44
Adapter for the degree of an element.
Definition adapters.hpp:166
Adapter for testing equality.
Definition adapters.hpp:418
Adapter for the value of a right action.
Definition adapters.hpp:397
Adapter for the inverse of an element.
Definition adapters.hpp:324
Adapter for the identity element of the given type.
Definition adapters.hpp:251
Adapter for the product of two elements.
Definition adapters.hpp:289
Traits class for use with the class template SchreierSims.
Definition schreier-sims.hpp:100
libsemigroups::Swap< element_type > Swap
Adapter for swapping.
Definition schreier-sims.hpp:141
detail::IntRange< Point > domain_type
Type of the object containing all points acted on.
Definition schreier-sims.hpp:109
libsemigroups::One< element_type > One
Adapter for the identity element of the given type.
Definition schreier-sims.hpp:135
libsemigroups::ImageRightAction< element_type, Point > Action
Adapter for the value of a right action.
Definition schreier-sims.hpp:123
Element element_type
Type of the elements.
Definition schreier-sims.hpp:120
libsemigroups::Inverse< element_type > Inverse
Adapter for increasing the degree of an element.
Definition schreier-sims.hpp:132
size_t index_type
The type of indices to be used by a SchreierSims instance.
Definition schreier-sims.hpp:104
libsemigroups::EqualTo< element_type > EqualTo
Adapter for testing equality.
Definition schreier-sims.hpp:129
Point point_type
Type of the points acted on.
Definition schreier-sims.hpp:115
libsemigroups::Product< element_type > Product
Adapter for the product of two elements.
Definition schreier-sims.hpp:138
libsemigroups::Degree< element_type > Degree
Adapter for the degree of an element.
Definition schreier-sims.hpp:126
std::conditional_t< N >=0x100000000, uint64_t, std::conditional_t< N >=0x10000, uint32_t, std::conditional_t< N >=0x100, uint16_t, uint8_t > > > type
Definition types.hpp:87
Adapter for swapping.
Definition adapters.hpp:671