libsemigroups  v3.1.2
C++ library for semigroups and monoids
Loading...
Searching...
No Matches
transf.hpp
1//
2// libsemigroups - C++ library for semigroups and monoids
3// Copyright (C) 2021-2025 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// This file contains the declaration of the partial transformation class and
20// its subclasses.
21
22// TODO(later)
23// * benchmarks
24// * add some tests for PTransf themselves
25// * allocator
26// * implement is_transformation(Iterator, Iterator) etc to avoid code
27// duplication with hpcombi.hpp
28
29#ifndef LIBSEMIGROUPS_TRANSF_HPP_
30#define LIBSEMIGROUPS_TRANSF_HPP_
31
32#include <algorithm> // for sort, max_element, unique
33#include <array> // for array
34#include <cstddef> // for size_t
35#include <cstdint> // for uint64_t, uint32_t
36#include <initializer_list> // for initializer_list
37#include <iterator> // for distance
38#include <limits> // for numeric_limits
39#include <numeric> // for iota
40#include <tuple> // for tuple_size
41#include <type_traits> // for enable_if_t
42#include <unordered_map> // for unordered_map
43#include <unordered_set> // for unordered_set
44#include <utility> // for forward
45#include <vector> // for vector
46
47#include "config.hpp" // for LIBSEMIGROUPS_HPCOMBI_ENABLED
48
49#include "adapters.hpp" // for Hash etc
50#include "bitset.hpp" // for BitSet
51#include "constants.hpp" // for UNDEFINED, Undefined
52#include "debug.hpp" // for LIBSEMIGROUPS_ASSERT
53#include "exception.hpp" // for LIBSEMIGROUPS_EXCEPTION
54#include "hpcombi.hpp" // for HPCombi::Transf16
55#include "types.hpp" // for SmallestInteger
56
57#include "detail/stl.hpp" // for is_array_v
58
59namespace libsemigroups {
69
70 namespace detail {
75 } // namespace detail
76
91 // TODO(1) example
92
100 template <typename T>
101 static constexpr bool IsDerivedFromPTransf
102 = std::is_base_of_v<detail::PTransfPolymorphicBase, T>;
103
104 namespace detail {
105
106 template <typename T>
107 struct IsStaticHelper : std::false_type {};
108
109 template <typename T>
110 struct IsDynamicHelper : std::false_type {};
111
112 } // namespace detail
113
127 template <typename Scalar, typename Container>
129 static_assert(std::is_integral_v<Scalar>,
130 "template parameter Scalar must be an integral type");
132 "template parameter Scalar must be unsigned");
133
134 public:
138 using point_type = Scalar;
139
143 using container_type = Container;
144
145 // Required by python bindings
156 [[nodiscard]] static point_type undef() noexcept {
157 return static_cast<point_type>(UNDEFINED);
158 }
159
169 PTransfBase() = default;
170
171 // No constructor from size_t this is delegated to StaticPTransf and
172 // DynamicPTransf
173
194 explicit PTransfBase(Container const& cont) : _container(cont) {}
195
197 explicit PTransfBase(Container&& cont) : _container(std::move(cont)) {}
198
219 template <typename Iterator>
220 explicit PTransfBase(Iterator first, Iterator last) : PTransfBase() {
221 using OtherScalar = typename std::iterator_traits<Iterator>::value_type;
222 // The below assertions exist to insure that we are not badly assigning
223 // values. The subsequent pragmas exist to suppress the false-positive
224 // warnings produced by g++ 13.2.0
225 static_assert(
226 std::is_same_v<OtherScalar, Undefined>
227 || std::is_convertible_v<OtherScalar, point_type>,
228 "the template parameter Iterator must have "
229 "value_type \"Undefined\" or convertible to \"point_type\"!");
230 static_assert(std::is_same_v<std::decay_t<decltype(*_container.begin())>,
231 point_type>);
232 resize(_container, std::distance(first, last));
233#pragma GCC diagnostic push
234#if defined(__GNUC__) && !defined(__clang__)
235#pragma GCC diagnostic ignored "-Wstringop-overflow"
236#endif
237 std::copy(first, last, _container.begin());
238#pragma GCC diagnostic pop
239 }
240
244
266 template <typename Subclass, typename OtherContainer = Container>
267 [[nodiscard]] static Subclass make(OtherContainer&& cont);
268
289 template <typename Subclass, typename OtherScalar>
290 [[nodiscard]] static Subclass make(std::initializer_list<OtherScalar> cont);
291
295 PTransfBase(PTransfBase const&) = default;
296
301
306
311
327 [[nodiscard]] bool operator<(PTransfBase const& that) const {
328 return _container < that._container;
329 }
330
346 [[nodiscard]] bool operator>(PTransfBase const& that) const {
347 return that < *this;
348 }
349
365 [[nodiscard]] bool operator==(PTransfBase const& that) const {
366 return _container == that._container;
367 }
368
384 [[nodiscard]] bool operator<=(PTransfBase const& that) const {
385 return _container < that._container || _container == that._container;
386 }
387
403 [[nodiscard]] bool operator>=(PTransfBase const& that) const {
404 return that <= *this;
405 }
406
422 [[nodiscard]] bool operator!=(PTransfBase const& that) const {
423 return !(*this == that);
424 }
425
440 [[nodiscard]] point_type& operator[](size_t i) {
441 return _container[i];
442 }
443
458 [[nodiscard]] point_type const& operator[](size_t i) const {
459 return _container[i];
460 }
461
475 // TODO(1) better exception message for python bindings
476 [[nodiscard]] point_type& at(size_t i) {
477 return _container.at(i);
478 }
479
493 // TODO(1) better exception message for python bindings
494 [[nodiscard]] point_type const& at(size_t i) const {
495 return _container.at(i);
496 }
497
518 // TODO(later) other operators such as power
519 template <typename Subclass>
520 [[nodiscard]] Subclass operator*(Subclass const& that) const {
521 static_assert(IsDerivedFromPTransf<Subclass>,
522 "the template parameter Subclass must be derived from "
523 "PTransfPolymorphicBase");
524 Subclass xy(that.degree());
525 xy.product_inplace(*static_cast<Subclass const*>(this), that);
526 return xy;
527 }
528
532 using iterator = typename Container::iterator;
533
537 using const_iterator = typename Container::const_iterator;
538
550 [[nodiscard]] const_iterator cbegin() const noexcept {
551 return _container.cbegin();
552 }
553
565 [[nodiscard]] const_iterator cend() const noexcept {
566 return _container.cend();
567 }
568
570 [[nodiscard]] const_iterator begin() const noexcept {
571 return _container.begin();
572 }
573
575 [[nodiscard]] const_iterator end() const noexcept {
576 return _container.end();
577 }
578
590 [[nodiscard]] iterator begin() noexcept {
591 return _container.begin();
592 }
593
605 [[nodiscard]] iterator end() noexcept {
606 return _container.end();
607 }
608
622 [[nodiscard]] size_t rank() const {
624 return (vals.find(UNDEFINED) == vals.end() ? vals.size()
625 : vals.size() - 1);
626 }
627
638 // not noexcept because Hash<T>::operator() isn't
639 [[nodiscard]] size_t hash_value() const {
640 return Hash<Container>()(_container);
641 }
642
649 void swap(PTransfBase& that) noexcept {
650 std::swap(_container, that._container);
651 }
652
664 [[nodiscard]] size_t degree() const noexcept {
665 return _container.size();
666 }
667
681 template <typename Subclass>
682 [[nodiscard]] static Subclass one(size_t N) {
683 static_assert(IsDerivedFromPTransf<Subclass>,
684 "the template parameter Subclass must be derived from "
685 "PTransfPolymorphicBase");
686 Subclass result(N);
687 std::iota(result.begin(), result.end(), 0);
688 return result;
689 }
690
691 protected:
692 static void resize(container_type& c, size_t N, point_type val = 0);
693 void resize(size_t N, point_type val = 0) {
694 resize(_container, N, val);
695 }
696
697 private:
698 template <typename T>
699 static void throw_if_bad_args(T const& cont);
700
701 Container _container;
702 };
703
705 // Helper variable templates
707
715 template <typename T>
716 static constexpr bool IsStatic = detail::IsStaticHelper<T>::value;
717
725 template <typename T>
726 static constexpr bool IsDynamic = detail::IsDynamicHelper<T>::value;
727
729 // DynamicPTransf
731
744 template <typename Scalar>
745 class DynamicPTransf : public PTransfBase<Scalar, std::vector<Scalar>> {
747
748 public:
754 using point_type = Scalar;
755
762
764 using base_type::begin;
765 using base_type::degree;
766 using base_type::end;
767
768 // No default constructor, because the degree would be 0, and so we can
769 // just use the PTransfBase default constructor for that. Note that there's
770 // a default constructor for StaticPTransf since there we do know the degree
771 // (at compile time) and we can fill it with UNDEFINED values.
772
785 explicit DynamicPTransf(size_t n) : base_type() {
786 resize(n, UNDEFINED);
787 }
788
804 resize(degree() + m);
805 std::iota(end() - m, end(), degree() - m);
806 return *this;
807 }
808
809 protected:
810 using base_type::resize;
811 };
812
814 // StaticPTransf
816
829 template <size_t N, typename Scalar>
830 class StaticPTransf : public PTransfBase<Scalar, std::array<Scalar, N>> {
832
833 public:
835 using point_type = Scalar;
836
841
843 using base_type::begin;
844 using base_type::end;
845
856 StaticPTransf() : base_type() {
858 }
859
870 explicit StaticPTransf(size_t n);
871
878 // do nothing can't increase the degree
879 LIBSEMIGROUPS_EXCEPTION("cannot increase the degree of a StaticPTransf!");
880 return *this;
881 }
882 };
883
885 // PTransf
887
905 template <
906 size_t N = 0,
907 typename Scalar
908 = std::conditional_t<N == 0, uint32_t, typename SmallestInteger<N>::type>>
909 using PTransf = std::
910 conditional_t<N == 0, DynamicPTransf<Scalar>, StaticPTransf<N, Scalar>>;
911
912 namespace detail {
913 template <typename T>
914 struct IsPTransfHelper : std::false_type {};
915
916 template <typename Scalar>
917 struct IsPTransfHelper<DynamicPTransf<Scalar>> : std::true_type {};
918
919 template <size_t N, typename Scalar>
920 struct IsPTransfHelper<StaticPTransf<N, Scalar>> : std::true_type {};
921
922 template <size_t N, typename Scalar>
923 struct IsStaticHelper<StaticPTransf<N, Scalar>> : std::true_type {};
924
925 template <typename Scalar>
926 struct IsDynamicHelper<DynamicPTransf<Scalar>> : std::true_type {};
927
928 } // namespace detail
929
940 template <typename T>
941 static constexpr bool IsPTransf = detail::IsPTransfHelper<T>::value;
942
958 // TODO(1) to tpp
959 template <typename Scalar, typename Container>
960 void
962 size_t const M = f.degree();
963 for (auto const& val : f) {
964 // the type of "val" is an unsigned int, and so we don't check for val
965 // being less than 0
966 if (val >= M && val != UNDEFINED) {
967 LIBSEMIGROUPS_EXCEPTION("image value out of bounds, expected value in "
968 "[{}, {}), found {}",
969 0,
970 M,
971 val);
972 }
973 }
974 }
975
977 // Transf
979
1006 template <
1007 size_t N = 0,
1008 typename Scalar
1009 = std::conditional_t<N == 0, uint32_t, typename SmallestInteger<N>::type>>
1010 class Transf : public PTransf<N, Scalar> {
1011 using base_type = PTransf<N, Scalar>;
1012
1013 public:
1017 using point_type = Scalar;
1018
1022 using container_type = typename base_type::container_type;
1023
1024 using PTransf<N, Scalar>::PTransf;
1025 using base_type::degree;
1026
1046 void product_inplace(Transf const& f, Transf const& g);
1047
1061 [[nodiscard]] static Transf one(size_t M) {
1062 return base_type::template one<Transf>(M);
1063 }
1064 };
1065
1066 namespace detail {
1067 template <typename T>
1068 struct IsTransfHelper : std::false_type {};
1069
1070 template <size_t N, typename Scalar>
1071 struct IsTransfHelper<Transf<N, Scalar>> : std::true_type {};
1072
1073 template <size_t N, typename Scalar>
1074 struct IsStaticHelper<Transf<N, Scalar>>
1075 : IsStaticHelper<PTransf<N, Scalar>> {};
1076
1077 template <size_t N, typename Scalar>
1078 struct IsDynamicHelper<Transf<N, Scalar>>
1079 : IsDynamicHelper<PTransf<N, Scalar>> {};
1080 } // namespace detail
1081
1083 // Transf helpers
1085
1093 template <typename T>
1094 static constexpr bool IsTransf = detail::IsTransfHelper<T>::value;
1095
1097 // Transf throw_if_image_value_out_of_range
1099
1113 template <size_t N, typename Scalar>
1115
1117 // make<Transf>
1119
1130
1153 template <typename Return, typename OtherContainer>
1154 [[nodiscard]] std::enable_if_t<IsTransf<Return>, Return>
1155 make(OtherContainer&& cont) {
1156 return Return::template make<Return>(std::forward<OtherContainer>(cont));
1157 }
1158
1182 template <typename Return, typename OtherScalar>
1183 [[nodiscard]] std::enable_if_t<IsTransf<Return>, Return>
1187
1189 // PPerm
1191
1219 template <
1220 size_t N = 0,
1221 typename Scalar
1222 = std::conditional_t<N == 0, uint32_t, typename SmallestInteger<N>::type>>
1223 class PPerm : public PTransf<N, Scalar> {
1224 using base_type = PTransf<N, Scalar>;
1225
1226 public:
1230 using point_type = Scalar;
1231
1235 using container_type = typename base_type::container_type;
1236
1237 using PTransf<N, point_type>::PTransf;
1238 using base_type::degree;
1239 using base_type::undef;
1240
1260 //
1261 // Note: we use vectors here not container_type (which might be array),
1262 // because the length of dom and img might not equal degree().
1263 // Also we don't use a universal reference because we can't actually use an
1264 // rvalue reference here (we don't store dom or img).
1265 template <typename OtherScalar>
1267 std::vector<OtherScalar> const& img,
1268 size_t M);
1269
1274 size_t M)
1275 : PPerm(std::vector<point_type>(dom), std::vector<point_type>(img), M) {
1276 }
1277
1294 void product_inplace(PPerm const& f, PPerm const& g);
1295
1297 [[nodiscard]] static PPerm one(size_t M) {
1298 return base_type::template one<PPerm>(M);
1299 }
1300 };
1301
1302 namespace detail {
1303 template <
1304 size_t N = 0,
1305 typename Scalar = std::
1306 conditional_t<N == 0, uint32_t, typename SmallestInteger<N>::type>>
1307 void throw_if_bad_args(std::vector<Scalar> const& dom,
1308 std::vector<Scalar> const& ran,
1309 size_t deg = N);
1310
1312 // PPerm helpers
1314
1315 template <typename T>
1316 struct IsPPermHelper : std::false_type {};
1317
1318 template <size_t N, typename Scalar>
1319 struct IsPPermHelper<PPerm<N, Scalar>> : std::true_type {};
1320
1321 template <size_t N, typename Scalar>
1322 struct IsStaticHelper<PPerm<N, Scalar>>
1323 : IsStaticHelper<PTransf<N, Scalar>> {};
1324
1325 template <size_t N, typename Scalar>
1326 struct IsDynamicHelper<PPerm<N, Scalar>>
1327 : IsDynamicHelper<PTransf<N, Scalar>> {};
1328 } // namespace detail
1329
1337 template <typename T>
1338 static constexpr bool IsPPerm = detail::IsPPermHelper<T>::value;
1339
1341 // PPerm check
1343
1358 template <size_t N, typename Scalar>
1361 detail::throw_if_duplicates(f.begin(), f.end());
1362 }
1363
1365 // make<PPerm>
1367
1378
1402 template <typename Return, typename OtherContainer>
1403 [[nodiscard]] std::enable_if_t<IsPPerm<Return>, Return>
1404 make(OtherContainer&& cont) {
1405 return Return::template make<Return>(std::forward<OtherContainer>(cont));
1406 }
1407
1429 template <typename Return>
1430 [[nodiscard]] std::enable_if_t<IsPPerm<Return>, Return>
1435
1459 template <typename Return>
1460 [[nodiscard]] std::enable_if_t<IsPPerm<Return>, Return>
1463 size_t M);
1464
1488 template <typename Return>
1489 [[nodiscard]] std::enable_if_t<IsPPerm<Return>, Return>
1497
1499 // Perm
1501
1528 template <
1529 size_t N = 0,
1530 typename Scalar
1531 = std::conditional_t<N == 0, uint32_t, typename SmallestInteger<N>::type>>
1532 class Perm : public Transf<N, Scalar> {
1533 using base_type = PTransf<N, Scalar>;
1534
1535 public:
1539 using point_type = Scalar;
1540
1545
1546 using Transf<N, Scalar>::Transf;
1547 using base_type::degree;
1548
1550 [[nodiscard]] static Perm one(size_t M) {
1551 return base_type::template one<Perm>(M);
1552 }
1553 };
1554
1556 // Perm helpers
1558
1559 namespace detail {
1560 template <typename T>
1561 struct IsPermHelper : std::false_type {};
1562
1563 template <size_t N, typename Scalar>
1564 struct IsPermHelper<Perm<N, Scalar>> : std::true_type {};
1565 } // namespace detail
1566
1574 template <typename T>
1575 static constexpr bool IsPerm = detail::IsPermHelper<T>::value;
1576
1578 // Perm throw_if_not_perm
1580
1595 template <size_t N, typename Scalar>
1598 detail::throw_if_duplicates(f.begin(), f.end());
1599 }
1600
1602 // make<Perm>
1604
1615
1639 template <typename Return, typename OtherContainer>
1640 [[nodiscard]] std::enable_if_t<IsPerm<Return>, Return>
1641 make(OtherContainer&& cont) {
1642 return Return::template make<Return>(std::forward<OtherContainer>(cont));
1643 }
1644
1667 template <typename Return>
1668 [[nodiscard]] std::enable_if_t<IsPerm<Return>, Return>
1673
1675 // Helper functions
1677
1704 template <typename T, typename Scalar>
1705 void image(T const& f, std::vector<Scalar>& im);
1706
1728 template <typename T>
1730
1757 template <typename T, typename Scalar>
1758 void domain(T const& f, std::vector<Scalar>& dom);
1759
1781 template <typename T>
1783
1802 template <typename T>
1803 [[nodiscard]] auto one(T const& f)
1804 -> std::enable_if_t<IsDerivedFromPTransf<T>, T> {
1805 return T::one(f.degree());
1806 }
1807
1825 template <size_t N, typename Scalar>
1827 // TODO(1) void pass by reference version
1828
1846 template <size_t N, typename Scalar>
1848 // TODO(1) void pass by reference version
1849
1865 // Put the inverse of this into that
1866 template <size_t N, typename Scalar>
1868
1891 template <size_t N, typename Scalar>
1893 PPerm<N, Scalar> to(f.degree());
1894 inverse(f, to);
1895 return to;
1896 }
1897
1916 template <size_t N, typename Scalar>
1918
1939 template <size_t N, typename Scalar>
1940 [[nodiscard]] Perm<N, Scalar> inverse(Perm<N, Scalar> const& f);
1941
1943 // Adapters
1945
1946 template <typename T>
1947 struct Degree<T, std::enable_if_t<IsDerivedFromPTransf<T>>> {
1948 [[nodiscard]] constexpr size_t operator()(T const& x) const noexcept {
1949 return x.degree();
1950 }
1951 };
1952
1953 template <typename T>
1954 struct One<T, std::enable_if_t<IsDerivedFromPTransf<T>>> {
1955 [[nodiscard]] T operator()(T const& x) const {
1956 return (*this)(x.degree());
1957 }
1958
1959 [[nodiscard]] T operator()(size_t N) const {
1960 return T::one(N);
1961 }
1962 };
1963
1964 template <size_t N, typename Scalar>
1965 struct Inverse<Perm<N, Scalar>> {
1966 [[nodiscard]] Perm<N, Scalar> operator()(Perm<N, Scalar> const& x) {
1967 return inverse(x);
1968 }
1969 };
1970
1971 template <typename Subclass>
1972 struct Product<Subclass, std::enable_if_t<IsDerivedFromPTransf<Subclass>>> {
1973 void
1974 operator()(Subclass& xy, Subclass const& x, Subclass const& y, size_t = 0) {
1975 xy.product_inplace(x, y);
1976 }
1977 };
1978
1979 template <typename T>
1980 struct Hash<T, std::enable_if_t<IsDerivedFromPTransf<T>>> {
1981 [[nodiscard]] constexpr size_t operator()(T const& x) const {
1982 return x.hash_value();
1983 }
1984 };
1985
1986 template <typename T>
1987 struct Complexity<T, std::enable_if_t<IsDerivedFromPTransf<T>>> {
1988 [[nodiscard]] constexpr size_t operator()(T const& x) const noexcept {
1989 return x.degree();
1990 }
1991 };
1992
1997 template <typename T>
1998 struct IncreaseDegree<T, std::enable_if_t<IsDerivedFromPTransf<T>>> {
2000 inline void operator()(T& x, size_t n) const {
2001 x.increase_degree_by(n);
2002 }
2003 };
2004
2006 // ImageRight/LeftAction - Transf
2008
2009 // Equivalent to OnSets in GAP
2010 // Slowest
2011 // works for T = std::vector and T = StaticVector1
2016 template <size_t N, typename Scalar, typename T>
2017 struct ImageRightAction<Transf<N, Scalar>, T> {
2019 void operator()(T& res, T const& pt, Transf<N, Scalar> const& x) const;
2020 };
2021
2022 // Fastest, but limited to at most degree 64
2027 template <size_t N, typename Scalar, size_t M>
2028 struct ImageRightAction<Transf<N, Scalar>, BitSet<M>> {
2030 void operator()(BitSet<M>& res,
2031 BitSet<M> const& pt,
2032 Transf<N, Scalar> const& x) const {
2033 res.reset();
2034 // Apply the lambda to every set bit in pt
2035 pt.apply([&x, &res](size_t i) { res.set(x[i]); });
2036 }
2037 };
2038
2039 // OnKernelAntiAction
2040 // T = StaticVector1<S> or std::vector<S>
2045 template <size_t N, typename Scalar, typename T>
2046 struct ImageLeftAction<Transf<N, Scalar>, T> {
2048 void operator()(T& res, T const& pt, Transf<N, Scalar> const& x) const;
2049 };
2050
2052 // Lambda/Rho - Transformation
2054
2055 // This currently limits the use of Konieczny to transformation of degree at
2056 // most 64 with the default traits class, since we cannot know the degree at
2057 // compile time, only at run time.
2064 template <size_t N, typename Scalar>
2065 struct LambdaValue<Transf<N, Scalar>> {
2068 using type = BitSet<BitSet<1>::max_size()>;
2069 };
2070
2071 // Benchmarks indicate that using std::vector yields similar performance to
2072 // using StaticVector1's.
2076 template <size_t N, typename Scalar>
2077 struct RhoValue<Transf<N, Scalar>> {
2081 };
2082
2083 // T = std::vector or StaticVector1
2088 template <size_t N, typename Scalar, typename T>
2089 struct Lambda<Transf<N, Scalar>, T> {
2090 // not noexcept because std::vector::resize isn't (although
2091 // StaticVector1::resize is).
2094 void operator()(T& res, Transf<N, Scalar> const& x) const;
2095 };
2096
2101 template <size_t N, typename Scalar, size_t M>
2102 struct Lambda<Transf<N, Scalar>, BitSet<M>> {
2103 // not noexcept because it can throw
2106 void operator()(BitSet<M>& res, Transf<N, Scalar> const& x) const;
2107 };
2108
2109 // T = std::vector<S> or StaticVector1<S, N>
2114 template <size_t N, typename Scalar, typename T>
2115 struct Rho<Transf<N, Scalar>, T> {
2127 // not noexcept because std::vector::resize isn't (although
2128 // StaticVector1::resize is).
2129 void operator()(T& res, Transf<N, Scalar> const& x) const;
2130 };
2131
2135 template <size_t N, typename Scalar>
2136 struct Rank<Transf<N, Scalar>> {
2149 [[nodiscard]] size_t operator()(Transf<N, Scalar> const& x) const {
2150 return x.rank();
2151 }
2152 };
2153
2155 // ImageRight/LeftAction - PPerm
2157
2158 // Slowest
2163 template <size_t N, typename Scalar>
2164 struct ImageRightAction<PPerm<N, Scalar>, PPerm<N, Scalar>> {
2167 PPerm<N, Scalar> const& pt,
2168 PPerm<N, Scalar> const& x) const noexcept {
2169 LIBSEMIGROUPS_ASSERT(res.degree() == pt.degree());
2170 LIBSEMIGROUPS_ASSERT(res.degree() == x.degree());
2171 res.product_inplace(pt, x);
2172 res = right_one(res);
2173 }
2174 };
2175
2176 // Faster than the above, but slower than the below
2177 // works for T = std::vector and T = StaticVector1
2182 template <size_t N, typename Scalar, typename T>
2183 struct ImageRightAction<PPerm<N, Scalar>, T> {
2185 void operator()(T& res, T const& pt, PPerm<N, Scalar> const& x) const;
2186 };
2187
2188 // Fastest, but limited to at most degree 64
2193 template <size_t N, typename Scalar, size_t M>
2194 struct ImageRightAction<PPerm<N, Scalar>, BitSet<M>> {
2196 void operator()(BitSet<M>& res,
2197 BitSet<M> const& pt,
2198 PPerm<N, Scalar> const& x) const;
2199 };
2200
2201 // Slowest
2206 template <size_t N, typename Scalar>
2207 struct ImageLeftAction<PPerm<N, Scalar>, PPerm<N, Scalar>> {
2210 PPerm<N, Scalar> const& pt,
2211 PPerm<N, Scalar> const& x) const noexcept {
2212 res.product_inplace(x, pt);
2213 res = left_one(res);
2214 }
2215 };
2216
2217 // Fastest when used with BitSet<N>.
2218 // works for T = std::vector and T = BitSet<N>
2219 // Using BitSet<N> limits this to size 64. However, if we are trying to
2220 // compute a LeftAction object, then the max size of such is 2 ^ 64, which
2221 // is probably not achievable. So, for higher degrees, we will only be able
2222 // to compute relatively sparse LeftActions (i.e. not containing the
2223 // majority of the 2 ^ n possible subsets), in which case using vectors or
2224 // StaticVector1's might be not be appreciable slower anyway. All of this is
2225 // to say that it probably isn't worthwhile trying to make BitSet's work for
2226 // more than 64 bits.
2231 template <size_t N, typename Scalar, typename T>
2232 struct ImageLeftAction<PPerm<N, Scalar>, T> {
2233 void operator()(T& res, T const& pt, PPerm<N, Scalar> const& x) const {
2235 static PPerm<N, Scalar> xx(x.degree());
2236 inverse(x, xx); // invert x into xx
2237 ImageRightAction<PPerm<N, Scalar>, T>()(res, pt, xx);
2238 }
2239 };
2240
2242 // Lambda/Rho - PPerm
2244
2245 // This currently limits the use of Konieczny to partial perms of degree at
2246 // most 64 with the default traits class, since we cannot know the degree
2247 // at compile time, only at run time.
2253 template <size_t N, typename Scalar>
2254 struct LambdaValue<PPerm<N, Scalar>> {
2257 using type = BitSet<BitSet<1>::max_size()>;
2258 };
2259
2265 template <size_t N, typename Scalar>
2266 struct RhoValue<PPerm<N, Scalar>> {
2270 };
2271
2276 template <size_t N, typename Scalar, size_t M>
2277 struct Lambda<PPerm<N, Scalar>, BitSet<M>> {
2280 void operator()(BitSet<M>& res, PPerm<N, Scalar> const& x) const;
2281 };
2282
2287 template <size_t N, typename Scalar, size_t M>
2288 struct Rho<PPerm<N, Scalar>, BitSet<M>> {
2291 void operator()(BitSet<M>& res, PPerm<N, Scalar> const& x) const;
2292 };
2293
2297 template <size_t N, typename Scalar>
2298 struct Rank<PPerm<N, Scalar>> {
2302 [[nodiscard]] size_t operator()(PPerm<N, Scalar> const& x) const {
2303 return x.rank();
2304 }
2305 };
2306
2308 // Perm
2310
2315 // TODO(later) this could work for everything derived from PTransf
2316 template <size_t N, typename Scalar, typename T>
2317 struct ImageRightAction<Perm<N, Scalar>,
2318 T,
2319 std::enable_if_t<std::is_integral_v<T>>> {
2321 void operator()(T& res,
2322 T const& pt,
2323 Perm<N, Scalar> const& p) const noexcept {
2324 LIBSEMIGROUPS_ASSERT(pt < p.degree());
2325 res = p[pt];
2326 }
2327
2329 [[nodiscard]] T operator()(T pt, Perm<N, Scalar> const& x) {
2330 return x[pt];
2331 }
2332 };
2333
2335 // Helpers
2337
2338 namespace detail {
2339 template <size_t N>
2340 struct LeastTransfHelper {
2341#ifdef LIBSEMIGROUPS_HPCOMBI_ENABLED
2342 using type = std::conditional_t<N >= 17, Transf<N>, HPCombi::Transf16>;
2343#else
2344 using type = Transf<N>;
2345#endif
2346 };
2347
2348 template <size_t N>
2349 struct LeastPPermHelper {
2350#ifdef LIBSEMIGROUPS_HPCOMBI_ENABLED
2351 using type = std::conditional_t<N >= 17, PPerm<N>, HPCombi::PPerm16>;
2352#else
2353 using type = PPerm<N>;
2354#endif
2355 };
2356
2357 template <size_t N>
2358 struct LeastPermHelper {
2359#ifdef LIBSEMIGROUPS_HPCOMBI_ENABLED
2360 using type = std::conditional_t<N >= 17, Perm<N>, HPCombi::Perm16>;
2361#else
2362 using type = Perm<N>;
2363#endif
2364 };
2365 } // namespace detail
2366
2379 template <size_t N>
2380 using LeastTransf = typename detail::LeastTransfHelper<N>::type;
2381
2394 template <size_t N>
2395 using LeastPPerm = typename detail::LeastPPermHelper<N>::type;
2396
2409 template <size_t N>
2410 using LeastPerm = typename detail::LeastPermHelper<N>::type;
2411
2436 template <size_t N, typename Scalar>
2438 std::string_view prefix = "",
2439 std::string_view braces = "{}");
2440
2465 template <size_t N, typename Scalar>
2467 std::string_view prefix = "",
2468 std::string_view braces = "{}");
2469
2495 template <size_t N, typename Scalar>
2497 std::string_view prefix = "",
2498 std::string_view braces = "{}");
2499
2529 template <size_t N, typename Scalar>
2531 std::string_view prefix = "",
2532 std::string_view braces
2533 = "{}",
2534 size_t max_width = 72);
2535
2564 template <size_t N, typename Scalar>
2566 std::string_view prefix = "",
2567 std::string_view braces
2568 = "{}",
2569 size_t max_width = 72);
2570
2601 template <size_t N, typename Scalar>
2603 std::string_view prefix = "",
2604 std::string_view braces
2605 = "{}",
2606 size_t max_width = 72);
2607} // namespace libsemigroups
2608
2609#include "transf.tpp"
2610
2611#endif // LIBSEMIGROUPS_TRANSF_HPP_
Partial transformations with dynamic degree.
Definition transf.hpp:745
DynamicPTransf(size_t n)
Construct with given degree.
Definition transf.hpp:785
size_t degree() const noexcept
Returns the degree of a partial transformation.
Definition transf.hpp:664
const_iterator end() const noexcept
Returns a const_iterator (random access iterator) pointing one past the last image value.
Definition transf.hpp:575
DynamicPTransf & increase_degree_by(size_t m)
Increase the degree in-place.
Definition transf.hpp:803
Scalar point_type
Type of the image values.
Definition transf.hpp:754
std::vector< point_type > container_type
Type of the underlying container.
Definition transf.hpp:761
Partial permutations with static or dynamic degree.
Definition transf.hpp:1223
PPerm(std::initializer_list< point_type > dom, std::initializer_list< point_type > img, size_t M)
Construct from domain, range, and degree.
Definition transf.hpp:1272
PPerm(std::vector< OtherScalar > const &dom, std::vector< OtherScalar > const &img, size_t M)
Construct from domain, range, and degree.
Scalar point_type
Type of the image values.
Definition transf.hpp:1230
void product_inplace(PPerm const &f, PPerm const &g)
Multiply two partial perms and store the product in this.
static PPerm one(size_t M)
Returns the identity transformation on the given number of points.
Definition transf.hpp:1297
typename base_type::container_type container_type
Type of the underlying container.
Definition transf.hpp:1235
Base class for partial transformations.
Definition transf.hpp:128
typename Container::iterator iterator
Type of iterators point to image values.
Definition transf.hpp:532
bool operator==(PTransfBase const &that) const
Compare for equality.
Definition transf.hpp:365
Subclass operator*(Subclass const &that) const
Multiply by another partial transformation.
Definition transf.hpp:520
size_t rank() const
Returns the number of distinct image values.
Definition transf.hpp:622
typename Container::const_iterator const_iterator
Type of const iterators point to image values.
Definition transf.hpp:537
PTransfBase(PTransfBase const &)=default
Default copy constructor.
const_iterator begin() const noexcept
Definition transf.hpp:570
PTransfBase()=default
Default constructor.
bool operator>=(PTransfBase const &that) const
Compare for greater than or equal.
Definition transf.hpp:403
PTransfBase & operator=(PTransfBase const &)=default
Default copy assignment operator.
PTransfBase(std::initializer_list< Scalar > cont)
Construct from a container of images.
Definition transf.hpp:242
point_type const & at(size_t i) const
Get a const reference to the image of a point.
Definition transf.hpp:494
size_t degree() const noexcept
Returns the degree of a partial transformation.
Definition transf.hpp:664
const_iterator end() const noexcept
Definition transf.hpp:575
bool operator>(PTransfBase const &that) const
Compare for greater.
Definition transf.hpp:346
static Subclass make(OtherContainer &&cont)
Construct from universal reference container and check.
PTransfBase & operator=(PTransfBase &&)=default
Default move assignment operator.
PTransfBase(PTransfBase &&)=default
Default move constructor.
Scalar point_type
Type of the image values.
Definition transf.hpp:138
static Subclass make(std::initializer_list< OtherScalar > cont)
Construct from std::initializer_list and check.
iterator begin() noexcept
Returns an iterator (random access iterator) pointing at the first image value.
Definition transf.hpp:590
const_iterator cend() const noexcept
Returns a const_iterator (random access iterator) pointing one past the last image value.
Definition transf.hpp:565
Container container_type
Type of the underlying container.
Definition transf.hpp:143
const_iterator cbegin() const noexcept
Returns a const_iterator (random access iterator) pointing at the first image value.
Definition transf.hpp:550
point_type & at(size_t i)
Get a reference to the image of a point.
Definition transf.hpp:476
static Subclass one(size_t N)
Returns the identity transformation on the given number of points.
Definition transf.hpp:682
PTransfBase(Iterator first, Iterator last)
Construct from a range of images.
Definition transf.hpp:220
point_type & operator[](size_t i)
Get a reference to the image of a point.
Definition transf.hpp:440
static point_type undef() noexcept
Returns the value used to represent "undefined".
Definition transf.hpp:156
size_t hash_value() const
Returns a hash value.
Definition transf.hpp:639
bool operator<=(PTransfBase const &that) const
Compare for less than or equal.
Definition transf.hpp:384
bool operator<(PTransfBase const &that) const
Compare for less.
Definition transf.hpp:327
PTransfBase(Container const &cont)
Construct from a container of images.
Definition transf.hpp:194
point_type const & operator[](size_t i) const
Get a const reference to the image of a point.
Definition transf.hpp:458
void swap(PTransfBase &that) noexcept
Swap with another partial transformation.
Definition transf.hpp:649
PTransfBase(Container &&cont)
Construct from a container of images.
Definition transf.hpp:197
bool operator!=(PTransfBase const &that) const
Compare for inequality.
Definition transf.hpp:422
iterator end() noexcept
Returns an iterator (random access iterator) pointing one past the last image value.
Definition transf.hpp:605
Permutations with static or dynamic degree.
Definition transf.hpp:1532
static Perm one(size_t M)
Returns the identity transformation on the given number of points.
Definition transf.hpp:1550
Scalar point_type
Type of the image values.
Definition transf.hpp:1539
typename PTransf< N, point_type >::container_type container_type
Type of the underlying container.
Definition transf.hpp:1544
Partial transformations with static degree.
Definition transf.hpp:830
StaticPTransf & increase_degree_by(size_t)
Increase the degree in-place.
Definition transf.hpp:877
const_iterator begin() const noexcept
Returns a const_iterator (random access iterator) pointing at the first image value.
Definition transf.hpp:570
std::array< Scalar, N > container_type
Type of the underlying container.
Definition transf.hpp:840
const_iterator end() const noexcept
Returns a const_iterator (random access iterator) pointing one past the last image value.
Definition transf.hpp:575
StaticPTransf(size_t n)
Construct with given degree.
StaticPTransf()
Default constructor.
Definition transf.hpp:856
Scalar point_type
Type of the image values.
Definition transf.hpp:835
Transformations with static or dynamic degree.
Definition transf.hpp:1010
static Transf one(size_t M)
Returns the identity transformation on the given number of points.
Definition transf.hpp:1061
Scalar point_type
Type of the image values.
Definition transf.hpp:1017
void product_inplace(Transf const &f, Transf const &g)
Multiply two transformations and store the product in this.
typename base_type::container_type container_type
Type of the underlying container.
Definition transf.hpp:1022
T copy(T... args)
T distance(T... args)
T fill(T... args)
T forward(T... args)
std::string to_human_readable_repr(AhoCorasick const &ac)
Return a string representation.
Undefined const UNDEFINED
Value for something undefined.
#define LIBSEMIGROUPS_EXCEPTION(...)
Throw a LibsemigroupsException.
Definition exception.hpp:99
enable_if_is_same< Return, Blocks > make(Container const &cont)
Check the arguments, construct a Blocks object, and check it.
Definition bipart.hpp:798
typename detail::LeastPermHelper< N >::type LeastPerm
Type of perms using the least memory for a given degree.
Definition transf.hpp:2410
static constexpr bool IsPPerm
Helper variable template.
Definition transf.hpp:1338
auto one(T const &f) -> std::enable_if_t< IsDerivedFromPTransf< T >, T >
Returns the identity transformation of same degree as a sample.
Definition transf.hpp:1803
static constexpr bool IsTransf
Helper variable template.
Definition transf.hpp:1094
typename detail::LeastTransfHelper< N >::type LeastTransf
Type of transformations using the least memory for a given degree.
Definition transf.hpp:2380
PPerm< N, Scalar > left_one(PPerm< N, Scalar > const &f)
Returns the left one of a partial perm.
typename detail::LeastPPermHelper< N >::type LeastPPerm
Type of partial perms using the least memory for a given degree.
Definition transf.hpp:2395
void domain(T const &f, std::vector< Scalar > &dom)
Replace the contents of a vector by the set of points where a partial transformation is defined.
static constexpr bool IsPTransf
Helper variable template.
Definition transf.hpp:941
PPerm< N, Scalar > right_one(PPerm< N, Scalar > const &f)
Returns the right one of a partial perm.
static constexpr bool IsDynamic
Helper variable template.
Definition transf.hpp:726
static constexpr bool IsStatic
Helper variable template.
Definition transf.hpp:716
void throw_if_not_pperm(PPerm< N, Scalar > const &f)
Check a partial perm.
Definition transf.hpp:1359
void image(T const &f, std::vector< Scalar > &im)
Replace the contents of a vector by the set of images of a partial transformation.
void throw_if_image_value_out_of_range(PTransfBase< Scalar, Container > const &f)
Check a partial transformation.
Definition transf.hpp:961
std::string to_input_string(Transf< N, Scalar > const &x, std::string_view prefix="", std::string_view braces="{}")
Return a string that can be used to recreate a transformation.
static constexpr bool IsPerm
Helper variable template.
Definition transf.hpp:1575
auto throw_if_not_perm(Perm< N, Scalar > const &f)
Check a permutation.
Definition transf.hpp:1596
static constexpr bool IsDerivedFromPTransf
Helper variable template.
Definition transf.hpp:102
std:: conditional_t< N==0, DynamicPTransf< Scalar >, StaticPTransf< N, Scalar > > PTransf
Partial transformations with static or dynamic degree.
Definition transf.hpp:909
void inverse(PPerm< N, Scalar > const &from, PPerm< N, Scalar > &to)
Replace contents of a partial perm with the inverse of another.
T iota(T... args)
T move(T... args)
Namespace for everything in the libsemigroups library.
Definition action.hpp:44
auto to(detail::KnuthBendixImpl< Rewriter, ReductionOrder > &kb) -> std::enable_if_t< std::is_same_v< Presentation< typename Result::word_type >, Result >, Result >
No doc.
Adapter for the complexity of multiplication.
Definition adapters.hpp:121
Adapter for the degree of an element.
Definition adapters.hpp:159
Adapter for hashing.
Definition adapters.hpp:446
size_t operator()(Value const &x) const
Hash x using std::hash.
Definition adapters.hpp:455
void operator()(PPerm< N, Scalar > &res, PPerm< N, Scalar > const &pt, PPerm< N, Scalar > const &x) const noexcept
Stores the idempotent in res.
Definition transf.hpp:2209
void operator()(T &res, T const &pt, PPerm< N, Scalar > const &x) const
Definition transf.hpp:2233
void operator()(T &res, T const &pt, Transf< N, Scalar > const &x) const
Stores the image of pt under the left action of x in res.
Adapter for the value of a left action.
Definition adapters.hpp:350
void operator()(BitSet< M > &res, BitSet< M > const &pt, PPerm< N, Scalar > const &x) const
Stores the image set of pt under x in res.
void operator()(PPerm< N, Scalar > &res, PPerm< N, Scalar > const &pt, PPerm< N, Scalar > const &x) const noexcept
Stores the idempotent in res.
Definition transf.hpp:2166
void operator()(T &res, T const &pt, PPerm< N, Scalar > const &x) const
Stores the image set of pt under x in res.
void operator()(T &res, T const &pt, Perm< N, Scalar > const &p) const noexcept
Stores the image of pt under the action of p in res.
Definition transf.hpp:2321
T operator()(T pt, Perm< N, Scalar > const &x)
Returns the image of pt under the action of p.
Definition transf.hpp:2329
void operator()(BitSet< M > &res, BitSet< M > const &pt, Transf< N, Scalar > const &x) const
Stores the image set of pt under x in res.
Definition transf.hpp:2030
void operator()(T &res, T const &pt, Transf< N, Scalar > const &x) const
Stores the image set of pt under x in res.
Adapter for the value of a right action.
Definition adapters.hpp:392
void operator()(T &x, size_t n) const
Returns x->increase_degree_by(n).
Definition transf.hpp:2000
Adapter for increasing the degree of an element.
Definition adapters.hpp:199
Adapter for the inverse of an element.
Definition adapters.hpp:319
void operator()(BitSet< M > &res, PPerm< N, Scalar > const &x) const
void operator()(BitSet< M > &res, Transf< N, Scalar > const &x) const
void operator()(T &res, Transf< N, Scalar > const &x) const
Adapter for the action on LambdaValue's.
Definition adapters.hpp:833
BitSet< BitSet< 1 >::max_size()> type
Definition transf.hpp:2257
BitSet< BitSet< 1 >::max_size()> type
Definition transf.hpp:2068
Adapter for lambda functions.
Definition adapters.hpp:793
Adapter for the identity element of the given type.
Definition adapters.hpp:246
Adapter for the product of two elements.
Definition adapters.hpp:284
size_t operator()(PPerm< N, Scalar > const &x) const
Definition transf.hpp:2302
size_t operator()(Transf< N, Scalar > const &x) const
Definition transf.hpp:2149
Adapter for calculating ranks.
Definition adapters.hpp:930
void operator()(BitSet< M > &res, PPerm< N, Scalar > const &x) const
void operator()(T &res, Transf< N, Scalar > const &x) const
Adapter for the action on RhoValue's.
Definition adapters.hpp:854
typename LambdaValue< PPerm< N, Scalar > >::type type
Definition transf.hpp:2269
std::vector< Scalar > type
Definition transf.hpp:2080
Adapter for rho functions.
Definition adapters.hpp:812
T swap(T... args)