1 // 2 // libsemigroups - C++ library for semigroups and monoids 3 // Copyright (C) 2020 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 specializations of various adapters for 20 // derived classes of Element. 21 22 #ifndef LIBSEMIGROUPS_ELEMENT_ADAPTERS_HPP_ 23 #define LIBSEMIGROUPS_ELEMENT_ADAPTERS_HPP_ 24 25 #include "action.hpp" // for RightAction 26 #include "adapters.hpp" // for Complexity etc 27 #include "bitset.hpp" // for BitSet 28 #include "constants.hpp" // for UNDEFINED 29 #include "element.hpp" // for Element 30 #include "konieczny.hpp" // for Konieczny 31 #include "libsemigroups-debug.hpp" // for LIBSEMIGROUPS_ASSERT 32 #include "stl.hpp" // for Hash, EqualTo, Less 33 34 namespace libsemigroups { 35 //////////////////////////////////////////////////////////////////////// 36 // Complexity specializations 37 //////////////////////////////////////////////////////////////////////// 38 39 // Specialization of templates from adapters.hpp for classes derived from 40 // the class Element, such as Transformation<size_t> and so on . . . 41 //! Specialization of the adapter Complexity for pointers to subclasses of 42 //! Element. 43 //! 44 //! \sa Complexity. 45 template <typename TSubclass> 46 struct Complexity<TSubclass*, 47 typename std::enable_if< 48 std::is_base_of<Element, TSubclass>::value>::type> { 49 //! Returns \p x->complexity(). operator ()libsemigroups::Complexity50 inline size_t operator()(TSubclass const* x) const { 51 return x->complexity(); 52 } 53 }; 54 55 //! Specialization of the adapter Complexity for subclasses of 56 //! Element. 57 //! 58 //! \sa Complexity. 59 template <typename TSubclass> 60 struct Complexity<TSubclass, 61 typename std::enable_if< 62 std::is_base_of<Element, TSubclass>::value>::type> { 63 //! Returns \p x.complexity(). operator ()libsemigroups::Complexity64 inline size_t operator()(TSubclass const& x) const { 65 return x.complexity(); 66 } 67 }; 68 69 //////////////////////////////////////////////////////////////////////// 70 // Degree specializations 71 //////////////////////////////////////////////////////////////////////// 72 73 //! Specialization of the adapter Degree for pointers to subclasses of 74 //! Element. 75 //! 76 //! \sa Degree. 77 template <typename TSubclass> 78 struct Degree<TSubclass*, 79 typename std::enable_if< 80 std::is_base_of<Element, TSubclass>::value>::type> { 81 //! Returns \p x->degree(). operator ()libsemigroups::Degree82 inline size_t operator()(TSubclass const* x) const { 83 return x->degree(); 84 } 85 }; 86 87 //! Specialization of the adapter Degree for subclasses of Element. 88 //! 89 //! \sa Degree. 90 template <typename TSubclass> 91 struct Degree< 92 TSubclass, 93 typename std::enable_if<std::is_base_of<Element, TSubclass>::value, 94 void>::type> { 95 //! Returns \p x.degree(). operator ()libsemigroups::Degree96 inline size_t operator()(TSubclass const& x) const { 97 return x.degree(); 98 } 99 }; 100 101 //////////////////////////////////////////////////////////////////////// 102 // IncreaseDegree specializations 103 //////////////////////////////////////////////////////////////////////// 104 105 //! Specialization of the adapter IncreaseDegree for pointers to subclasses 106 //! of Element. 107 //! 108 //! \sa IncreaseDegree. 109 template <typename TSubclass> 110 struct IncreaseDegree<TSubclass*, 111 typename std::enable_if< 112 std::is_base_of<Element, TSubclass>::value>::type> { 113 //! Returns \p x->increase_degree_by(\p n). operator ()libsemigroups::IncreaseDegree114 inline void operator()(TSubclass* x, size_t n) const { 115 return x->increase_degree_by(n); 116 } 117 }; 118 119 // TODO(later) why is there no specialisation for non-pointers for 120 // IncreaseDegree? 121 122 //////////////////////////////////////////////////////////////////////// 123 // Less specializations 124 //////////////////////////////////////////////////////////////////////// 125 126 //! Specialization of the adapter Less for pointers to subclasses 127 //! of Element. 128 //! 129 //! \sa Less. 130 template <typename TSubclass> 131 struct Less<TSubclass*, 132 typename std::enable_if< 133 std::is_base_of<Element, TSubclass>::value>::type> { 134 //! Returns \c true if the object pointed to by \p x is less than the 135 //! object pointed to by \p y. operator ()libsemigroups::Less136 inline bool operator()(TSubclass const* x, TSubclass const* y) const { 137 return *x < *y; 138 } 139 }; 140 141 // TODO(later) why is there no specialisation for non-pointers for 142 // Less? 143 144 //////////////////////////////////////////////////////////////////////// 145 // One specializations 146 //////////////////////////////////////////////////////////////////////// 147 148 //! Specialization of the adapter One for pointers to subclasses 149 //! of Element. 150 //! 151 //! \sa One. 152 template <typename TSubclass> 153 struct One<TSubclass*, 154 typename std::enable_if< 155 std::is_base_of<Element, TSubclass>::value>::type> { 156 //! Returns a pointer to a new instance of the one of \p x. operator ()libsemigroups::One157 TSubclass* operator()(Element const* x) const { 158 // return new TSubclass(std::move(x->identity<TSubclass>())); 159 return static_cast<TSubclass*>(x->heap_identity()); 160 } 161 162 //! Returns a pointer to a new instance of the one of \p x. operator ()libsemigroups::One163 TSubclass* operator()(size_t n) { 164 return new TSubclass(std::move(TSubclass::one(n))); 165 } 166 }; 167 168 //! Specialization of the adapter One for subclasses of Element. 169 //! 170 //! \sa One. 171 template <typename TSubclass> 172 struct One<TSubclass, 173 typename std::enable_if< 174 std::is_base_of<Element, TSubclass>::value>::type> { 175 //! Returns a new instance of the one of \p x. operator ()libsemigroups::One176 TSubclass operator()(TSubclass const& x) const { 177 return static_cast<TSubclass>(x.identity()); 178 } 179 180 //! Returns a new instance of the one of \p x. operator ()libsemigroups::One181 TSubclass operator()(size_t n) { 182 return TSubclass(std::move(TSubclass::identity(n))); 183 } 184 }; 185 186 //////////////////////////////////////////////////////////////////////// 187 // Product specializations 188 //////////////////////////////////////////////////////////////////////// 189 190 //! Specialization of the adapter Product for pointers to subclasses of 191 //! Element. 192 //! 193 //! \sa Product. 194 template <typename TSubclass> 195 struct Product<TSubclass*, 196 typename std::enable_if< 197 std::is_base_of<Element, TSubclass>::value>::type> { 198 //! Changes \p xy in-place to hold the product of \p x and \p y using 199 //! Element::redefine. operator ()libsemigroups::Product200 void operator()(TSubclass* xy, 201 TSubclass const* x, 202 TSubclass const* y, 203 size_t tid = 0) { 204 xy->redefine(*x, *y, tid); 205 } 206 }; 207 208 //! Specialization of the adapter Product for subclasses of Element. 209 //! 210 //! \sa Product. 211 template <typename TSubclass> 212 struct Product<TSubclass, 213 typename std::enable_if< 214 std::is_base_of<Element, TSubclass>::value>::type> { 215 //! Changes \p xy in-place to hold the product of \p x and \p y using 216 //! Element::redefine. operator ()libsemigroups::Product217 void operator()(TSubclass& xy, 218 TSubclass const& x, 219 TSubclass const& y, 220 size_t tid = 0) { 221 xy.redefine(x, y, tid); 222 } 223 }; 224 225 //////////////////////////////////////////////////////////////////////// 226 // Swap specializations 227 //////////////////////////////////////////////////////////////////////// 228 229 //! Specialization of the adapter Swap for subclasses of Element. 230 //! 231 //! \sa Swap. 232 template <typename TSubclass> 233 struct Swap<TSubclass*, 234 typename std::enable_if< 235 std::is_base_of<Element, TSubclass>::value>::type> { 236 //! Swaps the object pointed to by \p x with the one pointed to by \p y. operator ()libsemigroups::Swap237 void operator()(TSubclass* x, TSubclass* y) const { 238 x->swap(*y); 239 } 240 }; 241 242 // TODO(later) why is there no specialisation for non-pointers for 243 // Swap? 244 245 //////////////////////////////////////////////////////////////////////// 246 // Inverse specializations 247 //////////////////////////////////////////////////////////////////////// 248 249 //! Specialization of the adapter Inverse for pointers to 250 //! Permutation instances. 251 //! 252 //! \sa Inverse. 253 template <typename TValueType> 254 struct Inverse<Permutation<TValueType>*> { 255 //! Returns a pointer to a new instance of the inverse of \p x. operator ()libsemigroups::Inverse256 Permutation<TValueType>* operator()(Permutation<TValueType>* x) { 257 return new Permutation<TValueType>(std::move(x->inverse())); 258 } 259 }; 260 261 //! Specialization of the adapter Inverse for Permutation instances. 262 //! 263 //! \sa Inverse. 264 template <typename TValueType> 265 struct Inverse<Permutation<TValueType>> { 266 //! Returns the inverse of \p x. operator ()libsemigroups::Inverse267 Permutation<TValueType> operator()(Permutation<TValueType> const& x) { 268 return x.inverse(); 269 } 270 }; 271 272 //////////////////////////////////////////////////////////////////////// 273 // Hash specializations 274 //////////////////////////////////////////////////////////////////////// 275 276 //! Specialization of the adapter Hash for subclasses of Element. 277 //! 278 //! \sa Hash. 279 template <typename TSubclass> 280 struct Hash<TSubclass, 281 typename std::enable_if< 282 std::is_base_of<Element, TSubclass>::value>::type> { 283 //! Returns \p x.hash_value() operator ()libsemigroups::Hash284 size_t operator()(TSubclass const& x) const { 285 return x.hash_value(); 286 } 287 }; 288 289 //! Specialization of the adapter Hash for pointers to subclasses of Element. 290 //! 291 //! \sa Hash. 292 template <typename TSubclass> 293 struct Hash<TSubclass*, 294 typename std::enable_if< 295 std::is_base_of<Element, TSubclass>::value>::type> { 296 //! Returns \p x->hash_value() operator ()libsemigroups::Hash297 size_t operator()(TSubclass const* x) const { 298 return x->hash_value(); 299 } 300 }; 301 302 //////////////////////////////////////////////////////////////////////// 303 // EqualTo specializations 304 //////////////////////////////////////////////////////////////////////// 305 306 //! Specialization of the adapter EqualTo for pointers to subclasses of 307 //! Element. 308 //! 309 //! \sa EqualTo. 310 template <typename TSubclass> 311 struct EqualTo< 312 TSubclass*, 313 typename std::enable_if< 314 std::is_base_of<libsemigroups::Element, TSubclass>::value>::type> { 315 //! Tests equality of two const Element pointers via equality of the 316 //! Elements they point to. operator ()libsemigroups::EqualTo317 bool operator()(TSubclass const* x, TSubclass const* y) const { 318 return *x == *y; 319 } 320 }; 321 322 // TODO(later) why is there no specialisation for non-pointers for 323 // EqualTo? 324 325 //////////////////////////////////////////////////////////////////////// 326 // ImageRight/LeftAction - PartialPerm 327 //////////////////////////////////////////////////////////////////////// 328 329 // Slowest 330 //! Specialization of the adapter ImageRightAction for instances of 331 //! PartialPerm. 332 //! 333 //! \sa ImageRightAction 334 template <typename T> 335 struct ImageRightAction<PartialPerm<T>, PartialPerm<T>> { 336 //! Stores the idempotent \f$(xy) ^ {-1}xy\f$ in \p res. operator ()libsemigroups::ImageRightAction337 void operator()(PartialPerm<T>& res, 338 PartialPerm<T> const& pt, 339 PartialPerm<T> const& x) const noexcept { 340 res.redefine(pt, x); 341 res.swap(res.right_one()); 342 } 343 }; 344 345 // Faster than the above, but slower than the below 346 // works for T = std::vector and T = StaticVector1 347 //! Specialization of the adapter ImageRightAction for instances of 348 //! PartialPerm and std::vector. 349 //! 350 //! \sa ImageRightAction 351 template <typename S, typename T> 352 struct ImageRightAction<PartialPerm<S>, T> { 353 //! Stores the image set of \c pt under \c x in \p res. operator ()libsemigroups::ImageRightAction354 void operator()(T& res, T const& pt, PartialPerm<S> const& x) const { 355 res.clear(); 356 for (auto i : pt) { 357 if (x[i] != UNDEFINED) { 358 res.push_back(x[i]); 359 } 360 } 361 std::sort(res.begin(), res.end()); 362 } 363 }; 364 365 // Fastest, but limited to at most degree 64 366 //! Specialization of the adapter ImageRightAction for instances of 367 //! PartialPerm and BitSet<N> (\f$0 \leq N \leq 64\f$). 368 //! 369 //! \sa ImageRightAction 370 template <typename T, size_t N> 371 struct ImageRightAction<PartialPerm<T>, BitSet<N>> { 372 //! Stores the image set of \c pt under \c x in \p res. operator ()libsemigroups::ImageRightAction373 void operator()(BitSet<N>& res, 374 BitSet<N> const& pt, 375 PartialPerm<T> const& x) const { 376 res.reset(); 377 // Apply the lambda to every set bit in pt 378 pt.apply([&x, &res](size_t i) { 379 if (x[i] != UNDEFINED) { 380 res.set(x[i]); 381 } 382 }); 383 } 384 }; 385 386 // Slowest 387 //! Specialization of the adapter ImageLeftAction for instances of 388 //! PartialPerm. 389 //! 390 //! \sa ImageLeftAction. 391 template <typename T> 392 struct ImageLeftAction<PartialPerm<T>, PartialPerm<T>> { 393 //! Stores the idempotent \f$xy(xy) ^ {-1}\f$ in \p res. operator ()libsemigroups::ImageLeftAction394 void operator()(PartialPerm<T>& res, 395 PartialPerm<T> const& pt, 396 PartialPerm<T> const& x) const noexcept { 397 res.redefine(x, pt); 398 res.swap(res.left_one()); 399 } 400 }; 401 402 // Fastest when used with BitSet<N>. 403 // works for T = std::vector and T = BitSet<N> 404 // Using BitSet<N> limits this to size 64. However, if we are trying to 405 // compute a LeftAction object, then the max size of such is 2 ^ 64, which is 406 // probably not achievable. So, for higher degrees, we will only be able to 407 // compute relatively sparse LeftActions (i.e. not containing the majority of 408 // the 2 ^ n possible subsets), in which case using vectors or 409 // StaticVector1's might be not be appreciable slower anyway. All of this is 410 // to say that it probably isn't worthwhile trying to make BitSet's work for 411 // more than 64 bits. 412 //! Specialization of the adapter ImageLeftAction for instances of 413 //! PartialPerm and std::vector or BitSet<N> (\f$0 \leq N \leq 64\f$). 414 //! 415 //! \sa ImageLeftAction. 416 template <typename S, typename T> 417 struct ImageLeftAction<PartialPerm<S>, T> { operator ()libsemigroups::ImageLeftAction418 void operator()(T& res, T const& pt, PartialPerm<S> const& x) const { 419 //! Stores the inverse image set of \c pt under \c x in \p res. 420 static PartialPerm<S> xx({}); 421 x.inverse(xx); 422 ImageRightAction<PartialPerm<S>, T>()(res, pt, xx); 423 } 424 }; 425 426 //////////////////////////////////////////////////////////////////////// 427 // Lambda/Rho - PartialPerm 428 //////////////////////////////////////////////////////////////////////// 429 430 // This currently limits the use of Konieczny to partial perms of degree at 431 // most 64 with the default traits class, since we cannot know the degree 432 // at compile time, only at run time. 433 //! Specialization of the adapter LambdaValue for instances of PartialPerm. 434 //! Note that the the type chosen here limits the Konieczny algorithm to 435 //! PartialPerms of degree at most 64 (or 32 on 32-bit systems). 436 //! 437 //! \sa LambdaValue. 438 template <typename T> 439 struct LambdaValue<PartialPerm<T>> { 440 static constexpr size_t N = BitSet<1>::max_size(); 441 //! For PartialPerms, \c type is BitSet<N>, representing the image of the 442 //! PartialPerms. 443 using type = BitSet<N>; 444 }; 445 446 //! Specialization of the adapter RhoValue for instances of PartialPerm. 447 //! Note that the the type chosen here limits the Konieczny algorithm to 448 //! PartialPerms of degree at most 64 (or 32 on 32-bit systems). 449 //! 450 //! \sa RhoValue. 451 template <typename T> 452 struct RhoValue<PartialPerm<T>> { 453 //! For PartialPerms, \c type is BitSet<N>, representing the domain of the 454 //! PartialPerms. 455 using type = typename LambdaValue<PartialPerm<T>>::type; 456 }; 457 458 //! Specialization of the adapter Lambda for instances of PartialPerm and 459 //! BitSet<N>. 460 //! 461 //! \sa Lambda. 462 template <typename T, size_t N> 463 struct Lambda<PartialPerm<T>, BitSet<N>> { 464 //! Modifies \p res to contain the image set of \p x; that is, \p res[i] 465 //! will be \c true if and only if `x[j] = i` for some \f$j\f$. operator ()libsemigroups::Lambda466 void operator()(BitSet<N>& res, PartialPerm<T> const& x) const { 467 if (x.degree() > N) { 468 LIBSEMIGROUPS_EXCEPTION( 469 "expected partial perm of degree at most %llu, found %llu", 470 static_cast<uint64_t>(N), 471 static_cast<uint64_t>(x.degree())); 472 } 473 res.reset(); 474 for (size_t i = 0; i < x.degree(); ++i) { 475 if (x[i] != UNDEFINED) { 476 res.set(x[i]); 477 } 478 } 479 } 480 }; 481 482 //! Specialization of the adapter Rho for instances of PartialPerm and 483 //! BitSet<N>. 484 //! 485 //! \sa Rho. 486 template <typename T, size_t N> 487 struct Rho<PartialPerm<T>, BitSet<N>> { 488 //! Modifies \p res to contain the domain of \p x; that is, \p res[i] 489 //! will be \c true if and only if `x[i] != UNDEFINED`. operator ()libsemigroups::Rho490 void operator()(BitSet<N>& res, PartialPerm<T> const& x) const { 491 if (x.degree() > N) { 492 LIBSEMIGROUPS_EXCEPTION( 493 "expected partial perm of degree at most %llu, found %llu", 494 static_cast<uint64_t>(N), 495 static_cast<uint64_t>(x.degree())); 496 } 497 static PartialPerm<T> xx({}); 498 x.inverse(xx); 499 Lambda<PartialPerm<T>, BitSet<N>>()(res, xx); 500 } 501 }; 502 503 //! Specialization of the adapter Rank for instances of PartialPerm. 504 //! 505 //! \sa Rank and PartialPerm::crank. 506 template <typename T> 507 struct Rank<PartialPerm<T>> { 508 //! Returns the rank of \p x. 509 //! 510 //! The rank of a PartialPerm is the number of points in the image. operator ()libsemigroups::Rank511 size_t operator()(PartialPerm<T> const& x) const { 512 return x.crank(); 513 } 514 }; 515 516 //////////////////////////////////////////////////////////////////////// 517 // ImageRight/LeftAction - Transformation 518 //////////////////////////////////////////////////////////////////////// 519 520 // Equivalent to OnSets in GAP 521 // Slowest 522 // works for T = std::vector and T = StaticVector1 523 //! Specialization of the adapter ImageRightAction for instances of 524 //! Transformation and std::vector. 525 //! 526 //! \sa ImageRightAction 527 template <typename S, typename T> 528 struct ImageRightAction<Transformation<S>, T> { 529 //! Stores the image set of \c pt under \c x in \p res. operator ()libsemigroups::ImageRightAction530 void operator()(T& res, T const& pt, Transformation<S> const& x) const { 531 res.clear(); 532 for (auto i : pt) { 533 res.push_back(x[i]); 534 } 535 std::sort(res.begin(), res.end()); 536 res.erase(std::unique(res.begin(), res.end()), res.end()); 537 } 538 }; 539 540 // Fastest, but limited to at most degree 64 541 //! Specialization of the adapter ImageRightAction for instances of 542 //! Transformation and BitSet<N> (\f$0 \leq N leq 64\f$). 543 //! 544 //! \sa ImageRightAction 545 template <typename TIntType, size_t N> 546 struct ImageRightAction<Transformation<TIntType>, BitSet<N>> { 547 //! Stores the image set of \c pt under \c x in \p res. operator ()libsemigroups::ImageRightAction548 void operator()(BitSet<N>& res, 549 BitSet<N> const& pt, 550 Transformation<TIntType> const& x) const { 551 res.reset(); 552 // Apply the lambda to every set bit in pt 553 pt.apply([&x, &res](size_t i) { res.set(x[i]); }); 554 } 555 }; 556 557 // OnKernelAntiAction 558 // T = StaticVector1<S> or std::vector<S> 559 //! Specialization of the adapter ImageLeftAction for instances of 560 //! Transformation and std::vector. 561 //! 562 //! \sa ImageLeftAction 563 template <typename S, typename T> 564 struct ImageLeftAction<Transformation<S>, T> { 565 //! Stores the image of \p pt under the left action of \p x in \p res. operator ()libsemigroups::ImageLeftAction566 void operator()(T& res, T const& pt, Transformation<S> const& x) const { 567 res.clear(); 568 res.resize(x.degree()); 569 static thread_local std::vector<S> buf; 570 buf.clear(); 571 buf.resize(x.degree(), S(UNDEFINED)); 572 S next = 0; 573 574 for (size_t i = 0; i < res.size(); ++i) { 575 if (buf[pt[x[i]]] == UNDEFINED) { 576 buf[pt[x[i]]] = next++; 577 } 578 res[i] = buf[pt[x[i]]]; 579 } 580 } 581 }; 582 583 //////////////////////////////////////////////////////////////////////// 584 // Lambda/Rho - Transformation 585 //////////////////////////////////////////////////////////////////////// 586 587 // This currently limits the use of Konieczny to transformation of degree at 588 // most 64 with the default traits class, since we cannot know the degree at 589 // compile time, only at run time. 590 //! Specialization of the adapter LambdaValue for instances of Transformation. 591 //! Note that the the type chosen here limits the Konieczny algorithm to 592 //! Transformations of degree at most 64 (or 32 on 32-bit systems). 593 //! 594 //! \sa LambdaValue. 595 template <typename T> 596 struct LambdaValue<Transformation<T>> { 597 //! For Transformations, \c type is the largest BitSet available, 598 //! representing the image set of the Transformations. 599 static constexpr size_t N = BitSet<1>::max_size(); 600 using type = BitSet<N>; 601 }; 602 603 // Benchmarks indicate that using std::vector yields similar performance to 604 // using StaticVector1's. 605 //! Specialization of the adapter RhoValue for instances of Transformation. 606 //! 607 //! \sa RhoValue. 608 template <typename T> 609 struct RhoValue<Transformation<T>> { 610 //! For Transformation<T>s, \c type is std::vector<T>, representing the 611 //! kernel of the Transformations. 612 using type = std::vector<T>; 613 }; 614 615 // T = std::vector or StaticVector1 616 //! Specialization of the adapter Lambda for instances of Transformation and 617 //! std::vector. 618 //! 619 //! \sa Lambda. 620 template <typename S, typename T> 621 struct Lambda<Transformation<S>, T> { 622 // not noexcept because std::vector::resize isn't (although 623 // StaticVector1::resize is). 624 //! Modifies \p res to contain the image set of \p x; that is, \p res[i] 625 //! will be \c true if and only if `x[j] = i` for some \f$j\f$. operator ()libsemigroups::Lambda626 void operator()(T& res, Transformation<S> const& x) const { 627 res.clear(); 628 res.resize(x.degree()); 629 for (size_t i = 0; i < res.size(); ++i) { 630 res[i] = x[i]; 631 } 632 std::sort(res.begin(), res.end()); 633 res.erase(std::unique(res.begin(), res.end()), res.end()); 634 } 635 }; 636 637 //! Specialization of the adapter Lambda for instances of Transformation and 638 //! BitSet<N>. 639 //! 640 //! \sa Lambda. 641 template <typename T, size_t N> 642 struct Lambda<Transformation<T>, BitSet<N>> { 643 // not noexcept because it can throw 644 //! Modifies \p res to contain the image set of \p x; that is, \p res[i] 645 //! will be \c true if and only if `x[j] = i` for some \f$j\f$. operator ()libsemigroups::Lambda646 void operator()(BitSet<N>& res, Transformation<T> const& x) const { 647 if (x.degree() > N) { 648 LIBSEMIGROUPS_EXCEPTION( 649 "expected a transformation of degree at most %llu, found %llu", 650 static_cast<uint64_t>(N), 651 static_cast<uint64_t>(x.degree())); 652 } 653 res.reset(); 654 for (size_t i = 0; i < x.degree(); ++i) { 655 res.set(x[i]); 656 } 657 } 658 }; 659 660 // T = std::vector<S> or StaticVector1<S, N> 661 //! Specialization of the adapter Rho for instances of Transformation and 662 //! std::vector. 663 //! 664 //! \sa Rho. 665 template <typename S, typename T> 666 struct Rho<Transformation<S>, T> { 667 // not noexcept because std::vector::resize isn't (although 668 // StaticVector1::resize is). operator ()libsemigroups::Rho669 void operator()(T& res, Transformation<S> const& x) const { 670 res.clear(); 671 res.resize(x.degree()); 672 static thread_local std::vector<S> buf; 673 buf.clear(); 674 buf.resize(x.degree(), S(UNDEFINED)); 675 S next = 0; 676 677 for (size_t i = 0; i < res.size(); ++i) { 678 if (buf[x[i]] == UNDEFINED) { 679 buf[x[i]] = next++; 680 } 681 res[i] = buf[x[i]]; 682 } 683 } 684 }; 685 686 //! Specialization of the adapter Rank for instances of Transformation. 687 //! 688 //! \sa Rank. 689 template <typename T> 690 struct Rank<Transformation<T>> { operator ()libsemigroups::Rank691 size_t operator()(Transformation<T> const& x) { 692 return x.crank(); 693 } 694 }; 695 696 //////////////////////////////////////////////////////////////////////// 697 // ImageRightAction - Permutation specialisation 698 //////////////////////////////////////////////////////////////////////// 699 700 //! Specialization of the adapter ImageRightAction for pointers to 701 //! Permutation instances. 702 //! 703 //! \sa ImageRightAction. 704 template <typename TValueType> 705 struct ImageRightAction< 706 Permutation<TValueType>*, 707 TValueType, 708 typename std::enable_if<std::is_integral<TValueType>::value>::type> { 709 //! Returns the image of \p pt under \p x. operator ()libsemigroups::ImageRightAction710 TValueType operator()(Permutation<TValueType> const* x, 711 TValueType const pt) { 712 return (*x)[pt]; 713 } 714 }; 715 716 //! Specialization of the adapter ImageRightAction for instances of 717 //! Permutation. 718 //! 719 //! \sa ImageRightAction. 720 template <typename TIntType> 721 struct ImageRightAction< 722 Permutation<TIntType>, 723 TIntType, 724 typename std::enable_if<std::is_integral<TIntType>::value>::type> { 725 //! Stores the image of \p pt under the action of \p p in \p res. operator ()libsemigroups::ImageRightAction726 void operator()(TIntType& res, 727 TIntType const& pt, 728 Permutation<TIntType> const& p) const noexcept { 729 LIBSEMIGROUPS_ASSERT(pt < p.degree()); 730 res = p[pt]; 731 } 732 733 //! Returns the image of \p pt under the action of \p p. operator ()libsemigroups::ImageRightAction734 TIntType operator()(TIntType const pt, Permutation<TIntType> const& x) { 735 return x[pt]; 736 } 737 }; 738 739 //////////////////////////////////////////////////////////////////////// 740 // ImageRight/LeftAction - BooleanMat 741 //////////////////////////////////////////////////////////////////////// 742 743 // T = StaticVector1<BitSet<N>, N> or std::vector<BitSet<N>> 744 // Possibly further container when value_type is BitSet. 745 //! Specialization of the adapter ImageRightAction for instances of 746 //! BooleanMat and vectors of BitSet<N> (\f$0 \leq N leq 64\f$). 747 //! 748 //! \sa ImageRightAction 749 template <typename T> 750 struct ImageRightAction<BooleanMat, T> { 751 // not noexcept because BitSet<N>::apply isn't 752 //! Stores the image of \p pt under the right action of \p p in \p res. operator ()libsemigroups::ImageRightAction753 void operator()(T& res, T const& pt, BooleanMat const& x) const { 754 using value_type = typename T::value_type; 755 // TODO(later): 756 // static_assert(std::is_same<BitSet, value_type>::value(), 757 // "the template paramater T must be a container of 758 // BitSet's"); 759 // The above doesn't work because BitSet requires template parameters 760 res.clear(); 761 762 for (auto const& v : pt) { 763 value_type cup; 764 cup.reset(); 765 v.apply([&x, &cup](size_t i) { 766 for (size_t j = 0; j < x.degree(); ++j) { 767 cup.set(j, cup[j] || x[i * x.degree() + j]); 768 } 769 }); 770 res.push_back(std::move(cup)); 771 } 772 booleanmat_helpers::rows_basis<T>(res); 773 } 774 }; 775 776 //! Specialization of the adapter ImageRightAction for instances of 777 //! BooleanMat and std::vector<std::vector<bool>>. 778 //! 779 //! \sa ImageRightAction 780 template <> 781 struct ImageRightAction<BooleanMat, std::vector<std::vector<bool>>> { 782 // not noexcept because the constructor of std::vector isn't 783 //! Stores the image of \p pt under the right action of \p p in \p res. operator ()libsemigroups::ImageRightAction784 void operator()(std::vector<std::vector<bool>>& res, 785 std::vector<std::vector<bool>> const& pt, 786 BooleanMat const& x) const { 787 res.clear(); 788 789 for (auto it = pt.cbegin(); it < pt.cend(); ++it) { 790 std::vector<bool> cup(x.degree(), false); 791 for (size_t i = 0; i < x.degree(); ++i) { 792 if ((*it)[i]) { 793 for (size_t j = 0; j < x.degree(); ++j) { 794 cup[j] = cup[j] || x[i * x.degree() + j]; 795 } 796 } 797 } 798 res.push_back(std::move(cup)); 799 } 800 booleanmat_helpers::rows_basis(res); 801 } 802 }; 803 804 //! Specialization of the adapter ImageLeftAction for instances of 805 //! BooleanMat and std::vector<std::vector<bool>> or std::vector<BitSet<N>> 806 //! (\f$0 \leq N leq 64\f$). 807 //! 808 //! \sa ImageLeftAction 809 template <typename T> 810 struct ImageLeftAction<BooleanMat, T> { 811 //! Stores the image of \p pt under the left action of \p p in \p res. operator ()libsemigroups::ImageLeftAction812 void operator()(T& res, T const& pt, BooleanMat const& x) const { 813 const_cast<BooleanMat*>(&x)->transpose_in_place(); 814 // What if ImageRightAction throws? 815 ImageRightAction<BooleanMat, T>()(res, pt, x); 816 const_cast<BooleanMat*>(&x)->transpose_in_place(); 817 } 818 }; 819 820 //////////////////////////////////////////////////////////////////////// 821 // Lambda/Rho - BooleanMat 822 //////////////////////////////////////////////////////////////////////// 823 824 // This currently limits the use of Konieczny to matrices of dimension at 825 // most 64 with the default traits class, since we cannot know the dimension 826 // of the matrices at compile time, only at run time. 827 //! Specialization of the adapter LambdaValue for instances of BooleanMat. 828 //! Note that the the type chosen here limits the Konieczny algorithm to 829 //! BooleanMats of degree at most 64 (or 32 on 32-bit systems). 830 //! 831 //! \sa LambdaValue. 832 template <> 833 struct LambdaValue<BooleanMat> { 834 static constexpr size_t N = BitSet<1>::max_size(); 835 //! For BooleanMats, \c type is StaticVector1<BitSet<N>, N>, where \c N is 836 //! the maximum width of BitSet on the system. This represents the row space 837 //! basis of the BooleanMats. 838 using type = detail::StaticVector1<BitSet<N>, N>; 839 }; 840 841 //! Specialization of the adapter RhoValue for instances of BooleanMat. 842 //! Note that the the type chosen here limits the Konieczny algorithm to 843 //! BooleanMats of degree at most 64 (or 32 on 32-bit systems). 844 //! 845 //! \sa RhoValue. 846 template <> 847 struct RhoValue<BooleanMat> { 848 //! For BooleanMats, \c type is StaticVector1<BitSet<N>, N>, where \c N is 849 //! the maximum width of BitSet on the system. This represents the column 850 //! space basis of the BooleanMats. 851 using type = typename LambdaValue<BooleanMat>::type; 852 }; 853 854 // Benchmarks show that the following is the fastest (i.e. duplicating the 855 // code from ImageRightAction, and using StaticVector1). 856 // T = StaticVector1<BitSet>, std::vector<BitSet>, 857 // StaticVector1<std::bitset>, std::vector<std::bitset> 858 //! Specialization of the adapter Lambda for instances of Transformation and 859 //! std::vector<BitSet<N>> or StaticVector1<BitSet<N>>. 860 //! 861 //! \sa Lambda. 862 template <typename T> 863 struct Lambda<BooleanMat, T> { 864 //! Modifies \p res to contain the row space basis of \p x. operator ()libsemigroups::Lambda865 void operator()(T& res, BooleanMat const& x) const { 866 using S = typename T::value_type; 867 size_t const N = S().size(); 868 if (x.degree() > N) { 869 LIBSEMIGROUPS_EXCEPTION( 870 "expected matrix of dimension at most %llu, found %llu", 871 static_cast<uint64_t>(N), 872 static_cast<uint64_t>(x.degree())); 873 } 874 res.clear(); 875 for (size_t i = 0; i < x.degree(); ++i) { 876 S cup; 877 cup.reset(); 878 for (size_t j = 0; j < x.degree(); ++j) { 879 cup.set(j, x[i * x.degree() + j]); 880 } 881 res.push_back(std::move(cup)); 882 } 883 booleanmat_helpers::rows_basis<T>(res); 884 } 885 }; 886 887 // T = StaticVector1<BitSet>, std::vector<BitSet>, 888 // StaticVector1<std::bitset>, std::vector<std::bitset> 889 //! Specialization of the adapter Rho for instances of Transformation and 890 //! std::vector<BitSet<N>> or StaticVector1<BitSet<N>>. 891 //! 892 //! \sa Lambda. 893 template <typename T> 894 struct Rho<BooleanMat, T> { 895 //! Modifies \p res to contain the column space basis of \p x. operator ()libsemigroups::Rho896 void operator()(T& res, BooleanMat const& x) const noexcept { 897 const_cast<BooleanMat*>(&x)->transpose_in_place(); 898 Lambda<BooleanMat, T>()(res, x); 899 const_cast<BooleanMat*>(&x)->transpose_in_place(); 900 } 901 }; 902 903 //////////////////////////////////////////////////////////////////////// 904 // Rank - BooleanMat 905 //////////////////////////////////////////////////////////////////////// 906 907 // Version of ImageRightAction where we convert the matrix to bitsets 908 //! Specialization of the adapter ImageRightAction for instances of 909 //! BooleanMat and BitSet<N>. 910 //! 911 //! \sa ImageRightAction. 912 template <size_t N> 913 struct ImageRightAction<BooleanMat, BitSet<N>> { 914 using result_type = BitSet<N>; 915 //! Stores the image of \p pt under the right action of \p p in \p res. operator ()libsemigroups::ImageRightAction916 void operator()(result_type& res, 917 result_type const& pt, 918 BooleanMat const& x) const { 919 static thread_local detail::StaticVector1<BitSet<N>, N> x_rows; 920 x_rows.clear(); 921 for (size_t i = 0; i < x.degree(); ++i) { 922 BitSet<N> row(0); 923 for (size_t j = 0; j < x.degree(); ++j) { 924 if (x[i * x.degree() + j]) { 925 row.set(j, true); 926 } 927 } 928 x_rows.push_back(std::move(row)); 929 } 930 res.reset(); 931 pt.apply([&res](size_t i) { res |= x_rows[i]; }); 932 } 933 }; 934 935 //! Specialization of the adapter RankState for instances of BooleanMat. 936 //! 937 //! \sa RankState. 938 template <> 939 class RankState<BooleanMat> { 940 using MaxBitSet = BitSet<BitSet<1>::max_size()>; 941 942 public: 943 //! The additional data used by Rank<BooleanMat> is the orbit of the rows of 944 //! the semigroup. 945 using type = RightAction<BooleanMat, 946 MaxBitSet, 947 ImageRightAction<BooleanMat, MaxBitSet>>; 948 949 //! Default constructor. RankState()950 RankState() : _orb() {} 951 952 //! Construct from const iterators to the generators of the semigroup. 953 template <typename T> RankState(T first,T last)954 RankState(T first, T last) : _orb() { 955 static thread_local std::vector<MaxBitSet> seeds; 956 LIBSEMIGROUPS_ASSERT(_orb.empty()); 957 if (std::distance(first, last) == 0) { 958 LIBSEMIGROUPS_EXCEPTION( 959 "expected a positive number of generators in the second argument"); 960 } 961 for (auto it = first; it < last; ++it) { 962 _orb.add_generator(*it); 963 } 964 for (size_t i = 0; i < first->degree(); ++i) { 965 MaxBitSet seed(0); 966 seed.set(i, true); 967 _orb.add_seed(seed); 968 } 969 } 970 971 // Other constructors are deleted. 972 RankState(RankState const&) = delete; 973 RankState(RankState&&) = delete; 974 RankState& operator=(RankState const&) = delete; 975 RankState& operator=(RankState&&) = delete; 976 977 //! Returns the row orbit. get() const978 type const& get() const { 979 _orb.run(); 980 LIBSEMIGROUPS_ASSERT(_orb.finished()); 981 return _orb; 982 } 983 984 private: 985 mutable type _orb; 986 }; 987 988 //! Specialization of the adapter Rank for instances of BooleanMat 989 //! 990 //! \sa Rank. 991 template <> 992 struct Rank<BooleanMat> { 993 //! Returns the rank of \p x. 994 //! 995 //! The rank of a BooleanMat may be defined as the rank of the 996 //! Transformation obtained via the action of the BooleanMat on the row 997 //! orbit of the semigroup. operator ()libsemigroups::Rank998 size_t operator()(RankState<BooleanMat> const& state, 999 BooleanMat const& x) const { 1000 using bitset_type = BitSet<BitSet<1>::max_size()>; 1001 using orb_type = typename RankState<BooleanMat>::type; 1002 static thread_local std::vector<bool> seen; 1003 static thread_local std::vector<bitset_type> x_rows; 1004 seen.clear(); 1005 x_rows.clear(); 1006 orb_type const& orb = state.get(); 1007 LIBSEMIGROUPS_ASSERT(orb.finished()); 1008 seen.resize(orb.current_size()); 1009 for (size_t i = 0; i < x.degree(); ++i) { 1010 bitset_type row(0); 1011 for (size_t j = 0; j < x.degree(); ++j) { 1012 if (x[i * x.degree() + j]) { 1013 row.set(j, true); 1014 } 1015 } 1016 x_rows.push_back(std::move(row)); 1017 } 1018 size_t rnk = 0; 1019 for (size_t i = 0; i < orb.current_size(); ++i) { 1020 bitset_type const& row = orb[i]; 1021 bitset_type cup; 1022 cup.reset(); 1023 row.apply([&cup](size_t j) { cup |= x_rows[j]; }); 1024 size_t pos = orb.position(cup); 1025 LIBSEMIGROUPS_ASSERT(pos != UNDEFINED); 1026 if (!seen[pos]) { 1027 rnk++; 1028 seen[pos] = true; 1029 } 1030 } 1031 return rnk; 1032 } 1033 }; 1034 } // namespace libsemigroups 1035 #endif // LIBSEMIGROUPS_ELEMENT_ADAPTERS_HPP_ 1036