libsemigroups  v3.5.5
C++ library for semigroups and monoids
Loading...
Searching...
No Matches
matrix.hpp
1//
2// libsemigroups - C++ library for semigroups and monoids
3// Copyright (C) 2020-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(1) tpp file
20// TODO(1) put the detail stuff into detail/matrix-common.hpp
21// TODO(1) there're no complete set of init methods for matrices
22
23#ifndef LIBSEMIGROUPS_MATRIX_HPP_
24#define LIBSEMIGROUPS_MATRIX_HPP_
25
26#include <algorithm> // for min
27#include <array> // for array
28#include <bitset> // for bitset
29#include <cstddef> // for size_t
30#include <cstdint> // for uint64_t
31#include <initializer_list> // for initializer_list
32#include <iosfwd> // for ostringstream
33#include <iterator> // for distance
34#include <numeric> // for inner_product
35#include <ostream> // for operator<<, basic_ostream
36#include <string> // for string
37#include <tuple> // for tie
38#include <type_traits> // for false_type, is_signed, true_type
39#include <unordered_map> // for unordered_map
40#include <unordered_set> // for unordered_set
41#include <utility> // for forward, make_pair, pair
42#include <vector> // for vector
43
44#include "adapters.hpp" // for Degree
45#include "bitset.hpp" // for BitSet
46#include "config.hpp" // for LIBSEMIGROUPS_PARSED_BY_DOXYGEN
47#include "constants.hpp" // for POSITIVE_INFINITY
48#include "debug.hpp" // for LIBSEMIGROUPS_ASSERT
49#include "exception.hpp" // for LIBSEMIGROUPS_EXCEPTION
50
51#include "detail/containers.hpp" // for StaticVector1
52#include "detail/formatters.hpp" // for formatter of POSITIVE_INFINITY ...
53#include "detail/string.hpp" // for detail::to_string
54
55namespace libsemigroups {
56
128
130 // Detail
132
133 namespace detail {
134
135 template <typename T>
136 struct IsStdBitSetHelper : std::false_type {};
137
138 template <size_t N>
139 struct IsStdBitSetHelper<std::bitset<N>> : std::true_type {};
140
141 template <typename T>
142 static constexpr bool IsStdBitSet = IsStdBitSetHelper<T>::value;
143
144 struct MatrixPolymorphicBase {};
145
146 template <typename T>
147 struct IsMatrixHelper {
148 static constexpr bool value
149 = std::is_base_of<detail::MatrixPolymorphicBase, T>::value;
150 };
151 } // namespace detail
152
167 template <typename T>
168 constexpr bool IsMatrix = detail::IsMatrixHelper<T>::value;
169
170 namespace matrix {
171
183 template <typename Mat>
184 auto throw_if_not_square(Mat const& x) -> std::enable_if_t<IsMatrix<Mat>> {
185 if (x.number_of_rows() != x.number_of_cols()) {
186 LIBSEMIGROUPS_EXCEPTION("expected a square matrix, but found {}x{}",
187 x.number_of_rows(),
188 x.number_of_cols());
189 }
190 }
191
205 template <typename Mat>
206 auto throw_if_bad_dim(Mat const& x, Mat const& y)
207 -> std::enable_if_t<IsMatrix<Mat>> {
208 if (x.number_of_rows() != y.number_of_rows()
209 || x.number_of_cols() != y.number_of_cols()) {
211 "expected matrices with the same dimensions, the 1st argument is a "
212 "{}x{} matrix, and the 2nd is a {}x{} matrix",
213 x.number_of_rows(),
214 x.number_of_cols(),
215 y.number_of_rows(),
216 y.number_of_cols());
217 }
218 }
219
234 template <typename Mat>
235 auto throw_if_bad_coords(Mat const& x, size_t r, size_t c)
236 -> std::enable_if_t<IsMatrix<Mat>> {
237 if (r >= x.number_of_rows()) {
238 LIBSEMIGROUPS_EXCEPTION("invalid row index in ({}, {}), expected "
239 "values in [0, {}) x [0, {})",
240 r,
241 c,
242 x.number_of_rows(),
243 x.number_of_cols(),
244 r);
245 }
246 if (c >= x.number_of_cols()) {
247 LIBSEMIGROUPS_EXCEPTION("invalid column index in ({}, {}), expected "
248 "values in [0, {}) x [0, {})",
249 r,
250 c,
251 x.number_of_rows(),
252 x.number_of_cols(),
253 r);
254 }
255 }
256 } // namespace matrix
257
259 // Detail
261 namespace detail {
262 template <typename Container,
263 typename Subclass,
264 typename TRowView,
265 typename Semiring = void>
266 class MatrixCommon : MatrixPolymorphicBase {
267 public:
269 // MatrixCommon - Aliases - public
271
272 using scalar_type = typename Container::value_type;
273 using scalar_reference = typename Container::reference;
274 using scalar_const_reference = typename Container::const_reference;
275 using semiring_type = Semiring;
276
277 using container_type = Container;
278 using iterator = typename Container::iterator;
279 using const_iterator = typename Container::const_iterator;
280
281 using RowView = TRowView;
282
283 scalar_type scalar_one() const noexcept {
284 return static_cast<Subclass const*>(this)->one_impl();
285 }
286
287 scalar_type scalar_zero() const noexcept {
288 return static_cast<Subclass const*>(this)->zero_impl();
289 }
290
291 Semiring const* semiring() const noexcept {
292 return static_cast<Subclass const*>(this)->semiring_impl();
293 }
294
295 private:
297 // MatrixCommon - Semiring arithmetic - private
299
300 scalar_type plus_no_checks(scalar_type x, scalar_type y) const noexcept {
301 return static_cast<Subclass const*>(this)->plus_no_checks_impl(y, x);
302 }
303
304 scalar_type product_no_checks(scalar_type x,
305 scalar_type y) const noexcept {
306 return static_cast<Subclass const*>(this)->product_no_checks_impl(y, x);
307 }
308
309 protected:
311 // MatrixCommon - Container functions - protected
313
314 // TODO(1) use constexpr-if, not SFINAE
315 template <typename SFINAE = container_type>
316 auto resize(size_t r, size_t c) -> std::enable_if_t<
317 std::is_same_v<SFINAE, std::vector<scalar_type>>> {
318 _container.resize(r * c);
319 }
320
321 template <typename SFINAE = container_type>
322 auto resize(size_t, size_t) -> std::enable_if_t<
323 !std::is_same_v<SFINAE, std::vector<scalar_type>>> {}
324
325 public:
327 // MatrixCommon - Constructors + destructor - public
329
330 // none of the constructors are noexcept because they allocate
331 MatrixCommon() = default;
332 MatrixCommon(MatrixCommon const&) = default;
333 MatrixCommon(MatrixCommon&&) = default;
334 MatrixCommon& operator=(MatrixCommon const&) = default;
335 MatrixCommon& operator=(MatrixCommon&&) = default;
336
337 explicit MatrixCommon(std::initializer_list<scalar_type> const& c)
338 : MatrixCommon() {
339 resize(1, c.size());
340 std::copy(c.begin(), c.end(), _container.begin());
341 }
342
343 explicit MatrixCommon(std::vector<std::vector<scalar_type>> const& m)
344 : MatrixCommon() {
345 init(m);
346 }
347
348 MatrixCommon(
349 std::initializer_list<std::initializer_list<scalar_type>> const& m)
350 : MatrixCommon() {
351 init(m);
352 }
353
354 private:
355 // not noexcept because resize isn't
356 template <typename T>
357 void init(T const& m) {
358 size_t const R = m.size();
359 if (R == 0) {
360 return;
361 }
362 size_t const C = m.begin()->size();
363 resize(R, C);
364 for (size_t r = 0; r < R; ++r) {
365 auto row = m.begin() + r;
366 for (size_t c = 0; c < C; ++c) {
367 _container[r * C + c] = *(row->begin() + c);
368 }
369 }
370 }
371
372 // not noexcept because init isn't
373 void
374 init(std::initializer_list<std::initializer_list<scalar_type>> const& m) {
375 init<std::initializer_list<std::initializer_list<scalar_type>>>(m);
376 }
377
378 public:
379 explicit MatrixCommon(RowView const& rv) : MatrixCommon() {
380 resize(1, rv.size());
381 std::copy(rv.cbegin(), rv.cend(), _container.begin());
382 }
383
384 ~MatrixCommon() = default;
385
386 // not noexcept because mem allocate is required
387 Subclass one() const {
388 size_t const n = number_of_rows();
389 Subclass x(semiring(), n, n);
390 std::fill(x.begin(), x.end(), scalar_zero());
391 for (size_t r = 0; r < n; ++r) {
392 x(r, r) = scalar_one();
393 }
394 return x;
395 }
396
398 // Comparison operators
400
401 // not noexcept because apparently vector::operator== isn't
402 bool operator==(MatrixCommon const& that) const {
403 return _container == that._container;
404 }
405
406 // not noexcept because apparently vector::operator== isn't
407 bool operator==(RowView const& that) const {
408 return number_of_rows() == 1
409 && static_cast<RowView>(*static_cast<Subclass const*>(this))
410 == that;
411 }
412
413 // not noexcept because apparently vector::operator< isn't
414 bool operator<(MatrixCommon const& that) const {
415 return _container < that._container;
416 }
417
418 // not noexcept because apparently vector::operator< isn't
419 bool operator<(RowView const& that) const {
420 return number_of_rows() == 1
421 && static_cast<RowView>(*static_cast<Subclass const*>(this))
422 < that;
423 }
424
425 // not noexcept because operator== isn't
426 template <typename T>
427 bool operator!=(T const& that) const {
428 static_assert(IsMatrix<T> || std::is_same_v<T, RowView>);
429 return !(*this == that);
430 }
431
432 // not noexcept because operator< isn't
433 template <typename T>
434 bool operator>(T const& that) const {
435 static_assert(IsMatrix<T> || std::is_same_v<T, RowView>);
436 return that < *this;
437 }
438
439 // not noexcept because operator< isn't
440 template <typename T>
441 bool operator>=(T const& that) const {
442 static_assert(IsMatrix<T> || std::is_same_v<T, RowView>);
443 return that < *this || that == *this;
444 }
445
446 // not noexcept because operator< isn't
447 template <typename T>
448 bool operator<=(T const& that) const {
449 static_assert(IsMatrix<T> || std::is_same_v<T, RowView>);
450 return *this < that || that == *this;
451 }
452
454 // Attributes
456
457 // not noexcept because vector::operator[] isn't, and neither is
458 // array::operator[]
459 scalar_reference operator()(size_t r, size_t c) {
460 return this->_container[r * number_of_cols() + c];
461 }
462
463 scalar_reference at(size_t r, size_t c) {
464 matrix::throw_if_bad_coords(static_cast<Subclass const&>(*this), r, c);
465 return this->operator()(r, c);
466 }
467
468 // not noexcept because vector::operator[] isn't, and neither is
469 // array::operator[]
470 scalar_const_reference operator()(size_t r, size_t c) const {
471 return this->_container[r * number_of_cols() + c];
472 }
473
474 scalar_const_reference at(size_t r, size_t c) const {
475 matrix::throw_if_bad_coords(static_cast<Subclass const&>(*this), r, c);
476 return this->operator()(r, c);
477 }
478
479 // noexcept because number_of_rows_impl is noexcept
480 size_t number_of_rows() const noexcept {
481 return static_cast<Subclass const*>(this)->number_of_rows_impl();
482 }
483
484 // noexcept because number_of_cols_impl is noexcept
485 size_t number_of_cols() const noexcept {
486 return static_cast<Subclass const*>(this)->number_of_cols_impl();
487 }
488
489 // not noexcept because Hash<T>::operator() isn't
490 size_t hash_value() const {
491 return Hash<Container>()(_container);
492 }
493
495 // Arithmetic operators - in-place
497
498 // not noexcept because memory is allocated
499 void product_inplace_no_checks(Subclass const& A, Subclass const& B) {
500 LIBSEMIGROUPS_ASSERT(number_of_rows() == number_of_cols());
501 LIBSEMIGROUPS_ASSERT(A.number_of_rows() == number_of_rows());
502 LIBSEMIGROUPS_ASSERT(B.number_of_rows() == number_of_rows());
503 LIBSEMIGROUPS_ASSERT(A.number_of_cols() == number_of_cols());
504 LIBSEMIGROUPS_ASSERT(B.number_of_cols() == number_of_cols());
505 LIBSEMIGROUPS_ASSERT(&A != this);
506 LIBSEMIGROUPS_ASSERT(&B != this);
507
508 // Benchmarking boolean matrix multiplication reveals that using a
509 // non-static container_type gives the best performance, when compared
510 // to static container_type the performance is more or less the same
511 // (but not thread-safe), and there appears to be a performance
512 // penalty of about 50% when using static thread_local container_type
513 // (when compiling with clang).
514 size_t const N = A.number_of_rows();
515 std::vector<scalar_type> tmp(N, 0);
516
517 for (size_t c = 0; c < N; c++) {
518 for (size_t i = 0; i < N; i++) {
519 tmp[i] = B(i, c);
520 }
521 for (size_t r = 0; r < N; r++) {
522 (*this)(r, c) = std::inner_product(
523 A._container.begin() + r * N,
524 A._container.begin() + (r + 1) * N,
525 tmp.begin(),
526 scalar_zero(),
527 [this](scalar_type x, scalar_type y) {
528 return this->plus_no_checks(x, y);
529 },
530 [this](scalar_type x, scalar_type y) {
531 return this->product_no_checks(x, y);
532 });
533 }
534 }
535 }
536
537 // not noexcept because iterator increment isn't
538 void operator*=(scalar_type a) {
539 for (auto it = _container.begin(); it < _container.end(); ++it) {
540 *it = product_no_checks(*it, a);
541 }
542 }
543
544 // not noexcept because vector::operator[] and array::operator[] aren't
545 void operator+=(Subclass const& that) {
546 LIBSEMIGROUPS_ASSERT(that.number_of_rows() == number_of_rows());
547 LIBSEMIGROUPS_ASSERT(that.number_of_cols() == number_of_cols());
548 for (size_t i = 0; i < _container.size(); ++i) {
549 _container[i] = plus_no_checks(_container[i], that._container[i]);
550 }
551 }
552
553 void operator+=(RowView const& that) {
554 LIBSEMIGROUPS_ASSERT(number_of_rows() == 1);
555 RowView(*static_cast<Subclass const*>(this)) += that;
556 }
557
558 void operator+=(scalar_type a) {
559 for (auto it = _container.begin(); it < _container.end(); ++it) {
560 *it = plus_no_checks(*it, a);
561 }
562 }
563
564 // TODO(2) implement operator*=(Subclass const&)
565
567 // Arithmetic operators - not in-place
569
570 // not noexcept because operator+= isn't
571 Subclass operator+(Subclass const& y) const {
572 Subclass result(*static_cast<Subclass const*>(this));
573 result += y;
574 return result;
575 }
576
577 // not noexcept because product_inplace_no_checks isn't
578 Subclass operator*(Subclass const& y) const {
579 Subclass result(*static_cast<Subclass const*>(this));
580 result.product_inplace_no_checks(*static_cast<Subclass const*>(this),
581 y);
582 return result;
583 }
584
585 Subclass operator*(scalar_type a) const {
586 Subclass result(*static_cast<Subclass const*>(this));
587 result *= a;
588 return result;
589 }
590
591 Subclass operator+(scalar_type a) const {
592 Subclass result(*static_cast<Subclass const*>(this));
593 result += a;
594 return result;
595 }
596
598 // Iterators
600
601 // noexcept because vector::begin and array::begin are noexcept
602 iterator begin() noexcept {
603 return _container.begin();
604 }
605
606 // noexcept because vector::end and array::end are noexcept
607 iterator end() noexcept {
608 return _container.end();
609 }
610
611 // noexcept because vector::begin and array::begin are noexcept
612 const_iterator begin() const noexcept {
613 return _container.begin();
614 }
615
616 // noexcept because vector::end and array::end are noexcept
617 const_iterator end() const noexcept {
618 return _container.end();
619 }
620
621 // noexcept because vector::cbegin and array::cbegin are noexcept
622 const_iterator cbegin() const noexcept {
623 return _container.cbegin();
624 }
625
626 // noexcept because vector::cend and array::cend are noexcept
627 const_iterator cend() const noexcept {
628 return _container.cend();
629 }
630
631 template <typename U>
632 std::pair<scalar_type, scalar_type> coords(U const& it) const {
633 static_assert(
634 std::is_same_v<U, iterator> || std::is_same_v<U, const_iterator>,
635 "the parameter it must be of type iterator or const_iterator");
636 scalar_type const v = std::distance(_container.begin(), it);
637 return std::make_pair(v / number_of_cols(), v % number_of_cols());
638 }
639
641 // Modifiers
643
644 // noexcept because vector::swap and array::swap are noexcept
645 void swap(MatrixCommon& that) noexcept {
646 std::swap(_container, that._container);
647 }
648
649 // noexcept because swap is noexcept, and so too are number_of_rows and
650 // number_of_cols
651 void transpose_no_checks() noexcept {
652 LIBSEMIGROUPS_ASSERT(number_of_rows() == number_of_cols());
653 if (number_of_rows() == 0) {
654 return;
655 }
656 auto& x = *this;
657 for (size_t r = 0; r < number_of_rows() - 1; ++r) {
658 for (size_t c = r + 1; c < number_of_cols(); ++c) {
659 std::swap(x(r, c), x(c, r));
660 }
661 }
662 }
663
664 void transpose() {
665 matrix::throw_if_not_square(static_cast<Subclass&>(*this));
666 transpose_no_checks();
667 }
668
670 // Rows
672
673 // not noexcept because there's an allocation
674 RowView row_no_checks(size_t i) const {
675 auto& container = const_cast<Container&>(_container);
676 return RowView(static_cast<Subclass const*>(this),
677 container.begin() + i * number_of_cols(),
678 number_of_cols());
679 }
680
681 RowView row(size_t i) const {
682 if (i >= number_of_rows()) {
684 "index out of range, expected value in [{}, {}), found {}",
685 0,
686 number_of_rows(),
687 i);
688 }
689 return row_no_checks(i);
690 }
691
692 // not noexcept because there's an allocation
693 template <typename T>
694 void rows(T& x) const {
695 auto& container = const_cast<Container&>(_container);
696 for (auto itc = container.begin(); itc != container.end();
697 itc += number_of_cols()) {
698 x.emplace_back(
699 static_cast<Subclass const*>(this), itc, number_of_cols());
700 }
701 LIBSEMIGROUPS_ASSERT(x.size() == number_of_rows());
702 }
703
705 // Friend functions
707
708 friend std::ostream& operator<<(std::ostream& os, MatrixCommon const& x) {
709 os << detail::to_string(x);
710 return os;
711 }
712
713 private:
715 // Private data
717 container_type _container;
718 };
719
720 template <typename Scalar>
721 class MatrixDynamicDim {
722 public:
723 MatrixDynamicDim() : _number_of_cols(0), _number_of_rows(0) {}
724 MatrixDynamicDim(MatrixDynamicDim const&) = default;
725 MatrixDynamicDim(MatrixDynamicDim&&) = default;
726 MatrixDynamicDim& operator=(MatrixDynamicDim const&) = default;
727 MatrixDynamicDim& operator=(MatrixDynamicDim&&) = default;
728
729 MatrixDynamicDim(size_t r, size_t c)
730 : _number_of_cols(c), _number_of_rows(r) {}
731
732 ~MatrixDynamicDim() = default;
733
734 void swap(MatrixDynamicDim& that) noexcept {
735 std::swap(_number_of_cols, that._number_of_cols);
736 std::swap(_number_of_rows, that._number_of_rows);
737 }
738
739 protected:
740 size_t number_of_rows_impl() const noexcept {
741 return _number_of_rows;
742 }
743
744 size_t number_of_cols_impl() const noexcept {
745 return _number_of_cols;
746 }
747
748 private:
749 size_t _number_of_cols;
750 size_t _number_of_rows;
751 };
752
753 template <typename PlusOp,
754 typename ProdOp,
755 typename ZeroOp,
756 typename OneOp,
757 typename Scalar>
758 struct MatrixStaticArithmetic {
759 MatrixStaticArithmetic() = default;
760 MatrixStaticArithmetic(MatrixStaticArithmetic const&) = default;
761 MatrixStaticArithmetic(MatrixStaticArithmetic&&) = default;
762 MatrixStaticArithmetic& operator=(MatrixStaticArithmetic const&)
763 = default;
764 MatrixStaticArithmetic& operator=(MatrixStaticArithmetic&&) = default;
765
766 // TODO(2) from here to the end of MatrixStaticArithmetic should be
767 // private or protected
768 using scalar_type = Scalar;
769
770 static constexpr scalar_type plus_no_checks_impl(scalar_type x,
771 scalar_type y) noexcept {
772 return PlusOp()(x, y);
773 }
774
775 static constexpr scalar_type
776 product_no_checks_impl(scalar_type x, scalar_type y) noexcept {
777 return ProdOp()(x, y);
778 }
779
780 static constexpr scalar_type one_impl() noexcept {
781 return OneOp()();
782 }
783
784 static constexpr scalar_type zero_impl() noexcept {
785 return ZeroOp()();
786 }
787
788 static constexpr void const* semiring_impl() noexcept {
789 return nullptr;
790 }
791 };
792
794 // RowViews - class for cheaply storing iterators to rows
796
797 template <typename Mat, typename Subclass>
798 class RowViewCommon {
799 static_assert(IsMatrix<Mat>,
800 "the template parameter Mat must be derived from "
801 "MatrixPolymorphicBase");
802
803 public:
804 using const_iterator = typename Mat::const_iterator;
805 using iterator = typename Mat::iterator;
806
807 using scalar_type = typename Mat::scalar_type;
808 using scalar_reference = typename Mat::scalar_reference;
809 using scalar_const_reference = typename Mat::scalar_const_reference;
810
811 using Row = typename Mat::Row;
812 using matrix_type = Mat;
813
814 size_t size() const noexcept {
815 return static_cast<Subclass const*>(this)->length_impl();
816 }
817
818 private:
819 scalar_type plus_no_checks(scalar_type x, scalar_type y) const noexcept {
820 return static_cast<Subclass const*>(this)->plus_no_checks_impl(y, x);
821 }
822
823 scalar_type product_no_checks(scalar_type x,
824 scalar_type y) const noexcept {
825 return static_cast<Subclass const*>(this)->product_no_checks_impl(y, x);
826 }
827
828 public:
829 RowViewCommon() = default;
830 RowViewCommon(RowViewCommon const&) = default;
831 RowViewCommon(RowViewCommon&&) = default;
832 RowViewCommon& operator=(RowViewCommon const&) = default;
833 RowViewCommon& operator=(RowViewCommon&&) = default;
834
835 explicit RowViewCommon(Row const& r)
836 : RowViewCommon(const_cast<Row&>(r).begin()) {}
837
838 // Not noexcept because iterator::operator[] isn't
839 scalar_const_reference operator[](size_t i) const {
840 return _begin[i];
841 }
842
843 // Not noexcept because iterator::operator[] isn't
844 scalar_reference operator[](size_t i) {
845 return _begin[i];
846 }
847
848 // Not noexcept because iterator::operator[] isn't
849 scalar_const_reference operator()(size_t i) const {
850 return (*this)[i];
851 }
852
853 // Not noexcept because iterator::operator[] isn't
854 scalar_reference operator()(size_t i) {
855 return (*this)[i];
856 }
857
858 // noexcept because begin() is
859 const_iterator cbegin() const noexcept {
860 return _begin;
861 }
862
863 // not noexcept because iterator arithmetic isn't
864 const_iterator cend() const {
865 return _begin + size();
866 }
867
868 // noexcept because begin() is
869 const_iterator begin() const noexcept {
870 return _begin;
871 }
872
873 // not noexcept because iterator arithmetic isn't
874 const_iterator end() const {
875 return _begin + size();
876 }
877
878 // noexcept because begin() is
879 iterator begin() noexcept {
880 return _begin;
881 }
882
883 // not noexcept because iterator arithmetic isn't
884 iterator end() noexcept {
885 return _begin + size();
886 }
887
889 // Arithmetic operators
891
892 // not noexcept because operator[] isn't
893 void operator+=(RowViewCommon const& x) {
894 auto& this_ = *this;
895 for (size_t i = 0; i < size(); ++i) {
896 this_[i] = plus_no_checks(this_[i], x[i]);
897 }
898 }
899
900 // not noexcept because iterator arithmeic isn't
901 void operator+=(scalar_type a) {
902 for (auto& x : *this) {
903 x = plus_no_checks(x, a);
904 }
905 }
906
907 // not noexcept because iterator arithmeic isn't
908 void operator*=(scalar_type a) {
909 for (auto& x : *this) {
910 x = product_no_checks(x, a);
911 }
912 }
913
914 // not noexcept because operator*= isn't
915 Row operator*(scalar_type a) const {
916 Row result(*static_cast<Subclass const*>(this));
917 result *= a;
918 return result;
919 }
920
921 // not noexcept because operator+= isn't
922 Row operator+(RowViewCommon const& that) const {
923 Row result(*static_cast<Subclass const*>(this));
924 result += static_cast<Subclass const&>(that);
925 return result;
926 }
927
928 template <typename U>
929 bool operator==(U const& that) const {
930 // TODO(1) static assert that U is Row or RowView
931 return std::equal(begin(), end(), that.begin());
932 }
933
934 template <typename U>
935 bool operator!=(U const& that) const {
936 return !(*this == that);
937 }
938
939 template <typename U>
940 bool operator<(U const& that) const {
942 cbegin(), cend(), that.cbegin(), that.cend());
943 }
944
945 template <typename U>
946 bool operator>(U const& that) const {
947 return that < *this;
948 }
949
950 void swap(RowViewCommon& that) noexcept {
951 std::swap(that._begin, _begin);
952 }
953
954 friend std::ostream& operator<<(std::ostream& os,
955 RowViewCommon const& x) {
956 os << detail::to_string(x);
957 return os;
958 }
959
960 protected:
961 explicit RowViewCommon(iterator first) : _begin(first) {}
962
963 private:
964 iterator _begin;
965 };
966
967 template <typename Container>
968 void throw_if_any_row_wrong_size(Container const& m) {
969 if (m.size() <= 1) {
970 return;
971 }
972 uint64_t const C = m.begin()->size();
973 auto it = std::find_if_not(
974 m.begin() + 1, m.end(), [&C](typename Container::const_reference r) {
975 return r.size() == C;
976 });
977 if (it != m.end()) {
978 LIBSEMIGROUPS_EXCEPTION("invalid argument, expected every item to "
979 "have length {}, found {} in entry {}",
980 C,
981 it->size(),
982 std::distance(m.begin(), it));
983 }
984 }
985
986 template <typename Scalar>
987 void throw_if_any_row_wrong_size(
988 std::initializer_list<std::initializer_list<Scalar>> m) {
989 throw_if_any_row_wrong_size<
990 std::initializer_list<std::initializer_list<Scalar>>>(m);
991 }
992
993 } // namespace detail
994
996 // Matrix forward declarations
998
999#ifndef LIBSEMIGROUPS_PARSED_BY_DOXYGEN
1000 template <typename PlusOp,
1001 typename ProdOp,
1002 typename ZeroOp,
1003 typename OneOp,
1004 size_t R,
1005 size_t C,
1006 typename Scalar>
1007 class StaticMatrix;
1008
1009 template <typename... Args>
1010 class DynamicMatrix;
1011
1012 template <typename PlusOp,
1013 typename ProdOp,
1014 typename ZeroOp,
1015 typename OneOp,
1016 typename Scalar>
1017 class DynamicMatrix<PlusOp, ProdOp, ZeroOp, OneOp, Scalar>;
1018
1019 template <typename Semiring, typename Scalar>
1020 class DynamicMatrix<Semiring, Scalar>;
1021#endif
1022
1027
1029 // StaticRowViews - static arithmetic
1031
1068 template <typename PlusOp,
1069 typename ProdOp,
1070 typename ZeroOp,
1071 typename OneOp,
1072 size_t C,
1073 typename Scalar>
1075 : public detail::RowViewCommon<
1076 StaticMatrix<PlusOp, ProdOp, ZeroOp, OneOp, 1, C, Scalar>,
1077 StaticRowView<PlusOp, ProdOp, ZeroOp, OneOp, C, Scalar>>,
1078 public detail::
1079 MatrixStaticArithmetic<PlusOp, ProdOp, ZeroOp, OneOp, Scalar> {
1080 private:
1081 using RowViewCommon = detail::RowViewCommon<
1084 friend RowViewCommon;
1085
1086 template <size_t R>
1087 using StaticMatrix_ = ::libsemigroups::
1088 StaticMatrix<PlusOp, ProdOp, ZeroOp, OneOp, R, C, Scalar>;
1089
1090 public:
1092 using const_iterator = typename RowViewCommon::const_iterator;
1093
1095 using iterator = typename RowViewCommon::iterator;
1096
1098 using scalar_type = Scalar;
1099
1101 using scalar_reference = typename RowViewCommon::scalar_reference;
1102
1104 // clang-format off
1105 // NOLINTNEXTLINE(whitespace/line_length)
1106 using scalar_const_reference = typename RowViewCommon::scalar_const_reference;
1107 // clang-format on
1108
1110 using matrix_type = typename RowViewCommon::matrix_type;
1111
1113 using Row = typename matrix_type::Row;
1114
1116 StaticRowView() = default;
1117
1119 StaticRowView(StaticRowView const&) = default;
1120
1123
1126
1129
1130#ifndef LIBSEMIGROUPS_PARSED_BY_DOXYGEN
1131 using RowViewCommon::RowViewCommon;
1132
1133 // TODO(2) This constructor should be private
1134 template <size_t R>
1135 StaticRowView(StaticMatrix_<R> const*,
1136 typename RowViewCommon::iterator it,
1137 size_t)
1138 : RowViewCommon(it) {}
1139
1140 using RowViewCommon::size;
1141#else
1153 explicit StaticRowView(Row const& r);
1154
1167 static constexpr size_t size() const noexcept;
1168
1181 iterator begin() noexcept;
1182
1197
1210 const_iterator cbegin() const noexcept;
1211
1226
1244 scalar_reference operator()(size_t i);
1245
1263 scalar_const_reference operator()(size_t i) const;
1264
1266 scalar_reference operator[](size_t i);
1267
1269 scalar_const_reference operator[](size_t i) const;
1270
1291 template <typename U>
1292 bool operator==(U const& that) const;
1293
1314 template <typename U>
1315 bool operator!=(U const& that) const;
1316
1322 // clang-format off
1323 // NOLINTNEXTLINE(whitespace/line_length)
1327 // clang-format on
1340 template <typename U>
1341 bool operator<(U const& that) const;
1342
1364 template <typename U>
1365 bool operator<(U const& that) const;
1366
1386 Row operator+(StaticRowView const& that);
1387
1406 void operator+=(StaticRowView const& that);
1407
1421 void operator+=(scalar_type a);
1422
1440 Row operator*(scalar_type a) const;
1441
1455 void operator*=(scalar_type a);
1456#endif
1457
1458 private:
1459 static constexpr size_t length_impl() noexcept {
1460 return C;
1461 }
1462 };
1463
1465 // DynamicRowViews - static arithmetic
1467
1468#ifndef LIBSEMIGROUPS_PARSED_BY_DOXYGEN
1469 // Doxygen needs to ignore this so that the actual implementation of
1470 // DynamicRowView gets documented.
1471 template <typename... Args>
1472 class DynamicRowView;
1473#endif
1474
1512 template <typename PlusOp,
1513 typename ProdOp,
1514 typename ZeroOp,
1515 typename OneOp,
1516 typename Scalar>
1517 class DynamicRowView<PlusOp, ProdOp, ZeroOp, OneOp, Scalar>
1518 : public detail::RowViewCommon<
1519 DynamicMatrix<PlusOp, ProdOp, ZeroOp, OneOp, Scalar>,
1520 DynamicRowView<PlusOp, ProdOp, ZeroOp, OneOp, Scalar>>,
1521 public detail::
1522 MatrixStaticArithmetic<PlusOp, ProdOp, ZeroOp, OneOp, Scalar> {
1523 private:
1524 using DynamicMatrix_ = DynamicMatrix<PlusOp, ProdOp, ZeroOp, OneOp, Scalar>;
1525 using RowViewCommon = detail::RowViewCommon<
1526 DynamicMatrix_,
1528 friend RowViewCommon;
1529
1530 public:
1532 using const_iterator = typename RowViewCommon::const_iterator;
1533
1535 using iterator = typename RowViewCommon::iterator;
1536
1538 using scalar_type = Scalar;
1539
1541 using scalar_reference = typename RowViewCommon::scalar_reference;
1542
1544 // clang-format off
1545 // NOLINTNEXTLINE(whitespace/line_length)
1546 using scalar_const_reference = typename RowViewCommon::scalar_const_reference;
1547 // clang-format on
1548
1550 using matrix_type = typename RowViewCommon::matrix_type;
1551
1553 using Row = typename matrix_type::Row;
1554
1556 DynamicRowView() = default;
1557
1560
1563
1566
1569
1571 explicit DynamicRowView(Row const& r)
1572 : RowViewCommon(r), _length(r.number_of_cols()) {}
1573
1574#ifndef LIBSEMIGROUPS_PARSED_BY_DOXYGEN
1575 using RowViewCommon::RowViewCommon;
1576
1577 // TODO(2) This constructor should be private
1578 DynamicRowView(DynamicMatrix_ const*, iterator const& it, size_t N)
1579 : RowViewCommon(it), _length(N) {}
1580
1581 using RowViewCommon::size;
1582#else
1584 size_t size() const noexcept;
1585
1587 iterator begin() noexcept;
1588
1591
1593 const_iterator cbegin() const noexcept;
1594
1597
1599 scalar_reference operator()(size_t i);
1600
1602 scalar_const_reference operator()(size_t i) const;
1603
1605 scalar_reference operator[](size_t i);
1606
1608 scalar_const_reference operator[](size_t i) const;
1609
1611 template <typename U>
1612 bool operator==(U const& that) const;
1613
1615 template <typename U>
1616 bool operator!=(U const& that) const;
1617
1619 template <typename U>
1620 bool operator<(U const& that) const;
1621
1623 template <typename U>
1624 bool operator<(U const& that) const;
1625
1627 Row operator+(DynamicRowView const& that);
1628
1630 void operator+=(DynamicRowView const& that);
1631
1633 void operator+=(scalar_type a);
1634
1636 Row operator*(scalar_type a) const;
1637
1639 void operator*=(scalar_type a);
1640#endif // LIBSEMIGROUPS_PARSED_BY_DOXYGEN
1641
1642 private:
1643 size_t length_impl() const noexcept {
1644 return _length;
1645 }
1646 size_t _length;
1647 };
1648
1650 // DynamicRowViews - dynamic arithmetic
1652
1673 template <typename Semiring, typename Scalar>
1674 class DynamicRowView<Semiring, Scalar>
1675 : public detail::RowViewCommon<DynamicMatrix<Semiring, Scalar>,
1676 DynamicRowView<Semiring, Scalar>> {
1677 private:
1678 using DynamicMatrix_ = DynamicMatrix<Semiring, Scalar>;
1679 friend DynamicMatrix_;
1680 using RowViewCommon
1681 = detail::RowViewCommon<DynamicMatrix_,
1683 friend RowViewCommon;
1684
1685 public:
1687 using const_iterator = typename RowViewCommon::const_iterator;
1688
1690 using iterator = typename RowViewCommon::iterator;
1691
1693 using scalar_type = Scalar;
1694
1696 using scalar_reference = typename RowViewCommon::scalar_reference;
1697
1699 // clang-format off
1700 // NOLINTNEXTLINE(whitespace/line_length)
1701 using scalar_const_reference = typename RowViewCommon::scalar_const_reference;
1702 // clang-format on
1703
1705 using matrix_type = typename RowViewCommon::matrix_type;
1706
1708 using Row = typename matrix_type::Row;
1709
1711 DynamicRowView() = default;
1712
1714 DynamicRowView(DynamicRowView const&) = default;
1715
1717 DynamicRowView(DynamicRowView&&) = default;
1718
1720 DynamicRowView& operator=(DynamicRowView const&) = default;
1721
1724
1726 explicit DynamicRowView(Row const& r) : RowViewCommon(r), _matrix(&r) {}
1727
1728#ifndef LIBSEMIGROUPS_PARSED_BY_DOXYGEN
1729 using RowViewCommon::RowViewCommon;
1730
1731 // TODO(2) This constructor should be private
1732 DynamicRowView(DynamicMatrix_ const* mat, iterator const& it, size_t)
1733 : RowViewCommon(it), _matrix(mat) {}
1734
1735 using RowViewCommon::size;
1736#else
1738 size_t size() const noexcept;
1739
1741 iterator begin() noexcept;
1742
1744 iterator end();
1745
1747 const_iterator cbegin() const noexcept;
1748
1750 iterator cend();
1751
1753 scalar_reference operator()(size_t i);
1754
1756 scalar_const_reference operator()(size_t i) const;
1757
1759 scalar_reference operator[](size_t i);
1760
1762 scalar_const_reference operator[](size_t i) const;
1763
1765 template <typename U>
1766 bool operator==(U const& that) const;
1767
1769 template <typename U>
1770 bool operator!=(U const& that) const;
1771
1773 template <typename U>
1774 bool operator<(U const& that) const;
1775
1777 template <typename U>
1778 bool operator<(U const& that) const;
1779
1781 Row operator+(DynamicRowView const& that);
1782
1784 void operator+=(DynamicRowView const& that);
1785
1787 void operator+=(scalar_type a);
1788
1790 Row operator*(scalar_type a) const;
1791
1793 void operator*=(scalar_type a);
1794#endif
1795
1796 private:
1797 size_t length_impl() const noexcept {
1798 return _matrix->number_of_cols();
1799 }
1800
1801 scalar_type plus_no_checks_impl(scalar_type x,
1802 scalar_type y) const noexcept {
1803 return _matrix->plus_no_checks_impl(x, y);
1804 }
1805
1806 scalar_type product_no_checks_impl(scalar_type x,
1807 scalar_type y) const noexcept {
1808 return _matrix->product_no_checks_impl(x, y);
1809 }
1810
1811 DynamicMatrix_ const* _matrix;
1812 };
1813
1815 // StaticMatrix with compile time semiring arithmetic
1817
1851 template <typename PlusOp,
1852 typename ProdOp,
1853 typename ZeroOp,
1854 typename OneOp,
1855 size_t R,
1856 size_t C,
1857 typename Scalar>
1859 : public detail::
1860 MatrixStaticArithmetic<PlusOp, ProdOp, ZeroOp, OneOp, Scalar>,
1861 public detail::MatrixCommon<
1862 std::array<Scalar, R * C>,
1863 StaticMatrix<PlusOp, ProdOp, ZeroOp, OneOp, R, C, Scalar>,
1864 StaticRowView<PlusOp, ProdOp, ZeroOp, OneOp, C, Scalar>> {
1866 // StaticMatrix - Aliases - private
1868
1869 using MatrixCommon = ::libsemigroups::detail::MatrixCommon<
1873 friend MatrixCommon;
1874
1875 public:
1877 // StaticMatrix - Aliases - public
1879
1881 using scalar_type = typename MatrixCommon::scalar_type;
1882
1884 using scalar_reference = typename MatrixCommon::scalar_reference;
1885
1887 // clang-format off
1888 // NOLINTNEXTLINE(whitespace/line_length)
1889 using scalar_const_reference = typename MatrixCommon::scalar_const_reference;
1890 // clang-format on
1891
1894
1897
1899 using Plus = PlusOp;
1900
1902 using Prod = ProdOp;
1903
1905 using Zero = ZeroOp;
1906
1908 using One = OneOp;
1909
1911 using iterator = typename MatrixCommon::iterator;
1912
1914 using const_iterator = typename MatrixCommon::const_iterator;
1915
1916 static constexpr size_t nr_rows = R;
1917 static constexpr size_t nr_cols = C;
1918
1920 // StaticMatrix - Constructors + destructor - public
1922
1940 : MatrixCommon(c) {
1941 static_assert(R == 1,
1942 "cannot construct Matrix from the given initializer list, "
1943 "incompatible dimensions");
1944 LIBSEMIGROUPS_ASSERT(c.size() == C);
1945 }
1946
1968 : MatrixCommon(m) {}
1969
1983 : MatrixCommon(m) {}
1984
2002 explicit StaticMatrix(RowView const& rv) : MatrixCommon(rv) {
2003 static_assert(
2004 R == 1,
2005 "cannot construct Matrix with more than one row from RowView!");
2006 }
2007
2011 StaticMatrix() = default;
2012
2016 StaticMatrix(StaticMatrix const&) = default;
2017
2022
2027
2032
2033#ifndef LIBSEMIGROUPS_PARSED_BY_DOXYGEN
2034 // For uniformity of interface, the args do nothing
2035 StaticMatrix(size_t r, size_t c) : StaticMatrix() {
2036 (void) r;
2037 (void) c;
2038 LIBSEMIGROUPS_ASSERT(r == number_of_rows());
2039 LIBSEMIGROUPS_ASSERT(c == number_of_cols());
2040 }
2041
2042 // For uniformity of interface, the first arg does nothing
2043 StaticMatrix(void const* ptr, std::initializer_list<scalar_type> const& c)
2044 : StaticMatrix(c) {
2045 (void) ptr;
2046 LIBSEMIGROUPS_ASSERT(ptr == nullptr);
2047 }
2048
2049 // For uniformity of interface, the first arg does nothing
2051 void const* ptr,
2053 : StaticMatrix(m) {
2054 (void) ptr;
2055 LIBSEMIGROUPS_ASSERT(ptr == nullptr);
2056 }
2057
2058 // For uniformity of interface, the first arg does nothing
2059 explicit StaticMatrix(void const* ptr, RowView const& rv)
2060 : StaticMatrix(rv) {
2061 (void) ptr;
2062 LIBSEMIGROUPS_ASSERT(ptr == nullptr);
2063 }
2064
2065 // For uniformity of interface, no arg used for anything
2066 StaticMatrix(void const* ptr, size_t r, size_t c) : StaticMatrix(r, c) {
2067 (void) ptr;
2068 LIBSEMIGROUPS_ASSERT(ptr == nullptr);
2069 }
2070#endif
2071
2072 ~StaticMatrix() = default;
2073
2074#ifndef LIBSEMIGROUPS_PARSED_BY_DOXYGEN
2075 static StaticMatrix one(size_t n) {
2076 // If specified the value of n must equal R or otherwise weirdness will
2077 // ensue...
2078 LIBSEMIGROUPS_ASSERT(n == 0 || n == R);
2079 (void) n;
2080#if defined(__APPLE__) && defined(__clang__) \
2081 && (__clang_major__ == 13 || __clang_major__ == 14)
2082 // With Apple clang version 13.1.6 (clang-1316.0.21.2.5) something goes
2083 // wrong and the value R is optimized away somehow, meaning that the
2084 // values on the diagonal aren't actually set. This only occurs when
2085 // libsemigroups is compiled with -O2 or higher.
2086 size_t volatile const m = R;
2087#else
2088 size_t const m = R;
2089#endif
2090 StaticMatrix x(m, m);
2091 std::fill(x.begin(), x.end(), ZeroOp()());
2092 for (size_t r = 0; r < m; ++r) {
2093 x(r, r) = OneOp()();
2094 }
2095 return x;
2096 }
2097
2098 static StaticMatrix one(void const* ptr, size_t n = 0) {
2099 (void) ptr;
2100 LIBSEMIGROUPS_ASSERT(ptr == nullptr);
2101 LIBSEMIGROUPS_ASSERT(n == 0 || n == R);
2102 return one(n);
2103 }
2104#endif // LIBSEMIGROUPS_PARSED_BY_DOXYGEN
2105
2107 // StaticMatrix - member function aliases - public
2109#ifdef LIBSEMIGROUPS_PARSED_BY_DOXYGEN
2128 scalar_reference operator()(size_t r, size_t c);
2129
2143 scalar_reference at(size_t r, size_t c);
2144
2163 scalar_const_reference operator()(size_t r, size_t c) const;
2164
2178 scalar_const_reference at(size_t r, size_t c) const;
2179
2196 iterator begin() noexcept;
2197
2213
2231 const_iterator cbegin() const noexcept;
2232
2252
2266 bool operator==(StaticMatrix const& that) const;
2267
2269 bool operator==(RowView const& that) const;
2270
2282 bool operator!=(StaticMatrix const& that) const;
2283
2285 bool operator!=(RowView const& that) const;
2286
2300 bool operator<(StaticMatrix const& that) const;
2301
2303 bool operator<(RowView const& that) const;
2304
2318 bool operator>(StaticMatrix const& that) const;
2319
2336
2348 size_t number_of_rows() const noexcept;
2349
2361 size_t number_of_cols() const noexcept;
2362
2380 StaticMatrix operator+(StaticMatrix const& that);
2381
2398 void operator+=(StaticMatrix const& that);
2399
2401 void operator+=(RowView const& that);
2402
2414 void operator+=(scalar_type a);
2415
2433 StaticMatrix operator*(StaticMatrix const& that);
2434
2446 void operator*=(scalar_type a);
2447
2465 StaticMatrix const& y);
2466
2482 RowView row_no_checks(size_t i) const;
2483
2494 RowView row(size_t i) const;
2495
2509 template <typename T>
2510 void rows(T& x) const;
2511
2524 void swap(StaticMatrix& that) noexcept;
2525
2539
2555
2567 static StaticMatrix one() const;
2568
2582 size_t hash_value() const;
2583
2598 template <typename U>
2599 bool operator<=(U const& that) const;
2600
2615 template <typename U>
2616 bool operator>=(U const& that) const;
2617
2631
2646
2657 scalar_type scalar_zero() const noexcept;
2658
2669 scalar_type scalar_one() const noexcept;
2670
2682 semiring_type const* semiring() const noexcept;
2683
2684#else
2685 using MatrixCommon::at;
2686 using MatrixCommon::begin;
2687 using MatrixCommon::cbegin;
2688 using MatrixCommon::cend;
2689 using MatrixCommon::coords;
2690 using MatrixCommon::end;
2691 using MatrixCommon::hash_value;
2692 using MatrixCommon::number_of_cols;
2693 using MatrixCommon::number_of_rows;
2694 using MatrixCommon::one;
2695 using MatrixCommon::operator!=;
2696 using MatrixCommon::operator();
2697 using MatrixCommon::operator*;
2698 using MatrixCommon::operator*=;
2699 using MatrixCommon::operator+;
2700 using MatrixCommon::operator+=;
2701 using MatrixCommon::operator<; // NOLINT(whitespace/operators)
2702 using MatrixCommon::operator<=;
2703 using MatrixCommon::operator==;
2704 using MatrixCommon::operator>; // NOLINT(whitespace/operators)
2705 using MatrixCommon::operator>=;
2706 using MatrixCommon::product_inplace_no_checks;
2707 using MatrixCommon::row;
2708 using MatrixCommon::row_no_checks;
2709 using MatrixCommon::rows;
2710 using MatrixCommon::scalar_one;
2711 using MatrixCommon::scalar_zero;
2712 using MatrixCommon::semiring;
2713 using MatrixCommon::swap;
2714 using MatrixCommon::transpose;
2715 using MatrixCommon::transpose_no_checks;
2716#endif // LIBSEMIGROUPS_PARSED_BY_DOXYGEN
2717
2718 private:
2720 // StaticMatrix - implementation of MatrixCommon requirements - private
2722
2723 static constexpr size_t number_of_rows_impl() noexcept {
2724 return R;
2725 }
2726 static constexpr size_t number_of_cols_impl() noexcept {
2727 return C;
2728 }
2729 };
2730
2732 // DynamicMatrix with compile time semiring arithmetic
2734
2768 template <typename PlusOp,
2769 typename ProdOp,
2770 typename ZeroOp,
2771 typename OneOp,
2772 typename Scalar>
2773 class DynamicMatrix<PlusOp, ProdOp, ZeroOp, OneOp, Scalar>
2774 : public detail::MatrixDynamicDim<Scalar>,
2775 public detail::MatrixCommon<
2776 std::vector<Scalar>,
2777 DynamicMatrix<PlusOp, ProdOp, ZeroOp, OneOp, Scalar>,
2778 DynamicRowView<PlusOp, ProdOp, ZeroOp, OneOp, Scalar>>,
2779 public detail::
2780 MatrixStaticArithmetic<PlusOp, ProdOp, ZeroOp, OneOp, Scalar> {
2781 using MatrixDynamicDim = ::libsemigroups::detail::MatrixDynamicDim<Scalar>;
2782 using MatrixCommon = ::libsemigroups::detail::MatrixCommon<
2785 DynamicRowView<PlusOp, ProdOp, ZeroOp, OneOp, Scalar>>;
2786 friend MatrixCommon;
2787
2788 public:
2790 using scalar_type = typename MatrixCommon::scalar_type;
2791
2793 using scalar_reference = typename MatrixCommon::scalar_reference;
2794
2796 // clang-format off
2797 // NOLINTNEXTLINE(whitespace/line_length)
2798 using scalar_const_reference = typename MatrixCommon::scalar_const_reference;
2799 // clang-format on
2800
2803
2805 using RowView = DynamicRowView<PlusOp, ProdOp, ZeroOp, OneOp, Scalar>;
2806
2808 using Plus = PlusOp;
2809
2811 using Prod = ProdOp;
2812
2814 using Zero = ZeroOp;
2815
2817 using One = OneOp;
2818
2824 using semiring_type = void;
2825
2829 DynamicMatrix() = default;
2830
2834 DynamicMatrix(DynamicMatrix const&) = default;
2835
2840
2845
2850
2869 DynamicMatrix(size_t r, size_t c) : MatrixDynamicDim(r, c), MatrixCommon() {
2870 resize(number_of_rows(), number_of_cols());
2871 }
2872
2893 : MatrixDynamicDim(1, c.size()), MatrixCommon(c) {}
2894
2917 : MatrixDynamicDim(m.size(), std::empty(m) ? 0 : m.begin()->size()),
2918 MatrixCommon(m) {}
2919
2935 : MatrixDynamicDim(m.size(), std::empty(m) ? 0 : m.begin()->size()),
2936 MatrixCommon(m) {}
2937
2949 explicit DynamicMatrix(RowView const& rv)
2950 : MatrixDynamicDim(1, rv.size()), MatrixCommon(rv) {}
2951
2952#ifndef LIBSEMIGROUPS_PARSED_BY_DOXYGEN
2953 // For uniformity of interface, the first arg does nothing
2954 DynamicMatrix(void const* ptr, size_t r, size_t c) : DynamicMatrix(r, c) {
2955 (void) ptr;
2956 LIBSEMIGROUPS_ASSERT(ptr == nullptr);
2957 }
2958
2959 // For uniformity of interface, the first arg does nothing
2960 DynamicMatrix(void const* ptr, std::initializer_list<scalar_type> const& c)
2961 : DynamicMatrix(c) {
2962 (void) ptr;
2963 LIBSEMIGROUPS_ASSERT(ptr == nullptr);
2964 }
2965
2966 // For uniformity of interface, the first arg does nothing
2967 DynamicMatrix(
2968 void const* ptr,
2969 std::initializer_list<std::initializer_list<scalar_type>> const& m)
2970 : DynamicMatrix(m) {
2971 (void) ptr;
2972 LIBSEMIGROUPS_ASSERT(ptr == nullptr);
2973 }
2974
2975 static DynamicMatrix one(void const* ptr, size_t n) {
2976 (void) ptr;
2977 LIBSEMIGROUPS_ASSERT(ptr == nullptr);
2978 return one(n);
2979 }
2980#endif // LIBSEMIGROUPS_PARSED_BY_DOXYGEN
2981
2982 ~DynamicMatrix() = default;
2983
2996 static DynamicMatrix one(size_t n) {
2997 DynamicMatrix x(n, n);
2998 std::fill(x.begin(), x.end(), ZeroOp()());
2999 for (size_t r = 0; r < n; ++r) {
3000 x(r, r) = OneOp()();
3001 }
3002 return x;
3003 }
3004
3005#ifdef LIBSEMIGROUPS_PARSED_BY_DOXYGEN
3007 scalar_reference at(size_t r, size_t c);
3008
3010 scalar_reference at(size_t r, size_t c) const;
3011
3013 iterator begin() noexcept;
3014
3016 const_iterator cbegin() noexcept;
3017
3019 const_iterator cend() noexcept;
3020
3022 std::pair<scalar_type, scalar_type> coords(const_iterator it) const;
3023
3025 iterator end() noexcept;
3026
3028 size_t hash_value() const;
3029
3031 size_t number_of_cols() const noexcept;
3032
3034 size_t number_of_rows() const noexcept;
3035
3037 bool operator!=(DynamicMatrix const& that) const;
3038
3040 bool operator!=(RowView const& that) const;
3041
3043 scalar_reference operator()(size_t r, size_t c);
3044
3046 scalar_const_reference operator()(size_t r, size_t c) const;
3059
3061 DynamicMatrix operator*(DynamicMatrix const& that);
3062
3064 void operator*=(scalar_type a);
3065
3067 DynamicMatrix operator+(DynamicMatrix const& that);
3068
3070 void operator+=(DynamicMatrix const& that);
3071
3073 void operator+=(RowView const& that);
3074
3086 void operator+=(scalar_type a);
3087
3089 bool operator<(DynamicMatrix const& that) const;
3090
3092 bool operator<(RowView const& that) const;
3093
3095 template <typename T>
3096 bool operator<=(T const& that) const;
3097
3099 bool operator==(DynamicMatrix const& that) const;
3100
3102 bool operator==(RowView const& that) const;
3103
3105 bool operator>(DynamicMatrix const& that) const;
3106
3108 template <typename T>
3109 bool operator>=(T const& that) const;
3110
3113 DynamicMatrix const& y);
3114
3116 RowView row(size_t i) const;
3117
3119 RowView row_no_checks(size_t i) const;
3120
3122 template <typename T>
3123 void rows(T& x) const;
3124
3126 scalar_type scalar_one() const noexcept;
3127
3129 scalar_type scalar_zero() const noexcept;
3130
3132 semiring_type const* semiring() const noexcept;
3133
3136
3139#else
3140 using MatrixCommon::at;
3141 using MatrixCommon::begin;
3142 using MatrixCommon::cbegin;
3143 using MatrixCommon::cend;
3144 using MatrixCommon::coords;
3145 using MatrixCommon::end;
3146 using MatrixCommon::hash_value;
3147 using MatrixCommon::number_of_cols;
3148 using MatrixCommon::number_of_rows;
3149 using MatrixCommon::one;
3150 using MatrixCommon::operator!=;
3151 using MatrixCommon::operator();
3152 using MatrixCommon::operator*;
3153 using MatrixCommon::operator*=;
3154 using MatrixCommon::operator+;
3155 using MatrixCommon::operator+=;
3156 using MatrixCommon::operator<; // NOLINT(whitespace/operators)
3157 using MatrixCommon::operator<=;
3158 using MatrixCommon::operator==;
3159 using MatrixCommon::operator>; // NOLINT(whitespace/operators)
3160 using MatrixCommon::operator>=;
3161 using MatrixCommon::product_inplace_no_checks;
3162 using MatrixCommon::row;
3163 using MatrixCommon::row_no_checks;
3164 using MatrixCommon::rows;
3165 using MatrixCommon::scalar_one;
3166 using MatrixCommon::scalar_zero;
3167 using MatrixCommon::semiring;
3168 // using MatrixCommon::swap; // Don't want this use the one below.
3169 using MatrixCommon::transpose;
3170 using MatrixCommon::transpose_no_checks;
3171#endif // LIBSEMIGROUPS_PARSED_BY_DOXYGEN
3172
3174 void swap(DynamicMatrix& that) noexcept {
3175 static_cast<MatrixDynamicDim&>(*this).swap(
3176 static_cast<MatrixDynamicDim&>(that));
3177 static_cast<MatrixCommon&>(*this).swap(static_cast<MatrixCommon&>(that));
3178 }
3179
3180 private:
3181 using MatrixCommon::resize;
3182 };
3183
3185 // DynamicMatrix with runtime semiring arithmetic
3187
3222 template <typename Semiring, typename Scalar>
3223 class DynamicMatrix<Semiring, Scalar>
3224 : public detail::MatrixDynamicDim<Scalar>,
3225 public detail::MatrixCommon<std::vector<Scalar>,
3226 DynamicMatrix<Semiring, Scalar>,
3227 DynamicRowView<Semiring, Scalar>,
3228 Semiring> {
3229 using MatrixCommon = detail::MatrixCommon<std::vector<Scalar>,
3231 DynamicRowView<Semiring, Scalar>,
3232 Semiring>;
3233 friend MatrixCommon;
3234 using MatrixDynamicDim = ::libsemigroups::detail::MatrixDynamicDim<Scalar>;
3235
3236 public:
3238 using scalar_type = typename MatrixCommon::scalar_type;
3239
3241 using scalar_reference = typename MatrixCommon::scalar_reference;
3242
3244 // clang-format off
3245 // NOLINTNEXTLINE(whitespace/line_length)
3246 using scalar_const_reference = typename MatrixCommon::scalar_const_reference;
3247 // clang-format on
3248
3251
3253 using RowView = DynamicRowView<Semiring, Scalar>;
3254
3255 friend RowView;
3256
3258 using semiring_type = Semiring;
3259
3265 DynamicMatrix() = delete;
3266
3268 DynamicMatrix(DynamicMatrix const&) = default;
3269
3272
3275
3278
3293 DynamicMatrix(Semiring const* sr, size_t r, size_t c)
3294 : MatrixDynamicDim(r, c), MatrixCommon(), _semiring(sr) {
3295 resize(number_of_rows(), number_of_cols());
3296 }
3297
3314 Semiring const* sr,
3316 : MatrixDynamicDim(rws.size(),
3317 std::empty(rws) ? 0 : rws.begin()->size()),
3318 MatrixCommon(rws),
3319 _semiring(sr) {}
3320
3336 explicit DynamicMatrix(Semiring const* sr,
3338 : MatrixDynamicDim(rws.size(), (rws.empty() ? 0 : rws.begin()->size())),
3339 MatrixCommon(rws),
3340 _semiring(sr) {}
3341
3355 explicit DynamicMatrix(Semiring const* sr,
3357 : MatrixDynamicDim(1, rw.size()), MatrixCommon(rw), _semiring(sr) {}
3358
3370 explicit DynamicMatrix(RowView const& rv)
3371 : MatrixDynamicDim(1, rv.size()),
3372 MatrixCommon(rv),
3373 _semiring(rv._matrix->semiring()) {}
3374
3389 // No static DynamicMatrix::one(size_t n) because we need a semiring!
3390 static DynamicMatrix one(Semiring const* semiring, size_t n) {
3391 DynamicMatrix x(semiring, n, n);
3392 std::fill(x.begin(), x.end(), x.scalar_zero());
3393 for (size_t r = 0; r < n; ++r) {
3394 x(r, r) = x.scalar_one();
3395 }
3396 return x;
3397 }
3398
3399 ~DynamicMatrix() = default;
3400
3401#ifdef LIBSEMIGROUPS_PARSED_BY_DOXYGEN
3403 scalar_reference at(size_t r, size_t c);
3404
3406 scalar_reference at(size_t r, size_t c) const;
3407
3409 iterator begin() noexcept;
3410
3412 const_iterator cbegin() noexcept;
3413
3415 const_iterator cend() noexcept;
3416
3418 std::pair<scalar_type, scalar_type> coords(const_iterator it) const;
3419
3421 iterator end() noexcept;
3422
3424 size_t hash_value() const;
3425
3427 size_t number_of_cols() const noexcept;
3428
3430 size_t number_of_rows() const noexcept;
3431
3433 bool operator!=(DynamicMatrix const& that) const;
3434
3436 bool operator!=(RowView const& that) const;
3437
3439 scalar_reference operator()(size_t r, size_t c);
3440
3442 scalar_const_reference operator()(size_t r, size_t c) const;
3455
3457 DynamicMatrix operator*(DynamicMatrix const& that);
3458
3460 void operator*=(scalar_type a);
3461
3463 DynamicMatrix operator+(DynamicMatrix const& that);
3464
3466 void operator+=(DynamicMatrix const& that);
3467
3469 void operator+=(RowView const& that);
3470
3482 void operator+=(scalar_type a);
3483
3485 bool operator<(DynamicMatrix const& that) const;
3486
3488 bool operator<(RowView const& that) const;
3489
3491 template <typename T>
3492 bool operator<=(T const& that) const;
3493
3495 bool operator==(DynamicMatrix const& that) const;
3496
3498 bool operator==(RowView const& that) const;
3499
3501 bool operator>(DynamicMatrix const& that) const;
3502
3504 template <typename T>
3505 bool operator>=(T const& that) const;
3506
3509 DynamicMatrix const& y);
3510
3512 RowView row(size_t i) const;
3513
3515 RowView row_no_checks(size_t i) const;
3516
3518 template <typename T>
3519 void rows(T& x) const;
3520
3522 scalar_type scalar_one() const noexcept;
3523
3525 scalar_type scalar_zero() const noexcept;
3526
3528 semiring_type const* semiring() const noexcept;
3529
3532
3535#else
3536 using MatrixCommon::at;
3537 using MatrixCommon::begin;
3538 using MatrixCommon::cbegin;
3539 using MatrixCommon::cend;
3540 using MatrixCommon::coords;
3541 using MatrixCommon::end;
3542 using MatrixCommon::hash_value;
3543 using MatrixCommon::number_of_cols;
3544 using MatrixCommon::number_of_rows;
3545 using MatrixCommon::one;
3546 using MatrixCommon::operator!=;
3547 using MatrixCommon::operator();
3548 using MatrixCommon::operator*;
3549 using MatrixCommon::operator*=;
3550 using MatrixCommon::operator+;
3551 using MatrixCommon::operator+=;
3552 using MatrixCommon::operator<; // NOLINT(whitespace/operators)
3553 using MatrixCommon::operator<=;
3554 using MatrixCommon::operator==;
3555 using MatrixCommon::operator>; // NOLINT(whitespace/operators)
3556 using MatrixCommon::operator>=;
3557 using MatrixCommon::product_inplace_no_checks;
3558 using MatrixCommon::row;
3559 using MatrixCommon::row_no_checks;
3560 using MatrixCommon::rows;
3561 using MatrixCommon::scalar_one;
3562 using MatrixCommon::scalar_zero;
3563 using MatrixCommon::semiring;
3564 // using MatrixCommon::swap; // Don't want this use the one below.
3565 using MatrixCommon::transpose;
3566 using MatrixCommon::transpose_no_checks;
3567#endif // LIBSEMIGROUPS_PARSED_BY_DOXYGEN
3568
3570 void swap(DynamicMatrix& that) noexcept {
3571 static_cast<MatrixDynamicDim&>(*this).swap(
3572 static_cast<MatrixDynamicDim&>(that));
3573 static_cast<MatrixCommon&>(*this).swap(static_cast<MatrixCommon&>(that));
3574 std::swap(_semiring, that._semiring);
3575 }
3576
3577 private:
3578 using MatrixCommon::resize;
3579
3580 scalar_type plus_no_checks_impl(scalar_type x,
3581 scalar_type y) const noexcept {
3582 return _semiring->plus_no_checks(x, y);
3583 }
3584
3585 scalar_type product_no_checks_impl(scalar_type x,
3586 scalar_type y) const noexcept {
3587 return _semiring->product_no_checks(x, y);
3588 }
3589
3590 scalar_type one_impl() const noexcept {
3591 return _semiring->scalar_one();
3592 }
3593
3594 scalar_type zero_impl() const noexcept {
3595 return _semiring->scalar_zero();
3596 }
3597
3598 Semiring const* semiring_impl() const noexcept {
3599 return _semiring;
3600 }
3601
3602 Semiring const* _semiring;
3603 };
3604
3606 // Helper structs to check if matrix is static, or has a pointer to a
3607 // semiring
3609
3610 namespace detail {
3611 template <typename T>
3612 struct IsStaticMatrixHelper : std::false_type {};
3613
3614 template <typename PlusOp,
3615 typename ProdOp,
3616 typename ZeroOp,
3617 typename OneOp,
3618 size_t R,
3619 size_t C,
3620 typename Scalar>
3621 struct IsStaticMatrixHelper<
3622 StaticMatrix<PlusOp, ProdOp, ZeroOp, OneOp, R, C, Scalar>>
3623 : std::true_type {};
3624
3625 template <typename T>
3626 struct IsMatWithSemiringHelper : std::false_type {};
3627
3628 template <typename Semiring, typename Scalar>
3629 struct IsMatWithSemiringHelper<DynamicMatrix<Semiring, Scalar>>
3630 : std::true_type {};
3631
3632 template <typename S, typename T = void>
3633 struct IsTruncMatHelper : std::false_type {};
3634
3635 } // namespace detail
3636
3647 template <typename T>
3648 constexpr bool IsStaticMatrix = detail::IsStaticMatrixHelper<T>::value;
3649
3660 template <typename T>
3662
3673 template <typename T>
3674 static constexpr bool IsMatWithSemiring
3675 = detail::IsMatWithSemiringHelper<T>::value;
3676
3677 namespace detail {
3678
3679 template <typename T>
3680 static constexpr bool IsTruncMat = IsTruncMatHelper<T>::value;
3681
3682 template <typename Mat>
3683 void throw_if_semiring_nullptr(Mat const& m) {
3684 if (IsMatWithSemiring<Mat> && m.semiring() == nullptr) {
3686 "the matrix's pointer to a semiring is nullptr!")
3687 }
3688 }
3689
3690 template <typename Mat, typename Container>
3691 auto throw_if_bad_dim(Container const& m)
3692 -> std::enable_if_t<IsStaticMatrix<Mat>> {
3693 // Only call this if you've already called throw_if_any_row_wrong_size
3694 uint64_t const R = m.size();
3695 uint64_t const C = std::empty(m) ? 0 : m.begin()->size();
3696 if (R != Mat::nr_rows || C != Mat::nr_cols) {
3698 "invalid argument, cannot initialize an {}x{} matrix with compile "
3699 "time dimension, with an {}x{} container",
3700 Mat::nr_rows,
3701 Mat::nr_cols,
3702 R,
3703 C);
3704 }
3705 }
3706
3707 template <typename Mat, typename Container>
3708 auto throw_if_bad_dim(Container const&)
3709 -> std::enable_if_t<IsDynamicMatrix<Mat>> {}
3710 // Not checking dynamic matrices, no compile-time dimensions.
3711
3712 template <typename Mat, typename Container>
3713 auto throw_if_bad_row_dim(Container const& row)
3714 -> std::enable_if_t<IsStaticMatrix<Mat>> {
3715 uint64_t const C = row.size();
3716 if (C != Mat::nr_cols) {
3718 "invalid argument, cannot initialize a row of a matrix with "
3719 "compile time number of columns {} with a container of size {}",
3720 Mat::nr_cols,
3721 C);
3722 }
3723 }
3724
3725 template <typename Mat, typename Container>
3726 auto throw_if_bad_row_dim(Container const&)
3727 -> std::enable_if_t<IsDynamicMatrix<Mat>> {}
3728 } // namespace detail
3729
3738 namespace matrix {
3739
3753 template <typename Mat>
3754 constexpr auto threshold(Mat const&) noexcept
3755 -> std::enable_if_t<!detail::IsTruncMat<Mat>,
3756 typename Mat::scalar_type> {
3757 return UNDEFINED;
3758 }
3759
3773 template <typename Mat>
3774 constexpr auto threshold(Mat const&) noexcept
3775 -> std::enable_if_t<detail::IsTruncMat<Mat> && !IsMatWithSemiring<Mat>,
3776 typename Mat::scalar_type> {
3777 return detail::IsTruncMatHelper<Mat>::threshold;
3778 }
3779
3795 template <typename Mat>
3796 auto threshold(Mat const& x) noexcept
3797 -> std::enable_if_t<detail::IsTruncMat<Mat> && IsMatWithSemiring<Mat>,
3798 typename Mat::scalar_type> {
3799 return x.semiring()->threshold();
3800 }
3801 } // namespace matrix
3802
3804 // Boolean matrices - compile time semiring arithmetic
3806
3840
3863 constexpr bool operator()(bool x, bool y) const noexcept {
3864 return x || y;
3865 }
3866 };
3867
3890 constexpr bool operator()(bool x, bool y) const noexcept {
3891 return x & y;
3892 }
3893 };
3894
3904 struct BooleanOne {
3915 constexpr bool operator()() const noexcept {
3916 return true;
3917 }
3918 };
3919
3940 constexpr bool operator()() const noexcept {
3941 return false;
3942 }
3943 };
3944
3953 // The use of `int` rather than `bool` as the scalar type for dynamic
3954 // boolean matrices is intentional, because the bit iterators implemented in
3955 // std::vector<bool> entail a significant performance penalty.
3957 = DynamicMatrix<BooleanPlus, BooleanProd, BooleanZero, BooleanOne, int>;
3958
3970 template <size_t R, size_t C>
3974 BooleanOne,
3975 R,
3976 C,
3977 int>;
3978
3992 // FLS + JDM considered adding BMat8 and decided it wasn't a good idea.
3993 template <size_t R = 0, size_t C = R>
3994 using BMat
3995 = std::conditional_t<R == 0 || C == 0, DynamicBMat, StaticBMat<R, C>>;
3996
3997 namespace detail {
3998 template <typename T>
3999 struct IsBMatHelper : std::false_type {};
4000
4001 template <size_t R, size_t C>
4002 struct IsBMatHelper<StaticBMat<R, C>> : std::true_type {};
4003
4004 template <>
4005 struct IsBMatHelper<DynamicBMat> : std::true_type {};
4006
4007 template <typename T>
4008 struct BitSetCapacity {
4009 static constexpr size_t value = BitSet<1>::max_size();
4010 };
4011
4012 template <size_t R, size_t C>
4013 struct BitSetCapacity<StaticBMat<R, C>> {
4014 static_assert(R == C, "the number of rows and columns must be equal");
4015 static constexpr size_t value = R;
4016 };
4017 } // namespace detail
4018
4029 template <typename T>
4030 static constexpr bool IsBMat = detail::IsBMatHelper<T>::value;
4031
4032 namespace detail {
4033 // This function is required for exceptions and to_human_readable_repr, so
4034 // that if we encounter an entry of a matrix (Scalar type), then it can be
4035 // printed correctly. If we just did fmt::format("{}", val) and val ==
4036 // POSITIVE_INFINITY, but the type of val is, say, size_t, then this
4037 // wouldn't use the formatter for PositiveInfinity.
4038 //
4039 // Also in fmt v11.1.4 the custom formatter for POSITIVE_INFINITY and
4040 // NEGATIVE_INFINITY stopped working (and I wasn't able to figure out why)
4041 template <typename Scalar>
4042 std::string entry_repr(Scalar a) {
4043 if constexpr (std::is_same_v<Scalar, NegativeInfinity>
4044 || std::is_signed_v<Scalar>) {
4045 if (a == NEGATIVE_INFINITY) {
4046 return u8"-\u221E";
4047 }
4048 }
4049 if (a == POSITIVE_INFINITY) {
4050 return u8"+\u221E";
4051 }
4052 return fmt::format("{}", a);
4053 }
4054 } // namespace detail
4055
4056 namespace matrix {
4057
4073 //! but a matrix shouldn't contain values except \c 0 and \c 1.
4074 template <typename Mat>
4075 std::enable_if_t<IsBMat<Mat>> throw_if_bad_entry(Mat const& m) {
4076 using scalar_type = typename Mat::scalar_type;
4077 auto it = std::find_if_not(
4078 m.cbegin(), m.cend(), [](scalar_type x) { return x == 0 || x == 1; });
4079 if (it != m.cend()) {
4080 auto [r, c] = m.coords(it);
4082 "invalid entry, expected 0 or 1 but found {} in entry ({}, {})",
4083 detail::entry_repr(*it),
4084 r,
4085 c);
4086 }
4087 }
4088
4106 template <typename Mat>
4107 std::enable_if_t<IsBMat<Mat>>
4108 throw_if_bad_entry(Mat const&, typename Mat::scalar_type val) {
4109 if (val != 0 && val != 1) {
4110 LIBSEMIGROUPS_EXCEPTION("invalid entry, expected 0 or 1 but found {}",
4111 detail::entry_repr(val));
4112 }
4113 }
4114 } // namespace matrix
4115
4117 // Integer matrices - compile time semiring arithmetic
4119
4147
4159 //! \tparam Scalar the type of the entries in the matrix.
4160 template <typename Scalar>
4161 struct IntegerPlus {
4172 //! \exceptions
4173 //! \noexcept
4174 constexpr Scalar operator()(Scalar x, Scalar y) const noexcept {
4175 return x + y;
4176 }
4177 };
4178
4190 //! \tparam Scalar the type of the entries in the matrix.
4191 template <typename Scalar>
4192 struct IntegerProd {
4203 //! \exceptions
4204 //! \noexcept
4205 constexpr Scalar operator()(Scalar x, Scalar y) const noexcept {
4206 return x * y;
4207 }
4208 };
4209
4218 //! the additive identity of the integer semiring.
4219 template <typename Scalar>
4220 struct IntegerZero {
4228 //! \exceptions
4229 //! \noexcept
4230 constexpr Scalar operator()() const noexcept {
4231 return 0;
4232 }
4233 };
4234
4243 //! the multiplicative identity of the integer semiring.
4244 template <typename Scalar>
4245 struct IntegerOne {
4253 //! \exceptions
4254 //! \noexcept
4255 constexpr Scalar operator()() const noexcept {
4256 return 1;
4257 }
4258 };
4259
4270 template <typename Scalar>
4271 using DynamicIntMat = DynamicMatrix<IntegerPlus<Scalar>,
4275 Scalar>;
4276
4293 template <size_t R, size_t C, typename Scalar>
4298 R,
4299 C,
4300 Scalar>;
4301
4318 template <size_t R = 0, size_t C = R, typename Scalar = int>
4319 using IntMat = std::conditional_t<R == 0 || C == 0,
4322 namespace detail {
4323 template <typename T>
4324 struct IsIntMatHelper : std::false_type {};
4325
4326 template <size_t R, size_t C, typename Scalar>
4327 struct IsIntMatHelper<StaticIntMat<R, C, Scalar>> : std::true_type {};
4328
4329 template <typename Scalar>
4330 struct IsIntMatHelper<DynamicIntMat<Scalar>> : std::true_type {};
4331 } // namespace detail
4332
4343 template <typename T>
4344 static constexpr bool IsIntMat = detail::IsIntMatHelper<T>::value;
4345
4346 namespace matrix {
4360 //! \param x the matrix to check.
4361 template <typename Mat>
4362 std::enable_if_t<IsIntMat<Mat>> throw_if_bad_entry(Mat const& x) {
4363 using scalar_type = typename Mat::scalar_type;
4364 auto it = std::find_if(x.cbegin(), x.cend(), [](scalar_type val) {
4365 return val == POSITIVE_INFINITY || val == NEGATIVE_INFINITY;
4366 });
4367 if (it != x.cend()) {
4368 auto [r, c] = x.coords(it);
4370 "invalid entry, expected entries to be integers, "
4371 "but found {} in entry ({}, {})",
4372 detail::entry_repr(*it),
4373 r,
4374 c);
4375 }
4376 }
4377
4394 template <typename Mat>
4395 std::enable_if_t<IsIntMat<Mat>>
4396 throw_if_bad_entry(Mat const&, typename Mat::scalar_type val) {
4397 if (val == POSITIVE_INFINITY || val == NEGATIVE_INFINITY) {
4399 "invalid entry, expected entries to be integers, "
4400 "but found {}",
4401 detail::entry_repr(val));
4402 }
4403 }
4404 } // namespace matrix
4405
4407 // Max-plus matrices
4437
4461 // Static arithmetic
4462 template <typename Scalar>
4463 struct MaxPlusPlus {
4464 static_assert(std::is_signed<Scalar>::value,
4465 "MaxPlus requires a signed integer type as parameter!");
4476 //! \exceptions
4477 //! \noexcept
4478 Scalar operator()(Scalar x, Scalar y) const noexcept {
4479 if (x == NEGATIVE_INFINITY) {
4480 return y;
4481 } else if (y == NEGATIVE_INFINITY) {
4482 return x;
4483 }
4484 return std::max(x, y);
4485 }
4486 };
4487
4508 //! integer type).
4509 template <typename Scalar>
4510 struct MaxPlusProd {
4511 static_assert(std::is_signed<Scalar>::value,
4512 "MaxPlus requires a signed integer type as parameter!");
4523 //! \exceptions
4524 //! \noexcept
4525 Scalar operator()(Scalar x, Scalar y) const noexcept {
4526 if (x == NEGATIVE_INFINITY || y == NEGATIVE_INFINITY) {
4527 return NEGATIVE_INFINITY;
4528 }
4529 return x + y;
4530 }
4531 };
4532
4545 //! integer type).
4546 template <typename Scalar>
4547 struct MaxPlusZero {
4548 static_assert(std::is_signed<Scalar>::value,
4549 "MaxPlus requires a signed integer type as parameter!");
4557 //! \exceptions
4558 //! \noexcept
4559 constexpr Scalar operator()() const noexcept {
4560 return NEGATIVE_INFINITY;
4561 }
4562 };
4563
4574 template <typename Scalar>
4575 using DynamicMaxPlusMat = DynamicMatrix<MaxPlusPlus<Scalar>,
4579 Scalar>;
4580
4593 template <size_t R, size_t C, typename Scalar>
4598 R,
4599 C,
4600 Scalar>;
4601
4617 template <size_t R = 0, size_t C = R, typename Scalar = int>
4618 using MaxPlusMat = std::conditional_t<R == 0 || C == 0,
4621
4622 namespace detail {
4623 template <typename T>
4624 struct IsMaxPlusMatHelper : std::false_type {};
4625
4626 template <size_t R, size_t C, typename Scalar>
4627 struct IsMaxPlusMatHelper<StaticMaxPlusMat<R, C, Scalar>> : std::true_type {
4628 };
4629
4630 template <typename Scalar>
4631 struct IsMaxPlusMatHelper<DynamicMaxPlusMat<Scalar>> : std::true_type {};
4632 } // namespace detail
4633
4644 template <typename T>
4645 static constexpr bool IsMaxPlusMat = detail::IsMaxPlusMatHelper<T>::value;
4646
4647 namespace matrix {
4662 //! \ref POSITIVE_INFINITY.
4663 template <typename Mat>
4664 auto throw_if_bad_entry(Mat const& x)
4665 -> std::enable_if_t<IsMaxPlusMat<Mat>> {
4666 using scalar_type = typename Mat::scalar_type;
4667 auto it = std::find_if(x.cbegin(), x.cend(), [](scalar_type val) {
4668 return val == POSITIVE_INFINITY;
4669 });
4670 if (it != x.cend()) {
4671 auto [r, c] = x.coords(it);
4673 "invalid entry, expected entries to be integers or {} (= {}), "
4674 "but found {} (= {}) in entry ({}, {})",
4675 entry_repr(NEGATIVE_INFINITY),
4676 static_cast<scalar_type>(NEGATIVE_INFINITY),
4677 entry_repr(POSITIVE_INFINITY),
4678 static_cast<scalar_type>(POSITIVE_INFINITY),
4679 r,
4680 c);
4681 }
4682 }
4683
4699 template <typename Mat>
4700 std::enable_if_t<IsMaxPlusMat<Mat>>
4701 throw_if_bad_entry(Mat const&, typename Mat::scalar_type val) {
4702 if (val == POSITIVE_INFINITY) {
4703 using scalar_type = typename Mat::scalar_type;
4704 LIBSEMIGROUPS_EXCEPTION("invalid entry, expected entries to be "
4705 "integers or {} (= {}) but found {} (= {})",
4706 entry_repr(NEGATIVE_INFINITY),
4707 static_cast<scalar_type>(NEGATIVE_INFINITY),
4708 entry_repr(POSITIVE_INFINITY),
4709 static_cast<scalar_type>(POSITIVE_INFINITY));
4710 }
4711 }
4712 } // namespace matrix
4713
4715 // Min-plus matrices
4717
4746
4769 // Static arithmetic
4770 template <typename Scalar>
4771 struct MinPlusPlus {
4772 static_assert(std::is_signed<Scalar>::value,
4773 "MinPlus requires a signed integer type as parameter!");
4784 //! \exceptions
4785 //! \noexcept
4786 Scalar operator()(Scalar x, Scalar y) const noexcept {
4787 if (x == POSITIVE_INFINITY) {
4788 return y;
4789 } else if (y == POSITIVE_INFINITY) {
4790 return x;
4791 }
4792 return std::min(x, y);
4793 }
4794 };
4795
4816 //! integer type).
4817 template <typename Scalar>
4818 struct MinPlusProd {
4819 static_assert(std::is_signed<Scalar>::value,
4820 "MinPlus requires a signed integer type as parameter!");
4831 //! \exceptions
4832 //! \noexcept
4833 Scalar operator()(Scalar x, Scalar y) const noexcept {
4834 if (x == POSITIVE_INFINITY || y == POSITIVE_INFINITY) {
4835 return POSITIVE_INFINITY;
4836 }
4837 return x + y;
4838 }
4839 };
4840
4853 //! integer type).
4854 template <typename Scalar>
4855 struct MinPlusZero {
4856 static_assert(std::is_signed<Scalar>::value,
4857 "MinPlus requires a signed integer type as parameter!");
4865 //! \exceptions
4866 //! \noexcept
4867 constexpr Scalar operator()() const noexcept {
4868 return POSITIVE_INFINITY;
4869 }
4870 };
4871
4882 template <typename Scalar>
4883 using DynamicMinPlusMat = DynamicMatrix<MinPlusPlus<Scalar>,
4887 Scalar>;
4888
4901 template <size_t R, size_t C, typename Scalar>
4906 R,
4907 C,
4908 Scalar>;
4925 template <size_t R = 0, size_t C = R, typename Scalar = int>
4926 using MinPlusMat = std::conditional_t<R == 0 || C == 0,
4929
4930 namespace detail {
4931 template <typename T>
4932 struct IsMinPlusMatHelper : std::false_type {};
4933
4934 template <size_t R, size_t C, typename Scalar>
4935 struct IsMinPlusMatHelper<StaticMinPlusMat<R, C, Scalar>> : std::true_type {
4936 };
4937
4938 template <typename Scalar>
4939 struct IsMinPlusMatHelper<DynamicMinPlusMat<Scalar>> : std::true_type {};
4940 } // namespace detail
4941
4952 template <typename T>
4953 static constexpr bool IsMinPlusMat = detail::IsMinPlusMatHelper<T>::value;
4954
4955 namespace matrix {
4970 //! \ref NEGATIVE_INFINITY.
4971 template <typename Mat>
4972 std::enable_if_t<IsMinPlusMat<Mat>> throw_if_bad_entry(Mat const& x) {
4973 using scalar_type = typename Mat::scalar_type;
4974 auto it = std::find_if(x.cbegin(), x.cend(), [](scalar_type val) {
4975 return val == NEGATIVE_INFINITY;
4976 });
4977 if (it != x.cend()) {
4978 auto [r, c] = x.coords(it);
4980 "invalid entry, expected entries to be integers or {} (= {}), "
4981 "but found {} (= {}) in entry ({}, {})",
4982 entry_repr(POSITIVE_INFINITY),
4983 static_cast<scalar_type>(POSITIVE_INFINITY),
4984 entry_repr(NEGATIVE_INFINITY),
4985 static_cast<scalar_type>(NEGATIVE_INFINITY),
4986 r,
4987 c);
4988 }
4989 }
4990
5006 template <typename Mat>
5007 std::enable_if_t<IsMinPlusMat<Mat>>
5008 throw_if_bad_entry(Mat const&, typename Mat::scalar_type val) {
5009 if (val == NEGATIVE_INFINITY) {
5010 using scalar_type = typename Mat::scalar_type;
5011 LIBSEMIGROUPS_EXCEPTION("invalid entry, expected entries to be "
5012 "integers or {} (= {}) but found {} (= {})",
5013 entry_repr(POSITIVE_INFINITY),
5014 static_cast<scalar_type>(POSITIVE_INFINITY),
5015 entry_repr(NEGATIVE_INFINITY),
5016 static_cast<scalar_type>(NEGATIVE_INFINITY));
5017 }
5018 }
5019 } // namespace matrix
5020
5022 // Max-plus matrices with threshold
5024
5070
5095 //! integer type).
5096 template <size_t T, typename Scalar>
5097 struct MaxPlusTruncProd {
5098 static_assert(std::is_signed<Scalar>::value,
5099 "MaxPlus requires a signed integer type as parameter!");
5110 //! \exceptions
5111 //! \noexcept
5112 Scalar operator()(Scalar x, Scalar y) const noexcept {
5113 LIBSEMIGROUPS_ASSERT((x >= 0 && x <= static_cast<Scalar>(T))
5114 || x == NEGATIVE_INFINITY);
5115 LIBSEMIGROUPS_ASSERT((y >= 0 && y <= static_cast<Scalar>(T))
5116 || y == NEGATIVE_INFINITY);
5117 if (x == NEGATIVE_INFINITY || y == NEGATIVE_INFINITY) {
5118 return NEGATIVE_INFINITY;
5119 }
5120 return std::min(x + y, static_cast<Scalar>(T));
5121 }
5122 };
5123
5137 //! signed integer type (defaults to \c int).
5138 template <typename Scalar = int>
5139 class MaxPlusTruncSemiring {
5140 static_assert(std::is_signed<Scalar>::value,
5141 "MaxPlus requires a signed integer type as parameter!");
5142
5143 public:
5147 MaxPlusTruncSemiring() = delete;
5148
5152 MaxPlusTruncSemiring(MaxPlusTruncSemiring const&) noexcept = default;
5153
5157 MaxPlusTruncSemiring(MaxPlusTruncSemiring&&) noexcept = default;
5158
5162 MaxPlusTruncSemiring& operator=(MaxPlusTruncSemiring const&) noexcept
5163 = default;
5164
5168 MaxPlusTruncSemiring& operator=(MaxPlusTruncSemiring&&) noexcept = default;
5169
5170 ~MaxPlusTruncSemiring() = default;
5171
5180 //! \complexity
5181 //! Constant.
5182 explicit MaxPlusTruncSemiring(Scalar threshold) : _threshold(threshold) {
5183 if (threshold < 0) {
5184 LIBSEMIGROUPS_EXCEPTION("expected non-negative value, found {}",
5185 threshold);
5186 }
5187 }
5188
5197 //! \exceptions
5198 //! \noexcept
5199 static constexpr Scalar scalar_one() noexcept {
5200 return 0;
5201 }
5202
5211 //! \exceptions
5212 //! \noexcept
5213 static constexpr Scalar scalar_zero() noexcept {
5214 return NEGATIVE_INFINITY;
5215 }
5216
5239 //! \complexity
5240 //! Constant.
5241 Scalar product_no_checks(Scalar x, Scalar y) const noexcept {
5242 LIBSEMIGROUPS_ASSERT((x >= 0 && x <= _threshold)
5243 || x == NEGATIVE_INFINITY);
5244 LIBSEMIGROUPS_ASSERT((y >= 0 && y <= _threshold)
5245 || y == NEGATIVE_INFINITY);
5246 if (x == NEGATIVE_INFINITY || y == NEGATIVE_INFINITY) {
5247 return NEGATIVE_INFINITY;
5248 }
5249 return std::min(x + y, _threshold);
5250 }
5251
5274 //! \complexity
5275 //! Constant.
5276 Scalar plus_no_checks(Scalar x, Scalar y) const noexcept {
5277 LIBSEMIGROUPS_ASSERT((x >= 0 && x <= _threshold)
5278 || x == NEGATIVE_INFINITY);
5279 LIBSEMIGROUPS_ASSERT((y >= 0 && y <= _threshold)
5280 || y == NEGATIVE_INFINITY);
5281 if (x == NEGATIVE_INFINITY) {
5282 return y;
5283 } else if (y == NEGATIVE_INFINITY) {
5284 return x;
5285 }
5286 return std::max(x, y);
5287 }
5288
5299 //! \complexity
5300 //! Constant.
5301 Scalar threshold() const noexcept {
5302 return _threshold;
5303 }
5304
5305 public:
5306 Scalar const _threshold;
5307 };
5308
5321 template <size_t T, typename Scalar>
5322 using DynamicMaxPlusTruncMat = DynamicMatrix<MaxPlusPlus<Scalar>,
5326 Scalar>;
5327
5341 template <size_t T, size_t R, size_t C, typename Scalar>
5346 R,
5347 C,
5348 Scalar>;
5367 template <size_t T = 0, size_t R = 0, size_t C = R, typename Scalar = int>
5368 using MaxPlusTruncMat = std::conditional_t<
5369 R == 0 || C == 0,
5370 std::conditional_t<T == 0,
5371 DynamicMatrix<MaxPlusTruncSemiring<Scalar>, Scalar>,
5374
5375 namespace detail {
5376 template <typename T>
5377 struct IsMaxPlusTruncMatHelper : std::false_type {};
5378
5379 template <size_t T, size_t R, size_t C, typename Scalar>
5380 struct IsMaxPlusTruncMatHelper<StaticMaxPlusTruncMat<T, R, C, Scalar>>
5381 : std::true_type {
5382 static constexpr Scalar threshold = T;
5383 };
5384
5385 template <size_t T, typename Scalar>
5386 struct IsMaxPlusTruncMatHelper<DynamicMaxPlusTruncMat<T, Scalar>>
5387 : std::true_type {
5388 static constexpr Scalar threshold = T;
5389 };
5390
5391 template <typename Scalar>
5392 struct IsMaxPlusTruncMatHelper<
5393 DynamicMatrix<MaxPlusTruncSemiring<Scalar>, Scalar>> : std::true_type {
5394 static constexpr Scalar threshold = UNDEFINED;
5395 };
5396 } // namespace detail
5397
5409 template <typename T>
5410 static constexpr bool IsMaxPlusTruncMat
5411 = detail::IsMaxPlusTruncMatHelper<T>::value;
5412
5413 namespace detail {
5414 template <typename T>
5415 struct IsTruncMatHelper<T, std::enable_if_t<IsMaxPlusTruncMat<T>>>
5416 : std::true_type {
5417 static constexpr typename T::scalar_type threshold
5418 = IsMaxPlusTruncMatHelper<T>::threshold;
5419 };
5420 } // namespace detail
5421
5422 namespace matrix {
5440 //! (only applies to matrices with run time arithmetic).
5441 template <typename Mat>
5442 std::enable_if_t<IsMaxPlusTruncMat<Mat>> throw_if_bad_entry(Mat const& m) {
5443 // TODO(1) to tpp
5444 detail::throw_if_semiring_nullptr(m);
5445
5446 using scalar_type = typename Mat::scalar_type;
5447 scalar_type const t = matrix::threshold(m);
5448 auto it = std::find_if_not(m.cbegin(), m.cend(), [t](scalar_type x) {
5449 return x == NEGATIVE_INFINITY || (0 <= x && x <= t);
5450 });
5451 if (it != m.cend()) {
5452 auto [r, c] = m.coords(it);
5454 "invalid entry, expected values in {{0, 1, ..., {}, {} (= {})}} "
5455 "but found {} in entry ({}, {})",
5456 t,
5457 entry_repr(NEGATIVE_INFINITY),
5458 static_cast<scalar_type>(NEGATIVE_INFINITY),
5459 detail::entry_repr(*it),
5460 r,
5461 c);
5462 }
5463 }
5464
5484 template <typename Mat>
5485 std::enable_if_t<IsMaxPlusTruncMat<Mat>>
5486 throw_if_bad_entry(Mat const& m, typename Mat::scalar_type val) {
5487 detail::throw_if_semiring_nullptr(m);
5488 using scalar_type = typename Mat::scalar_type;
5489 scalar_type const t = matrix::threshold(m);
5490 if (val == POSITIVE_INFINITY || 0 > val || val > t) {
5492 "invalid entry, expected values in {{0, 1, ..., {}, -{} (= {})}} "
5493 "but found {}",
5494 t,
5495 entry_repr(NEGATIVE_INFINITY),
5496 static_cast<scalar_type>(NEGATIVE_INFINITY),
5497 detail::entry_repr(val));
5498 }
5499 }
5500 } // namespace matrix
5501
5503 // Min-plus matrices with threshold
5505
5551
5575 //! \tparam Scalar the type of the values in the semiring.
5576 template <size_t T, typename Scalar>
5577 struct MinPlusTruncProd {
5588 //! \exceptions
5589 //! \noexcept
5590 Scalar operator()(Scalar x, Scalar y) const noexcept {
5591 LIBSEMIGROUPS_ASSERT((x >= 0 && x <= static_cast<Scalar>(T))
5592 || x == POSITIVE_INFINITY);
5593 LIBSEMIGROUPS_ASSERT((y >= 0 && y <= static_cast<Scalar>(T))
5594 || y == POSITIVE_INFINITY);
5595 if (x == POSITIVE_INFINITY || y == POSITIVE_INFINITY) {
5596 return POSITIVE_INFINITY;
5597 }
5598 return std::min(x + y, static_cast<Scalar>(T));
5599 }
5600 };
5601
5614 //! integral type.
5615 template <typename Scalar = int>
5616 class MinPlusTruncSemiring {
5617 static_assert(std::is_integral<Scalar>::value,
5618 "MinPlus requires an integral type as parameter!");
5619
5620 public:
5624 MinPlusTruncSemiring() = delete;
5625
5629 MinPlusTruncSemiring(MinPlusTruncSemiring const&) noexcept = default;
5630
5634 MinPlusTruncSemiring(MinPlusTruncSemiring&&) noexcept = default;
5635
5639 MinPlusTruncSemiring& operator=(MinPlusTruncSemiring const&) noexcept
5640 = default;
5641
5645 MinPlusTruncSemiring& operator=(MinPlusTruncSemiring&&) noexcept = default;
5646
5655 //! \complexity
5656 //! Constant.
5657 explicit MinPlusTruncSemiring(Scalar threshold) : _threshold(threshold) {
5659 LIBSEMIGROUPS_EXCEPTION("expected non-negative value, found {}",
5660 threshold);
5661 }
5662 }
5663
5672 //! \exceptions
5673 //! \noexcept
5674 static constexpr Scalar scalar_one() noexcept {
5675 return 0;
5676 }
5677
5687 //! \noexcept
5688 // TODO(1) These mem fns (one and zero) aren't needed?
5689 static constexpr Scalar scalar_zero() noexcept {
5690 return POSITIVE_INFINITY;
5691 }
5692
5715 //! \complexity
5716 //! Constant.
5717 Scalar product_no_checks(Scalar x, Scalar y) const noexcept {
5718 LIBSEMIGROUPS_ASSERT((x >= 0 && x <= _threshold)
5719 || x == POSITIVE_INFINITY);
5720 LIBSEMIGROUPS_ASSERT((y >= 0 && y <= _threshold)
5721 || y == POSITIVE_INFINITY);
5722 if (x == POSITIVE_INFINITY || y == POSITIVE_INFINITY) {
5723 return POSITIVE_INFINITY;
5724 }
5725 return std::min(x + y, _threshold);
5726 }
5727
5750 //! \complexity
5751 //! Constant.
5752 Scalar plus_no_checks(Scalar x, Scalar y) const noexcept {
5753 LIBSEMIGROUPS_ASSERT((x >= 0 && x <= _threshold)
5754 || x == POSITIVE_INFINITY);
5755 LIBSEMIGROUPS_ASSERT((y >= 0 && y <= _threshold)
5756 || y == POSITIVE_INFINITY);
5757 if (x == POSITIVE_INFINITY) {
5758 return y;
5759 } else if (y == POSITIVE_INFINITY) {
5760 return x;
5761 }
5762 return std::min(x, y);
5763 }
5764
5775 //! \complexity
5776 //! Constant.
5777 Scalar threshold() const noexcept {
5778 return _threshold;
5779 }
5780
5781 public:
5782 Scalar const _threshold;
5783 };
5784
5797 template <size_t T, typename Scalar>
5798 using DynamicMinPlusTruncMat = DynamicMatrix<MinPlusPlus<Scalar>,
5802 Scalar>;
5803
5817 template <size_t T, size_t R, size_t C, typename Scalar>
5822 R,
5823 C,
5824 Scalar>;
5825
5844 template <size_t T = 0, size_t R = 0, size_t C = R, typename Scalar = int>
5845 using MinPlusTruncMat = std::conditional_t<
5846 R == 0 || C == 0,
5847 std::conditional_t<T == 0,
5848 DynamicMatrix<MinPlusTruncSemiring<Scalar>, Scalar>,
5851
5852 namespace detail {
5853 template <typename T>
5854 struct IsMinPlusTruncMatHelper : std::false_type {};
5855
5856 template <size_t T, size_t R, size_t C, typename Scalar>
5857 struct IsMinPlusTruncMatHelper<StaticMinPlusTruncMat<T, R, C, Scalar>>
5858 : std::true_type {
5859 static constexpr Scalar threshold = T;
5860 };
5861
5862 template <size_t T, typename Scalar>
5863 struct IsMinPlusTruncMatHelper<DynamicMinPlusTruncMat<T, Scalar>>
5864 : std::true_type {
5865 static constexpr Scalar threshold = T;
5866 };
5867
5868 template <typename Scalar>
5869 struct IsMinPlusTruncMatHelper<
5870 DynamicMatrix<MinPlusTruncSemiring<Scalar>, Scalar>> : std::true_type {
5871 static constexpr Scalar threshold = UNDEFINED;
5872 };
5873 } // namespace detail
5874
5886 template <typename T>
5887 static constexpr bool IsMinPlusTruncMat
5888 = detail::IsMinPlusTruncMatHelper<T>::value;
5889
5890 namespace detail {
5891 template <typename T>
5892 struct IsTruncMatHelper<T, std::enable_if_t<IsMinPlusTruncMat<T>>>
5893 : std::true_type {
5894 static constexpr typename T::scalar_type threshold
5895 = IsMinPlusTruncMatHelper<T>::threshold;
5896 };
5897 } // namespace detail
5898
5899 namespace matrix {
5918 // TODO(1) to tpp
5919 template <typename Mat>
5920 std::enable_if_t<IsMinPlusTruncMat<Mat>> throw_if_bad_entry(Mat const& m) {
5921 // Check that the semiring pointer isn't the nullptr if it shouldn't be
5922 detail::throw_if_semiring_nullptr(m);
5923
5924 using scalar_type = typename Mat::scalar_type;
5925 scalar_type const t = matrix::threshold(m);
5926 auto it = std::find_if_not(m.cbegin(), m.cend(), [t](scalar_type x) {
5927 return x == POSITIVE_INFINITY || (0 <= x && x <= t);
5928 });
5929 if (it != m.cend()) {
5930 uint64_t r, c;
5931 std::tie(r, c) = m.coords(it);
5932
5934 "invalid entry, expected values in {{0, 1, ..., {}, {} (= {})}} "
5935 "but found {} in entry ({}, {})",
5936 t,
5937 detail::entry_repr(POSITIVE_INFINITY),
5938 static_cast<scalar_type>(POSITIVE_INFINITY),
5939 detail::entry_repr(*it),
5940 r,
5941 c);
5942 }
5943 }
5944
5964 template <typename Mat>
5965 std::enable_if_t<IsMinPlusTruncMat<Mat>>
5966 throw_if_bad_entry(Mat const& m, typename Mat::scalar_type val) {
5967 detail::throw_if_semiring_nullptr(m);
5968
5969 using scalar_type = typename Mat::scalar_type;
5970 scalar_type const t = matrix::threshold(m);
5971 if (!(val == POSITIVE_INFINITY || (0 <= val && val <= t))) {
5973 "invalid entry, expected values in {{0, 1, ..., {}, {} (= {})}} "
5974 "but found {}",
5975 t,
5976 detail::entry_repr(POSITIVE_INFINITY),
5977 static_cast<scalar_type>(POSITIVE_INFINITY),
5978 detail::entry_repr(val));
5979 }
5980 }
5981 } // namespace matrix
5982
5984 // NTP matrices
5986
6039
6040 namespace detail {
6041 template <size_t T, size_t P, typename Scalar>
6042 constexpr Scalar thresholdperiod(Scalar x) noexcept {
6043 if (x > T) {
6044 return T + (x - T) % P;
6045 }
6046 return x;
6047 }
6048
6049 template <typename Scalar>
6050 inline Scalar thresholdperiod(Scalar x,
6051 Scalar threshold,
6052 Scalar period) noexcept {
6053 if (x > threshold) {
6054 return threshold + (x - threshold) % period;
6055 }
6056 return x;
6057 }
6058 } // namespace detail
6059
6082 // Static arithmetic
6083 template <size_t T, size_t P, typename Scalar>
6084 struct NTPPlus {
6094 //! \exceptions
6095 //! \noexcept
6096 constexpr Scalar operator()(Scalar x, Scalar y) const noexcept {
6097 return detail::thresholdperiod<T, P>(x + y);
6098 }
6099 };
6100
6123 //! \tparam Scalar the type of the values in the semiring.
6124 template <size_t T, size_t P, typename Scalar>
6125 struct NTPProd {
6137 //! \exceptions
6138 //! \noexcept
6139 constexpr Scalar operator()(Scalar x, Scalar y) const noexcept {
6140 return detail::thresholdperiod<T, P>(x * y);
6141 }
6142 };
6143
6157 // Dynamic arithmetic
6158 template <typename Scalar = size_t>
6159 class NTPSemiring {
6160 public:
6164 // Deleted to avoid uninitialised values of period and threshold.
6165 NTPSemiring() = delete;
6166
6170 NTPSemiring(NTPSemiring const&) = default;
6171
6175 NTPSemiring(NTPSemiring&&) = default;
6176
6180 NTPSemiring& operator=(NTPSemiring const&) = default;
6181
6185 NTPSemiring& operator=(NTPSemiring&&) = default;
6186
6187 ~NTPSemiring() = default;
6188
6199 //! \complexity
6200 //! Constant.
6201 NTPSemiring(Scalar t, Scalar p) : _period(p), _threshold(t) {
6202 if constexpr (std::is_signed<Scalar>::value) {
6203 if (t < 0) {
6205 "expected non-negative value for 1st argument, found {}", t);
6206 }
6207 }
6208 if (p <= 0) {
6210 "expected positive value for 2nd argument, found {}", p);
6211 }
6212 }
6213
6222 //! \exceptions
6223 //! \noexcept
6224 static constexpr Scalar scalar_one() noexcept {
6225 return 1;
6226 }
6227
6238 //! \complexity
6239 //! Constant.
6240 static constexpr Scalar scalar_zero() noexcept {
6241 return 0;
6242 }
6243
6266 //! \complexity
6267 //! Constant.
6268 Scalar product_no_checks(Scalar x, Scalar y) const noexcept {
6269 LIBSEMIGROUPS_ASSERT(x >= 0 && x <= _period + _threshold - 1);
6270 LIBSEMIGROUPS_ASSERT(y >= 0 && y <= _period + _threshold - 1);
6271 return detail::thresholdperiod(x * y, _threshold, _period);
6272 }
6273
6296 //! \complexity
6297 //! Constant.
6298 Scalar plus_no_checks(Scalar x, Scalar y) const noexcept {
6299 LIBSEMIGROUPS_ASSERT(x >= 0 && x <= _period + _threshold - 1);
6300 LIBSEMIGROUPS_ASSERT(y >= 0 && y <= _period + _threshold - 1);
6301 return detail::thresholdperiod(x + y, _threshold, _period);
6302 }
6303
6314 //! \complexity
6315 //! Constant.
6316 Scalar threshold() const noexcept {
6317 return _threshold;
6318 }
6319
6330 //! \complexity
6331 //! Constant.
6332 Scalar period() const noexcept {
6333 return _period;
6334 }
6335
6336 private:
6337 Scalar _period;
6338 Scalar _threshold;
6339 };
6340
6351 template <typename Scalar>
6352 using DynamicNTPMatWithSemiring = DynamicMatrix<NTPSemiring<Scalar>, Scalar>;
6353
6367 template <size_t T, size_t P, typename Scalar>
6368 using DynamicNTPMatWithoutSemiring = DynamicMatrix<NTPPlus<T, P, Scalar>,
6372 Scalar>;
6373
6393 template <size_t T, size_t P, size_t R, size_t C, typename Scalar>
6398 R,
6399 C,
6400 Scalar>;
6401
6424 template <size_t T = 0,
6425 size_t P = 0,
6426 size_t R = 0,
6427 size_t C = R,
6428 typename Scalar = size_t>
6429 using NTPMat = std::conditional_t<
6430 R == 0 || C == 0,
6431 std::conditional_t<T == 0 && P == 0,
6435
6436 namespace detail {
6437 template <typename T>
6438 struct IsNTPMatHelper : std::false_type {};
6439
6440 template <typename Scalar>
6441 struct IsNTPMatHelper<DynamicNTPMatWithSemiring<Scalar>> : std::true_type {
6442 static constexpr Scalar threshold = UNDEFINED;
6443 static constexpr Scalar period = UNDEFINED;
6444 };
6445
6446 template <size_t T, size_t P, typename Scalar>
6447 struct IsNTPMatHelper<DynamicNTPMatWithoutSemiring<T, P, Scalar>>
6448 : std::true_type {
6449 static constexpr Scalar threshold = T;
6450 static constexpr Scalar period = P;
6451 };
6452
6453 template <size_t T, size_t P, size_t R, size_t C, typename Scalar>
6454 struct IsNTPMatHelper<StaticNTPMat<T, P, R, C, Scalar>> : std::true_type {
6455 static constexpr Scalar threshold = T;
6456 static constexpr Scalar period = P;
6457 };
6458 } // namespace detail
6459
6471 template <typename U>
6472 static constexpr bool IsNTPMat = detail::IsNTPMatHelper<U>::value;
6473
6474 namespace detail {
6475 template <typename T>
6476 struct IsTruncMatHelper<T, std::enable_if_t<IsNTPMat<T>>> : std::true_type {
6477 static constexpr typename T::scalar_type threshold
6478 = IsNTPMatHelper<T>::threshold;
6479 static constexpr typename T::scalar_type period
6480 = IsNTPMatHelper<T>::period;
6481 };
6482
6483 } // namespace detail
6484
6485 namespace matrix {
6506 //! \noexcept
6507 template <size_t T, size_t P, size_t R, size_t C, typename Scalar>
6508 constexpr Scalar period(StaticNTPMat<T, P, R, C, Scalar> const&) noexcept {
6509 return P;
6510 }
6511
6529 template <size_t T, size_t P, typename Scalar>
6530 constexpr Scalar
6532 return P;
6533 }
6534
6549 //! \noexcept
6550 template <typename Scalar>
6551 Scalar period(DynamicNTPMatWithSemiring<Scalar> const& x) noexcept {
6552 return x.semiring()->period();
6553 }
6554 } // namespace matrix
6555
6556 namespace matrix {
6576 //! defined (only applies to matrices with run time arithmetic).
6577 template <typename Mat>
6578 std::enable_if_t<IsNTPMat<Mat>> throw_if_bad_entry(Mat const& m) {
6579 detail::throw_if_semiring_nullptr(m);
6580
6581 using scalar_type = typename Mat::scalar_type;
6582 scalar_type const t = matrix::threshold(m);
6583 scalar_type const p = matrix::period(m);
6584 auto it = std::find_if_not(m.cbegin(), m.cend(), [t, p](scalar_type x) {
6585 return (0 <= x && x < p + t);
6586 });
6587 if (it != m.cend()) {
6588 uint64_t r, c;
6589 std::tie(r, c) = m.coords(it);
6590
6592 "invalid entry, expected values in {{0, 1, ..., {}}}, but "
6593 "found {} in entry ({}, {})",
6594 p + t,
6595 detail::entry_repr(*it),
6596 r,
6597 c);
6598 }
6599 }
6600
6622 template <typename Mat>
6623 std::enable_if_t<IsNTPMat<Mat>>
6624 throw_if_bad_entry(Mat const& m, typename Mat::scalar_type val) {
6625 detail::throw_if_semiring_nullptr(m);
6626 using scalar_type = typename Mat::scalar_type;
6627 scalar_type const t = matrix::threshold(m);
6628 scalar_type const p = matrix::period(m);
6629 if (val < 0 || val >= p + t) {
6631 "invalid entry, expected values in {{0, 1, ..., {}}}, but "
6632 "found {}",
6633 p + t,
6634 detail::entry_repr(val));
6635 }
6636 }
6637 } // namespace matrix
6638
6640 // Projective max-plus matrices
6642
6643 namespace detail {
6644 template <typename T>
6645 class ProjMaxPlusMat : MatrixPolymorphicBase {
6646 public:
6647 using scalar_type = typename T::scalar_type;
6648 using scalar_reference = typename T::scalar_reference;
6649 using scalar_const_reference = typename T::scalar_const_reference;
6650 using semiring_type = void;
6651
6652 using container_type = typename T::container_type;
6653 using iterator = typename T::iterator;
6654 using const_iterator = typename T::const_iterator;
6655
6656 using underlying_matrix_type = T;
6657
6658 using RowView = typename T::RowView;
6659
6660 // Note that Rows are never normalised, and that's why we use the
6661 // underlying matrix Row type and not 1 x n ProjMaxPlusMat's instead
6662 // (since these will be normalised according to their entries, and
6663 // this might not correspond to the normalised entries of the matrix).
6664 using Row = typename T::Row;
6665
6666 scalar_type scalar_one() const noexcept {
6667 return _underlying_mat.scalar_one();
6668 }
6669
6670 scalar_type scalar_zero() const noexcept {
6671 return _underlying_mat.scalar_zero();
6672 }
6673
6675 // ProjMaxPlusMat - Constructors + destructor - public
6677
6678 ProjMaxPlusMat() : _is_normalized(false), _underlying_mat() {}
6679 ProjMaxPlusMat(ProjMaxPlusMat const&) = default;
6680 ProjMaxPlusMat(ProjMaxPlusMat&&) = default;
6681 ProjMaxPlusMat& operator=(ProjMaxPlusMat const&) = default;
6682 ProjMaxPlusMat& operator=(ProjMaxPlusMat&&) = default;
6683
6684 ProjMaxPlusMat(size_t r, size_t c)
6685 : _is_normalized(false), _underlying_mat(r, c) {}
6686
6687 // TODO(1) other missing constructors
6688 ProjMaxPlusMat(
6689 typename underlying_matrix_type::semiring_type const* semiring,
6690 size_t r,
6691 size_t c)
6692 : _is_normalized(false), _underlying_mat(semiring, r, c) {}
6693
6694 explicit ProjMaxPlusMat(std::vector<std::vector<scalar_type>> const& m)
6695 : _is_normalized(false), _underlying_mat(m) {
6696 normalize();
6697 }
6698
6699 ProjMaxPlusMat(
6700 std::initializer_list<std::initializer_list<scalar_type>> const& m)
6701 : ProjMaxPlusMat(
6702 std::vector<std::vector<scalar_type>>(m.begin(), m.end())) {}
6703
6704 ~ProjMaxPlusMat() = default;
6705
6706 ProjMaxPlusMat one() const {
6707 auto result = ProjMaxPlusMat(_underlying_mat.one());
6708 return result;
6709 }
6710
6711 static ProjMaxPlusMat one(size_t n) {
6712 return ProjMaxPlusMat(T::one(n));
6713 }
6714
6716 // Comparison operators
6718
6719 bool operator==(ProjMaxPlusMat const& that) const {
6720 normalize();
6721 that.normalize();
6722 return _underlying_mat == that._underlying_mat;
6723 }
6724
6725 bool operator!=(ProjMaxPlusMat const& that) const {
6726 return !(_underlying_mat == that._underlying_mat);
6727 }
6728
6729 bool operator<(ProjMaxPlusMat const& that) const {
6730 normalize();
6731 that.normalize();
6732 return _underlying_mat < that._underlying_mat;
6733 }
6734
6735 bool operator>(ProjMaxPlusMat const& that) const {
6736 return that < *this;
6737 }
6738
6739 template <typename Thing>
6740 bool operator>=(Thing const& that) const {
6741 static_assert(IsMatrix<Thing> || std::is_same_v<Thing, RowView>);
6742 return that < *this || that == *this;
6743 }
6744
6745 // not noexcept because operator< isn't
6746 template <typename Thing>
6747 bool operator<=(Thing const& that) const {
6748 static_assert(IsMatrix<Thing> || std::is_same_v<Thing, RowView>);
6749 return *this < that || that == *this;
6750 }
6751
6753 // Attributes
6755
6756 scalar_reference operator()(size_t r, size_t c) {
6757 // to ensure the returned value is normalised
6758 normalize();
6759 // to ensure that the matrix is renormalised if the returned scalar is
6760 // assigned.
6761 _is_normalized = false;
6762 return _underlying_mat(r, c);
6763 }
6764
6765 scalar_reference at(size_t r, size_t c) {
6766 matrix::throw_if_bad_coords(*this, r, c);
6767 return this->operator()(r, c);
6768 }
6769
6770 scalar_const_reference operator()(size_t r, size_t c) const {
6771 normalize();
6772 return _underlying_mat(r, c);
6773 }
6774
6775 scalar_const_reference at(size_t r, size_t c) const {
6776 matrix::throw_if_bad_coords(*this, r, c);
6777 return this->operator()(r, c);
6778 }
6779
6780 size_t number_of_rows() const noexcept {
6781 return _underlying_mat.number_of_rows();
6782 }
6783
6784 size_t number_of_cols() const noexcept {
6785 return _underlying_mat.number_of_cols();
6786 }
6787
6788 size_t hash_value() const {
6789 normalize();
6790 return Hash<T>()(_underlying_mat);
6791 }
6792
6794 // Arithmetic operators - in-place
6796
6797 void product_inplace_no_checks(ProjMaxPlusMat const& A,
6798 ProjMaxPlusMat const& B) {
6799 _underlying_mat.product_inplace_no_checks(A._underlying_mat,
6800 B._underlying_mat);
6801 normalize(true); // force normalize
6802 }
6803
6804 void operator+=(ProjMaxPlusMat const& that) {
6805 _underlying_mat += that._underlying_mat;
6806 normalize(true); // force normalize
6807 }
6808
6809 void operator*=(scalar_type a) {
6810 _underlying_mat *= a;
6811 normalize(true); // force normalize
6812 }
6813
6814 void operator+=(scalar_type a) {
6815 _underlying_mat += a;
6816 normalize(true); // force normalize
6817 }
6818
6819 ProjMaxPlusMat operator*(scalar_type a) const {
6820 ProjMaxPlusMat result(*this);
6821 result *= a;
6822 return result;
6823 }
6824
6825 ProjMaxPlusMat operator+(scalar_type a) const {
6826 ProjMaxPlusMat result(*this);
6827 result += a;
6828 return result;
6829 }
6830
6832 // Arithmetic operators - not in-place
6834
6835 ProjMaxPlusMat operator+(ProjMaxPlusMat const& that) const {
6836 return ProjMaxPlusMat(_underlying_mat + that._underlying_mat);
6837 }
6838
6839 ProjMaxPlusMat operator*(ProjMaxPlusMat const& that) const {
6840 return ProjMaxPlusMat(_underlying_mat * that._underlying_mat);
6841 }
6842
6844 // Iterators
6846
6847 // The following should probably be commented out because I can't
6848 // currently think how to ensure that the matrix is normalised if it's
6849 // changed this way.
6850
6851 iterator begin() noexcept {
6852 // to ensure the returned value is normalised
6853 normalize();
6854 // to ensure that the matrix is renormalised if the returned scalar is
6855 // assigned.
6856 _is_normalized = false;
6857 return _underlying_mat.begin();
6858 }
6859
6860 iterator end() noexcept {
6861 // to ensure the returned value is normalised
6862 normalize();
6863 // to ensure that the matrix is renormalised if the returned scalar is
6864 // assigned.
6865 _is_normalized = false;
6866 return _underlying_mat.end();
6867 }
6868
6869 const_iterator begin() const noexcept {
6870 normalize();
6871 return _underlying_mat.begin();
6872 }
6873
6874 const_iterator end() const noexcept {
6875 normalize();
6876 return _underlying_mat.end();
6877 }
6878
6879 const_iterator cbegin() const noexcept {
6880 normalize();
6881 return _underlying_mat.cbegin();
6882 }
6883
6884 const_iterator cend() const noexcept {
6885 normalize();
6886 return _underlying_mat.cend();
6887 }
6888
6890 // Modifiers
6892
6893 void swap(ProjMaxPlusMat& that) noexcept {
6894 std::swap(_underlying_mat, that._underlying_mat);
6895 }
6896
6897 void transpose() noexcept {
6898 _underlying_mat.transpose();
6899 }
6900
6901 void transpose_no_checks() noexcept {
6902 _underlying_mat.transpose_no_checks();
6903 }
6904
6906 // Rows
6908
6909 RowView row(size_t i) const {
6910 normalize();
6911 return _underlying_mat.row(i);
6912 }
6913
6914 template <typename C>
6915 void rows(C& x) const {
6916 normalize();
6917 return _underlying_mat.rows(x);
6918 }
6919
6921 // Friend functions
6923
6924 friend std::ostream& operator<<(std::ostream& os,
6925 ProjMaxPlusMat const& x) {
6926 x.normalize();
6927 os << detail::to_string(x._underlying_mat);
6928 return os;
6929 }
6930
6931 T const& underlying_matrix() const noexcept {
6932 normalize();
6933 return _underlying_mat;
6934 }
6935
6936 private:
6937 explicit ProjMaxPlusMat(T&& mat)
6938 : _is_normalized(false), _underlying_mat(std::move(mat)) {
6939 normalize();
6940 }
6941
6942 void normalize(bool force = false) const {
6943 if ((_is_normalized && !force)
6944 || (_underlying_mat.number_of_rows() == 0)
6945 || (_underlying_mat.number_of_cols() == 0)) {
6946 _is_normalized = true;
6947 return;
6948 }
6949 scalar_type const n = *std::max_element(_underlying_mat.cbegin(),
6950 _underlying_mat.cend());
6951 std::for_each(_underlying_mat.begin(),
6952 _underlying_mat.end(),
6953 [&n](scalar_type& s) {
6954 if (s != NEGATIVE_INFINITY) {
6955 s -= n;
6956 }
6957 });
6958 _is_normalized = true;
6959 }
6960
6961 mutable bool _is_normalized;
6962 mutable T _underlying_mat;
6963 };
6964 } // namespace detail
6965
7010
7024 template <size_t R, size_t C, typename Scalar>
7026 = detail::ProjMaxPlusMat<StaticMaxPlusMat<R, C, Scalar>>;
7027
7039 template <typename Scalar>
7041 = detail::ProjMaxPlusMat<DynamicMaxPlusMat<Scalar>>;
7042
7057 template <size_t R = 0, size_t C = R, typename Scalar = int>
7058 using ProjMaxPlusMat = std::conditional_t<R == 0 || C == 0,
7061
7062 namespace detail {
7063 template <typename T>
7064 struct IsProjMaxPlusMatHelper : std::false_type {};
7065
7066 template <size_t R, size_t C, typename Scalar>
7067 struct IsProjMaxPlusMatHelper<StaticProjMaxPlusMat<R, C, Scalar>>
7068 : std::true_type {};
7069
7070 template <typename Scalar>
7071 struct IsProjMaxPlusMatHelper<DynamicProjMaxPlusMat<Scalar>>
7072 : std::true_type {};
7073 } // namespace detail
7074
7086 template <typename T>
7087 static constexpr bool IsProjMaxPlusMat
7088 = detail::IsProjMaxPlusMatHelper<T>::value;
7089
7090 namespace matrix {
7091 // \ingroup projmaxplus_group
7092 //
7106 //! \throws LibsemigroupsException if
7107 //! `throw_if_bad_entry(x.underlying_matrix())` throws.
7108 template <typename Mat>
7109 constexpr std::enable_if_t<IsProjMaxPlusMat<Mat>>
7110 throw_if_bad_entry(Mat const& x) {
7111 throw_if_bad_entry(x.underlying_matrix());
7112 }
7113
7114 // \ingroup projmaxplus_group
7115 //
7129 //! \throws LibsemigroupsException if
7130 //! `throw_if_bad_entry(x.underlying_matrix(), val)` throws.
7131 template <typename Mat>
7132 constexpr std::enable_if_t<IsProjMaxPlusMat<Mat>>
7133 throw_if_bad_entry(Mat const& x, typename Mat::scalar_type val) {
7134 throw_if_bad_entry(x.underlying_matrix(), val);
7135 }
7136
7138 // Matrix helpers - pow
7140
7174 //! \endcode
7175 // TODO(1) pow_no_checks
7176 // TODO(2) version that changes x in-place
7177 template <typename Mat>
7178 Mat pow(Mat const& x, typename Mat::scalar_type e) {
7179 using scalar_type = typename Mat::scalar_type;
7180
7181 if constexpr (std::is_signed<scalar_type>::value) {
7182 if (e < 0) {
7184 "negative exponent, expected value >= 0, found {}", e);
7185 }
7186 }
7187
7189
7190 typename Mat::semiring_type const* sr = nullptr;
7191
7192 if constexpr (IsMatWithSemiring<Mat>) {
7193 sr = x.semiring();
7194 }
7195
7196 if (e == 0) {
7197 return x.one();
7198 }
7199
7200 auto y = Mat(x);
7201 if (e == 1) {
7202 return y;
7203 }
7204 auto z = (e % 2 == 0 ? x.one() : y);
7205
7206 Mat tmp(sr, x.number_of_rows(), x.number_of_cols());
7207 while (e > 1) {
7208 tmp.product_inplace_no_checks(y, y);
7209 std::swap(y, tmp);
7210 e /= 2;
7211 if (e % 2 == 1) {
7212 tmp.product_inplace_no_checks(z, y);
7213 std::swap(z, tmp);
7214 }
7215 }
7216 return z;
7217 }
7218
7220 // Matrix helpers - rows
7222
7239 //!
7240 //! \complexity
7241 //! \f$O(m)\f$ where \f$m\f$ is the number of rows in the matrix \p x.
7242 template <typename Mat, typename = std::enable_if_t<IsDynamicMatrix<Mat>>>
7245 x.rows(container);
7246 return container;
7247 }
7248
7267 //! \complexity
7268 //! \f$O(m)\f$ where \f$m\f$ is the number of rows in the matrix \p x.
7269 template <typename Mat, typename = std::enable_if_t<IsStaticMatrix<Mat>>>
7270 detail::StaticVector1<typename Mat::RowView, Mat::nr_rows>
7271 rows(Mat const& x) {
7272 detail::StaticVector1<typename Mat::RowView, Mat::nr_rows> container;
7273 x.rows(container);
7274 return container;
7275 }
7276
7278 // Matrix helpers - bitset_rows
7280
7281 // The main function
7310 //! \complexity
7311 //! \f$O(mn)\f$ where \f$m\f$ is the number of rows in `views` and
7312 //! and \f$n\f$ is the number of columns in any vector in `views`.
7313 template <typename Mat, size_t R, size_t C, typename Container>
7314 void bitset_rows(Container&& views,
7315 detail::StaticVector1<BitSet<C>, R>& result) {
7316 using RowView = typename Mat::RowView;
7317 using value_type = typename std::decay_t<Container>::value_type;
7318 // std::vector<bool> is used as value_type in the benchmarks
7319 static_assert(IsBMat<Mat>, "IsBMat<Mat> must be true!");
7320 static_assert(std::is_same_v<value_type, RowView>
7321 || std::is_same_v<value_type, std::vector<bool>>,
7322 "Container::value_type must equal Mat::RowView or "
7323 "std::vector<bool>!!");
7324 static_assert(R <= BitSet<1>::max_size(),
7325 "R must be at most BitSet<1>::max_size()!");
7326 static_assert(C <= BitSet<1>::max_size(),
7327 "C must be at most BitSet<1>::max_size()!");
7328 LIBSEMIGROUPS_ASSERT(views.size() <= R);
7329 LIBSEMIGROUPS_ASSERT(views.empty() || views[0].size() <= C);
7330 for (auto const& v : views) {
7331 result.emplace_back(v.cbegin(), v.cend());
7332 }
7333 }
7334
7364 //! \complexity
7365 //! \f$O(mn)\f$ where \f$m\f$ is the number of rows in \p views and
7366 //! and \f$n\f$ is the number of columns in any vector in \p views.
7367 template <typename Mat, size_t R, size_t C, typename Container>
7368 auto bitset_rows(Container&& views) {
7369 using RowView = typename Mat::RowView;
7370 using value_type = typename std::decay_t<Container>::value_type;
7371 static_assert(IsBMat<Mat>, "IsBMat<Mat> must be true!");
7372 static_assert(std::is_same_v<value_type, RowView>
7373 || std::is_same_v<value_type, std::vector<bool>>,
7374 "Container::value_type must equal Mat::RowView or "
7375 "std::vector<bool>!!");
7376 static_assert(R <= BitSet<1>::max_size(),
7377 "R must be at most BitSet<1>::max_size()!");
7378 static_assert(C <= BitSet<1>::max_size(),
7379 "C must be at most BitSet<1>::max_size()!");
7380 LIBSEMIGROUPS_ASSERT(views.size() <= R);
7381 LIBSEMIGROUPS_ASSERT(views.empty() || views[0].size() <= C);
7382 detail::StaticVector1<BitSet<C>, R> result;
7384 return result;
7385 }
7386
7387 // Helper
7413 //! \complexity
7414 //! \f$O(mn)\f$ where \f$m\f$ is the number of rows in \p x and and
7415 //! \f$n\f$ is the number of columns in \p x.
7416 template <typename Mat, size_t R, size_t C>
7417 void bitset_rows(Mat const& x,
7418 detail::StaticVector1<BitSet<C>, R>& result) {
7419 static_assert(IsBMat<Mat>, "IsBMat<Mat> must be true!");
7420 static_assert(R <= BitSet<1>::max_size(),
7421 "R must be at most BitSet<1>::max_size()!");
7422 static_assert(C <= BitSet<1>::max_size(),
7423 "C must be at most BitSet<1>::max_size()!");
7424 LIBSEMIGROUPS_ASSERT(x.number_of_cols() <= C);
7425 LIBSEMIGROUPS_ASSERT(x.number_of_rows() <= R);
7426 bitset_rows<Mat>(std::move(rows(x)), result);
7427 }
7428
7429 // Helper
7445 //! \complexity
7446 //! \f$O(mn)\f$ where \f$m\f$ is the number of rows in \p x and
7447 //! and \f$n\f$ is the number of columns in \p x.
7448 template <typename Mat>
7449 auto bitset_rows(Mat const& x) {
7450 static_assert(IsBMat<Mat>, "IsBMat<Mat> must be true!");
7451 LIBSEMIGROUPS_ASSERT(x.number_of_rows() <= BitSet<1>::max_size());
7452 LIBSEMIGROUPS_ASSERT(x.number_of_cols() <= BitSet<1>::max_size());
7453 size_t const M = detail::BitSetCapacity<Mat>::value;
7455 }
7456
7458 // Matrix helpers - bitset_row_basis
7460
7482 //! \f$c\f$ is the size of each bitset in `rows`.
7483 // This works with std::vector and StaticVector1, with value_type equal
7484 // to std::bitset and BitSet.
7485 template <typename Mat, typename Container>
7486 void bitset_row_basis(Container&& rows, std::decay_t<Container>& result) {
7487 using value_type = typename std::decay_t<Container>::value_type;
7488 static_assert(IsBMat<Mat>, "IsBMat<Mat> must be true!");
7489 static_assert(IsBitSet<value_type> || detail::IsStdBitSet<value_type>,
7490 "Container::value_type must be BitSet or std::bitset");
7491 LIBSEMIGROUPS_ASSERT(rows.size() <= BitSet<1>::max_size());
7492 LIBSEMIGROUPS_ASSERT(rows.empty()
7493 || rows[0].size() <= BitSet<1>::max_size());
7494
7495 std::sort(rows.begin(), rows.end(), detail::LessBitSet());
7496 // Remove duplicates
7497 rows.erase(std::unique(rows.begin(), rows.end()), rows.end());
7498 for (size_t i = 0; i < rows.size(); ++i) {
7499 value_type cup;
7500 cup.reset();
7501 for (size_t j = 0; j < i; ++j) {
7502 if ((rows[i] & rows[j]) == rows[j]) {
7503 cup |= rows[j];
7504 }
7505 }
7506 for (size_t j = i + 1; j < rows.size(); ++j) {
7507 if ((rows[i] & rows[j]) == rows[j]) {
7508 cup |= rows[j];
7509 }
7510 }
7511 if (cup != rows[i]) {
7512 result.push_back(std::move(rows[i]));
7513 }
7514 }
7515 }
7516
7537 //! \complexity
7538 //! \f$O(r ^ 2 c)\f$ where \f$r\f$ is the size of \p rows and
7539 //! \f$c\f$ is the size of each bitset in \p rows.
7540 template <typename Mat, typename Container>
7541 std::decay_t<Container> bitset_row_basis(Container&& rows) {
7542 using value_type = typename std::decay_t<Container>::value_type;
7543 static_assert(IsBMat<Mat>, "IsBMat<Mat> must be true!");
7544 static_assert(IsBitSet<value_type> || detail::IsStdBitSet<value_type>,
7545 "Container::value_type must be BitSet or std::bitset");
7546 LIBSEMIGROUPS_ASSERT(rows.size() <= BitSet<1>::max_size());
7547 LIBSEMIGROUPS_ASSERT(rows.empty()
7548 || rows[0].size() <= BitSet<1>::max_size());
7549 std::decay_t<Container> result;
7551 return result;
7552 }
7553
7578 //! \complexity
7579 //! \f$O(r ^ 2 c)\f$ where \f$r\f$ is the number of rows in \p x and
7580 //! \f$c\f$ is the number of columns in \p x.
7581 template <typename Mat, size_t M = detail::BitSetCapacity<Mat>::value>
7582 detail::StaticVector1<BitSet<M>, M> bitset_row_basis(Mat const& x) {
7583 static_assert(IsBMat<Mat>, "IsBMat<Mat> must be true!");
7584 LIBSEMIGROUPS_ASSERT(x.number_of_rows() <= BitSet<1>::max_size());
7585 LIBSEMIGROUPS_ASSERT(x.number_of_cols() <= BitSet<1>::max_size());
7586 detail::StaticVector1<BitSet<M>, M> result;
7588 return result;
7589 }
7590
7611 //! \complexity
7612 //! \f$O(r ^ 2 c)\f$ where \f$r\f$ is the number of rows in \p x
7613 //! and \f$c\f$ is the number of columns in \p x.
7614 template <typename Mat, typename Container>
7615 void bitset_row_basis(Mat const& x, Container& result) {
7616 using value_type = typename Container::value_type;
7617 static_assert(IsBMat<Mat>, "IsBMat<Mat> must be true!");
7618 static_assert(IsBitSet<value_type> || detail::IsStdBitSet<value_type>,
7619 "Container::value_type must be BitSet or std::bitset");
7620 LIBSEMIGROUPS_ASSERT(x.number_of_rows() <= BitSet<1>::max_size());
7621 LIBSEMIGROUPS_ASSERT(x.number_of_cols() <= BitSet<1>::max_size());
7623 }
7624
7626 // Matrix helpers - row_basis - MaxPlusTruncMat
7628
7654 //! \f$O(r ^ 2 c)\f$ where \f$r\f$ is the size of \p views and
7655 //! \f$c\f$ is the size of each row view or bit set in \p views.
7656 template <typename Mat, typename Container>
7657 std::enable_if_t<IsMaxPlusTruncMat<Mat>>
7658 row_basis(Container&& views, std::decay_t<Container>& result) {
7659 using value_type = typename std::decay_t<Container>::value_type;
7660 static_assert(std::is_same_v<value_type, typename Mat::RowView>,
7661 "Container::value_type must be Mat::RowView");
7662 using scalar_type = typename Mat::scalar_type;
7663 using Row = typename Mat::Row;
7664
7665 if (views.empty()) {
7666 return;
7667 }
7668
7669 LIBSEMIGROUPS_ASSERT(result.empty());
7670
7671 std::sort(views.begin(), views.end());
7672 Row tmp1(views[0]);
7673
7674 for (size_t r1 = 0; r1 < views.size(); ++r1) {
7675 if (r1 == 0 || views[r1] != views[r1 - 1]) {
7676 std::fill(tmp1.begin(), tmp1.end(), tmp1.scalar_zero());
7677 for (size_t r2 = 0; r2 < r1; ++r2) {
7678 scalar_type max_scalar = matrix::threshold(tmp1);
7679 for (size_t c = 0; c < tmp1.number_of_cols(); ++c) {
7680 if (views[r2][c] == tmp1.scalar_zero()) {
7681 continue;
7682 }
7683 if (views[r1][c] >= views[r2][c]) {
7684 if (views[r1][c] != matrix::threshold(tmp1)) {
7685 max_scalar
7686 = std::min(max_scalar, views[r1][c] - views[r2][c]);
7687 }
7688 } else {
7689 max_scalar = tmp1.scalar_zero();
7690 break;
7691 }
7692 }
7693 if (max_scalar != tmp1.scalar_zero()) {
7694 tmp1 += views[r2] * max_scalar;
7695 }
7696 }
7697 if (tmp1 != views[r1]) {
7698 result.push_back(views[r1]);
7699 }
7700 }
7701 }
7702 }
7703
7705 // Matrix helpers - row_basis - BMat
7707
7708 // This version of row_basis for BMat's is for used for compatibility
7709 // with the MatrixCommon framework, i.e. so that BMat's exhibit the same
7710 // interface/behaviour as other matrices.
7711 //
7712 // This version takes a container of row views of BMat, converts it to a
7713 // container of BitSets, computes the row basis using the BitSets, then
7714 // selects those row views in views that belong to the computed basis.
7715
7731 //! \exceptions
7732 //! \no_libsemigroups_except
7733 // TODO(2) complexity
7734 template <typename Mat, typename Container>
7735 std::enable_if_t<IsBMat<Mat>> row_basis(Container&& views,
7736 std::decay_t<Container>& result) {
7737 using RowView = typename Mat::RowView;
7738 using value_type = typename std::decay_t<Container>::value_type;
7739 // std::vector<bool> is used as value_type in the benchmarks
7740 static_assert(std::is_same_v<value_type, RowView>
7741 || std::is_same_v<value_type, std::vector<bool>>,
7742 "Container::value_type must equal Mat::RowView or "
7743 "std::vector<bool>!!");
7744
7745 if (views.empty()) {
7746 return;
7747 }
7748
7749 // Convert RowViews to BitSets
7750 size_t const M = detail::BitSetCapacity<Mat>::value;
7752 using bitset_type = typename decltype(br)::value_type;
7753
7754 // Map for converting bitsets back to row views
7756 LIBSEMIGROUPS_ASSERT(br.size() == views.size());
7757 for (size_t i = 0; i < br.size(); ++i) {
7758 lookup.insert({br[i], i});
7759 }
7760
7761 // Compute rowbasis using bitsets + convert back to rowviews
7762 for (auto const& bs : bitset_row_basis<Mat>(br)) {
7763 auto it = lookup.find(bs);
7764 LIBSEMIGROUPS_ASSERT(it != lookup.end());
7765 result.push_back(views[it->second]);
7766 }
7767 }
7768
7770 // Matrix helpers - row_basis - generic helpers
7772
7794 // Row basis of rowspace of matrix <x> appended to <result>
7795 template <typename Mat,
7796 typename Container,
7797 typename = std::enable_if_t<IsMatrix<Mat>>>
7798 void row_basis(Mat const& x, Container& result) {
7799 row_basis<Mat>(std::move(rows(x)), result);
7800 }
7801
7819 //! \f$O(r ^ 2 c)\f$ where \f$r\f$ is the number of rows in \p x
7820 //! and \f$c\f$ is the number of columns in \p x.
7821 // Row basis of rowspace of matrix <x>
7822 template <typename Mat, typename = std::enable_if_t<IsDynamicMatrix<Mat>>>
7825 row_basis(x, container);
7826 return container;
7827 }
7828
7846 //! \f$O(r ^ 2 c)\f$ where \f$r\f$ is the number of rows in \p x
7847 //! and \f$c\f$ is the number of columns in \p x.
7848 template <typename Mat, typename = std::enable_if_t<IsStaticMatrix<Mat>>>
7849 detail::StaticVector1<typename Mat::RowView, Mat::nr_rows>
7850 row_basis(Mat const& x) {
7851 detail::StaticVector1<typename Mat::RowView, Mat::nr_rows> container;
7852 row_basis(x, container);
7853 return container;
7854 }
7855
7874 // TODO(2) complexity
7875 template <typename Mat,
7876 typename Container,
7877 typename = std::enable_if_t<!IsMatrix<std::decay_t<Container>>>>
7878 std::decay_t<Container> row_basis(Container&& rows) {
7879 using value_type = typename std::decay_t<Container>::value_type;
7880 static_assert(IsMatrix<Mat>, "IsMatrix<Mat> must be true!");
7881 static_assert(std::is_same_v<value_type, typename Mat::RowView>,
7882 "Container::value_type must be Mat::RowView");
7883
7884 std::decay_t<Container> result;
7886 return result;
7887 }
7888
7889 template <typename T>
7890 struct RowSum {
7891 void operator()(T& res, T const& pt, T const& x) const {
7892 res = pt;
7893 res += x;
7894 }
7895 };
7896
7897 template <typename Mat,
7898 typename Container,
7899 typename = std::enable_if_t<IsMatrix<Mat>>>
7900 void row_basis_rows(Mat const& x, Container& result) {
7901 LIBSEMIGROUPS_ASSERT(result.empty());
7902 for (auto y : row_basis<Mat>(x)) {
7903 result.push_back(typename Mat::Row(y));
7904 }
7905 }
7906
7907 template <typename Mat,
7908 typename Container,
7909 typename = std::enable_if_t<IsStaticMatrix<Mat>>>
7910 detail::StaticVector1<typename Mat::Row, Mat::nr_rows>
7911 row_basis_rows(Mat const& x) {
7912 detail::StaticVector1<typename Mat::Row, Mat::nr_rows> container;
7913 row_basis_rows(x, container);
7914 return container;
7915 }
7916
7917 template <typename Mat,
7918 typename Container,
7919 typename = std::enable_if_t<IsDynamicMatrix<Mat>>>
7920 std::vector<typename Mat::Row> row_basis_rows(Mat const& x) {
7921 std::vector<typename Mat::Row> container;
7922 row_basis_rows(x, container);
7923 return container;
7924 }
7925
7926 // This modifies the argument rows by moving out those in the basis
7927 template <typename Mat,
7928 typename Container,
7929 typename = std::enable_if_t<IsStaticMatrix<Mat>>>
7930 detail::StaticVector1<typename Mat::Row, Mat::nr_rows>
7931 row_basis_rows(Container&& rows) {
7932 using value_type = typename std::decay_t<Container>::value_type;
7933 static_assert(IsMatrix<Mat>, "IsMatrix<Mat> must be true!");
7934 static_assert(std::is_same_v<value_type, typename Mat::Row>,
7935 "Container::value_type must be Mat::Row");
7936
7937 detail::StaticVector1<typename Mat::RowView, Mat::nr_rows> rvs;
7938 std::unordered_map<typename Mat::scalar_type*, size_t> lookup;
7939
7940 for (size_t i = 0; i < rows.size(); ++i) {
7941 auto rv = typename Mat::RowView(rows[i]);
7942 rvs.push_back(rv);
7943 lookup.insert({&(*rv.begin()), i});
7944 }
7945 std::decay_t<Container> result;
7946 for (auto rv : row_basis<Mat>(rvs)) {
7947 auto&& row = rows[lookup.at(&(*rv.begin()))];
7948 result.push_back(std::forward<decltype(row)>(row));
7949 }
7950 return result;
7951 }
7952
7954 // Matrix helpers - row_space_size
7956
7984 //! auto x = make<BMat<>>({{1, 0, 0}, {0, 0, 1}, {0, 1, 0}});
7985 //! matrix::row_space_size(x); // returns 7
7986 //! \endcode
7987 template <typename Mat, typename = std::enable_if_t<IsBMat<Mat>>>
7988 size_t row_space_size(Mat const& x) {
7989 size_t const M = detail::BitSetCapacity<Mat>::value;
7990 auto bitset_row_basis_ = bitset_row_basis<Mat>(
7992
7994 st.insert(bitset_row_basis_.cbegin(), bitset_row_basis_.cend());
7995 std::vector<BitSet<M>> orb(bitset_row_basis_.cbegin(),
7996 bitset_row_basis_.cend());
7997 for (size_t i = 0; i < orb.size(); ++i) {
7998 for (auto& row : bitset_row_basis_) {
7999 auto cup = orb[i];
8000 for (size_t j = 0; j < x.number_of_rows(); ++j) {
8001 cup.set(j, cup[j] || row[j]);
8002 }
8003 if (st.insert(cup).second) {
8004 orb.push_back(std::move(cup));
8005 }
8006 }
8007 }
8008 return orb.size();
8009 }
8010
8011 } // namespace matrix
8012
8029 //! \no_libsemigroups_except
8030 //!
8031 //! \warning This function does not detect overflows of `Mat::scalar_type`.
8032 template <typename Mat>
8033 auto operator+(typename Mat::scalar_type a, Mat const& x)
8034 -> std::enable_if_t<IsMatrix<Mat>, Mat> {
8035 return x + a;
8036 }
8037
8054 //! \no_libsemigroups_except
8055 //!
8056 //! \warning This function does not detect overflows of `Mat::scalar_type`.
8057 template <typename Mat>
8058 auto operator*(typename Mat::scalar_type a, Mat const& x)
8059 -> std::enable_if_t<IsMatrix<Mat>, Mat> {
8060 return x * a;
8061 }
8062
8073
8098 //! \f$n\f$ is the number of columns of the matrix.
8099 template <typename Mat,
8100 typename
8101 = std::enable_if_t<IsMatrix<Mat> && !IsMatWithSemiring<Mat>>>
8103 detail::throw_if_any_row_wrong_size(rows);
8104 detail::throw_if_bad_dim<Mat>(rows);
8105 Mat m(rows);
8107 return m;
8108 }
8109
8134 //! \f$n\f$ is the number of columns of the matrix.
8135 template <typename Mat,
8136 typename
8137 = std::enable_if_t<IsMatrix<Mat> && !IsMatWithSemiring<Mat>>>
8139 rows) {
8141 }
8142
8168 //! parameter \c R is \c 1.
8169 template <typename Mat,
8170 typename
8171 = std::enable_if_t<IsMatrix<Mat> && !IsMatWithSemiring<Mat>>>
8173 detail::throw_if_bad_row_dim<Mat>(row);
8174 Mat m(row);
8176 return m;
8177 }
8178 // TODO(1) vector version of above
8179
8211 template <typename Mat,
8212 typename Semiring,
8213 typename = std::enable_if_t<IsMatrix<Mat>>>
8214 // TODO(1) pass Semiring by reference, this is hard mostly due to the way
8215 // the tests are written, which is not optimal.
8216 Mat make(Semiring const* semiring,
8219 detail::throw_if_any_row_wrong_size(rows);
8220 detail::throw_if_bad_dim<Mat>(rows);
8221 Mat m(semiring, rows);
8223 return m;
8224 }
8225
8256 //! \f$n\f$ is the number of columns of the matrix.
8257 template <typename Mat,
8258 typename Semiring,
8259 typename = std::enable_if_t<IsMatrix<Mat>>>
8260 Mat make(Semiring const* semiring,
8262 detail::throw_if_any_row_wrong_size(rows);
8263 detail::throw_if_bad_dim<Mat>(rows);
8264 Mat m(semiring, rows);
8266 return m;
8267 }
8268
8290 //! \f$O(n)\f$ where \f$n\f$ is the number of columns of the matrix.
8291 template <typename Mat,
8292 typename Semiring,
8293 typename = std::enable_if_t<IsMatrix<Mat>>>
8294 Mat make(Semiring const* semiring,
8296 detail::throw_if_bad_row_dim<Mat>(row);
8297 Mat m(semiring, row);
8299 return m;
8300 }
8301
8327 //! \f$O(mn)\f$ where \f$m\f$ is the number of rows and \f$n\f$ is the
8328 //! number of columns of the matrix.
8329 template <size_t R, size_t C, typename Scalar>
8334 }
8335
8337 // Printing etc...
8339
8349 //!
8350 //! \exceptions
8351 //! \no_libsemigroups_except
8352 template <typename S, typename T>
8354 detail::RowViewCommon<S, T> const& x) {
8355 os << "{";
8356 for (auto it = x.cbegin(); it != x.cend(); ++it) {
8357 os << *it;
8358 if (it != x.cend() - 1) {
8359 os << ", ";
8360 }
8361 }
8362 os << "}";
8363 return os;
8364 }
8365
8379 //!
8380 //! \exceptions
8381 //! \no_libsemigroups_except
8382 template <typename Mat>
8383 auto operator<<(std::ostringstream& os, Mat const& x)
8384 -> std::enable_if_t<IsMatrix<Mat>, std::ostringstream&> {
8385 size_t n = 0;
8386 if (x.number_of_rows() != 1) {
8387 os << "{";
8388 }
8389 for (auto&& r : matrix::rows(x)) {
8390 os << r;
8391 if (n != x.number_of_rows() - 1) {
8392 os << ", ";
8393 }
8394 n++;
8395 }
8396 if (x.number_of_rows() != 1) {
8397 os << "}";
8398 }
8399 return os;
8400 }
8401
8415 //! (default: \c 72).
8416 //!
8417 //! \throws LibsemigroupsException if \p braces does not have size \c 2.
8418 template <typename Mat>
8419 auto to_human_readable_repr(Mat const& x,
8420 std::string const& prefix,
8421 std::string const& short_name = "",
8422 std::string const& braces = "{}",
8423 size_t max_width = 72)
8424 -> std::enable_if_t<IsMatrix<Mat>, std::string> {
8425 if (braces.size() != 2) {
8427 "the 4th argument (braces) must have size 2, found {}",
8428 braces.size());
8429 }
8430
8431 size_t const R = x.number_of_rows();
8432 size_t const C = x.number_of_cols();
8433
8434 std::vector<size_t> max_col_widths(C, 0);
8435 std::vector<size_t> row_widths(C, prefix.size() + 1);
8436 for (size_t r = 0; r < R; ++r) {
8437 for (size_t c = 0; c < C; ++c) {
8438 size_t width
8439 = detail::unicode_string_length(detail::entry_repr(x(r, c)));
8440 row_widths[r] += width;
8441 if (width > max_col_widths[c]) {
8442 max_col_widths[c] = width;
8443 }
8444 }
8445 }
8446 auto col_width
8447 = *std::max_element(max_col_widths.begin(), max_col_widths.end());
8448 // The total width if we pad the entries according to the widest column.
8449 auto const total_width = col_width * C + prefix.size() + 1;
8450 if (total_width > max_width) {
8451 // Padding according to the widest column is too wide!
8452 if (*std::max_element(row_widths.begin(), row_widths.end()) > max_width) {
8453 // If the widest row is too wide, then use the short name
8454 return fmt::format(
8455 "<{}x{} {}>", x.number_of_rows(), x.number_of_cols(), short_name);
8456 }
8457 // If the widest row is not too wide, then just don't pad the entries
8458 col_width = 0;
8459 }
8460
8461 std::string result = fmt::format("{}", prefix);
8462 std::string rindent;
8463 auto const lbrace = braces[0], rbrace = braces[1];
8464 if (R != 0 && C != 0) {
8465 result += lbrace;
8466 for (size_t r = 0; r < R; ++r) {
8467 result += fmt::format("{}{}", rindent, lbrace);
8468 rindent = std::string(prefix.size() + 1, ' ');
8469 std::string csep = "";
8470 for (size_t c = 0; c < C; ++c) {
8471 result += fmt::format(
8472 "{}{:>{}}", csep, detail::entry_repr(x(r, c)), col_width);
8473 csep = ", ";
8474 }
8475 result += fmt::format("{}", rbrace);
8476 if (r != R - 1) {
8477 result += ",\n";
8478 }
8479 }
8480 result += rbrace;
8481 }
8482 result += ")";
8483 return result;
8484 }
8485
8487 // Adapters
8489
8504
8512 //! satisfying \ref IsMatrix<Mat>.
8513 //!
8514 //! \tparam Mat the type of matrices.
8515 template <typename Mat>
8516 struct Complexity<Mat, std::enable_if_t<IsMatrix<Mat>>> {
8526 //! \noexcept
8527 //!
8528 //! \complexity
8529 //! Constant.
8530 constexpr size_t operator()(Mat const& x) const noexcept {
8531 return x.number_of_rows() * x.number_of_rows() * x.number_of_rows();
8532 }
8533 };
8534
8542 //! \ref IsMatrix<Mat>.
8543 //!
8544 //! \tparam Mat the type of matrices.
8545 template <typename Mat>
8546 struct Degree<Mat, std::enable_if_t<IsMatrix<Mat>>> {
8555 //! \noexcept
8556 //!
8557 //! \complexity
8558 //! Constant.
8559 constexpr size_t operator()(Mat const& x) const noexcept {
8560 return x.number_of_rows();
8561 }
8562 };
8563
8571 //! \ref IsMatrix<Mat>.
8572 //!
8573 //! \tparam Mat the type of matrices.
8574 template <typename Mat>
8575 struct Hash<Mat, std::enable_if_t<IsMatrix<Mat>>> {
8584 //! \no_libsemigroups_except
8585 //!
8586 //! \complexity
8587 //! Constant.
8588 constexpr size_t operator()(Mat const& x) const {
8589 return x.hash_value();
8590 }
8591 };
8592
8605 //! It is not possible to increase the degree of any of the types
8606 //! satisfying \ref IsMatrix, and as such the call operator of this type
8607 //! does nothing.
8608 template <typename Mat>
8609 struct IncreaseDegree<Mat, std::enable_if_t<IsMatrix<Mat>>> {
8613 constexpr void operator()(Mat&, size_t) const noexcept {
8614 // static_assert(false, "Cannot increase degree for Matrix");
8615 LIBSEMIGROUPS_ASSERT(false);
8616 }
8617 };
8618
8629 //!
8630 //! \note There is no `operator()(size_t)` meaning it isn't possible to use
8631 //! elements of type \p Mat with \ref SchreierSims.
8632 template <typename Mat>
8633 struct One<Mat, std::enable_if_t<IsMatrix<Mat>>> {
8643 //!
8644 //! \complexity
8645 //! \f$O(m ^ 2)\f$ where \f$m\f$ is the number of rows of the
8646 //! matrix \p x.
8647 inline Mat operator()(Mat const& x) const {
8648 return x.one();
8649 }
8650 };
8651
8659 //! \ref IsMatrix<Mat>.
8660 //!
8661 //! \tparam Mat the type of matrices.
8662 template <typename Mat>
8663 struct Product<Mat, std::enable_if_t<IsMatrix<Mat>>> {
8679 //!
8680 //! \warning
8681 //! This function only works for square matrices.
8682 inline void
8683 operator()(Mat& xy, Mat const& x, Mat const& y, size_t = 0) const {
8684 xy.product_inplace_no_checks(x, y);
8685 }
8686 };
8687} // namespace libsemigroups
8688
8689namespace std {
8690 template <size_t N,
8691 typename Mat,
8692 std::enable_if_t<libsemigroups::IsMatrix<Mat>>>
8693 inline void swap(Mat& x, Mat& y) noexcept {
8694 x.swap(y);
8695 }
8696} // namespace std
8697
8698#endif // LIBSEMIGROUPS_MATRIX_HPP_
T at(T... args)
DynamicMatrix(std::initializer_list< scalar_type > const &c)
Construct a vector from a std::initializer_list.
Definition matrix.hpp:2892
DynamicMatrix(std::initializer_list< std::initializer_list< scalar_type > > const &m)
Construct a matrix from std::initializer_list of std::initializer_list of scalars.
Definition matrix.hpp:2915
DynamicMatrix & operator=(DynamicMatrix &&)=default
Default move assignment operator.
scalar_reference at(size_t r, size_t c)
Returns a reference to the specified entry of the matrix.
ProdOp Prod
Alias for the template parameter ProdOp.
Definition matrix.hpp:2811
void product_inplace_no_checks(DynamicMatrix const &x, DynamicMatrix const &y)
Multiplies x and y and stores the result in this.
DynamicMatrix & operator=(DynamicMatrix const &)=default
Default copy assignment operator.
DynamicMatrix Row
The type of a row of a DynamicMatrix.
Definition matrix.hpp:2802
PlusOp Plus
Alias for the template parameter PlusOp.
Definition matrix.hpp:2808
void rows(T &x) const
Add row views for every row in the matrix to a container.
scalar_type scalar_one() const noexcept
Returns the multiplicative identity of the underlying semiring.
typename MatrixCommon::scalar_const_reference scalar_const_reference
The type of const references to the entries in the matrix.
Definition matrix.hpp:2798
void swap(DynamicMatrix &that) noexcept
Swaps the contents of *this with the contents of that.
Definition matrix.hpp:3174
const_iterator cend() noexcept
Returns a const iterator pointing one beyond the last entry in the matrix.
static DynamicMatrix one(size_t n)
Construct the identity matrix.
Definition matrix.hpp:2996
DynamicMatrix(size_t r, size_t c)
Construct a matrix with given dimensions.
Definition matrix.hpp:2869
ZeroOp Zero
Alias for the template parameter ZeroOp.
Definition matrix.hpp:2814
scalar_type scalar_zero() const noexcept
Returns the additive identity of the underlying semiring.
semiring_type const * semiring() const noexcept
Returns the underlying semiring.
OneOp One
Alias for the template parameter OneOp.
Definition matrix.hpp:2817
void semiring_type
Alias for the semiring type (void).
Definition matrix.hpp:2824
RowView row(size_t i) const
Returns a view into a row.
DynamicMatrix(RowView const &rv)
Construct a row from a row view.
Definition matrix.hpp:2949
iterator begin() noexcept
Returns an iterator pointing at the first entry.
size_t number_of_rows() const noexcept
Returns the number of rows of the matrix.
DynamicRowView< PlusOp, ProdOp, ZeroOp, OneOp, Scalar > RowView
The type of a row view into a DynamicMatrix.
Definition matrix.hpp:2805
RowView row_no_checks(size_t i) const
Returns a view into a row.
DynamicMatrix(std::vector< std::vector< scalar_type > > const &m)
Construct a matrix from std::vector of std::vector of scalars.
Definition matrix.hpp:2934
typename MatrixCommon::scalar_reference scalar_reference
The type of references to the entries in the matrix.
Definition matrix.hpp:2793
scalar_reference at(size_t r, size_t c) const
Returns a const reference to the specified entry of the matrix.
DynamicMatrix(DynamicMatrix const &)=default
Default copy constructor.
size_t hash_value() const
Return a hash value of a matrix.
const_iterator cbegin() noexcept
Returns a const iterator pointing at the first entry.
DynamicMatrix(DynamicMatrix &&)=default
Default move constructor.
typename MatrixCommon::scalar_type scalar_type
The type of the entries in the matrix.
Definition matrix.hpp:2790
size_t number_of_cols() const noexcept
Returns the number of columns of the matrix.
std::pair< scalar_type, scalar_type > coords(const_iterator it) const
Get the coordinates of an iterator.
iterator end() noexcept
Returns an iterator pointing one beyond the last entry in the matrix.
DynamicMatrix(Semiring const *sr, std::initializer_list< std::initializer_list< scalar_type > > const &rws)
Construct a matrix over a given semiring (std::initializer_list of std::initializer_list).
Definition matrix.hpp:3313
DynamicMatrix & operator=(DynamicMatrix &&)=default
Default move assignment operator.
static DynamicMatrix one(Semiring const *semiring, size_t n)
Construct the identity matrix.
Definition matrix.hpp:3390
scalar_reference at(size_t r, size_t c)
Returns a reference to the specified entry of the matrix.
DynamicMatrix(Semiring const *sr, std::vector< std::vector< scalar_type > > const &rws)
Construct a matrix over a given semiring (std::vector of std::vector).
Definition matrix.hpp:3336
DynamicMatrix(Semiring const *sr, size_t r, size_t c)
Construct a matrix over a given semiring with given dimensions.
Definition matrix.hpp:3293
void product_inplace_no_checks(DynamicMatrix const &x, DynamicMatrix const &y)
Multiplies x and y and stores the result in this.
DynamicMatrix & operator=(DynamicMatrix const &)=default
Default copy assignment operator.
DynamicMatrix Row
Alias for the type of the rows of a DynamicMatrix.
Definition matrix.hpp:3250
void rows(T &x) const
Add row views for every row in the matrix to a container.
scalar_type scalar_one() const noexcept
Returns the multiplicative identity of the underlying semiring.
typename MatrixCommon::scalar_const_reference scalar_const_reference
Alias for const references to the template parameter Scalar.
Definition matrix.hpp:3246
void swap(DynamicMatrix &that) noexcept
Swaps the contents of *this with the contents of that.
Definition matrix.hpp:3570
const_iterator cend() noexcept
Returns a const iterator pointing one beyond the last entry in the matrix.
scalar_type scalar_zero() const noexcept
Returns the additive identity of the underlying semiring.
void transpose_no_checks()
Transpose a matrix in-place.
semiring_type const * semiring() const noexcept
Returns the underlying semiring.
RowView row(size_t i) const
Returns a view into a row.
DynamicMatrix(RowView const &rv)
Construct a row over a given semiring (RowView).
Definition matrix.hpp:3370
iterator begin() noexcept
Returns an iterator pointing at the first entry.
size_t number_of_rows() const noexcept
Returns the number of rows of the matrix.
DynamicMatrix(Semiring const *sr, std::initializer_list< scalar_type > const &rw)
Construct a row over a given semiring (std::initializer_list).
Definition matrix.hpp:3355
RowView row_no_checks(size_t i) const
Returns a view into a row.
typename MatrixCommon::scalar_reference scalar_reference
Alias for references to the template parameter Scalar.
Definition matrix.hpp:3241
scalar_reference at(size_t r, size_t c) const
Returns a const reference to the specified entry of the matrix.
DynamicMatrix(DynamicMatrix const &)=default
Default copy constructor.
size_t hash_value() const
Return a hash value of a matrix.
const_iterator cbegin() noexcept
Returns a const iterator pointing at the first entry.
DynamicMatrix(DynamicMatrix &&)=default
Default move constructor.
typename MatrixCommon::scalar_type scalar_type
Alias for the template parameter Scalar.
Definition matrix.hpp:3238
DynamicRowView< Semiring, Scalar > RowView
Alias for the type of row views of a DynamicMatrix.
Definition matrix.hpp:3253
Semiring semiring_type
Alias for the template parameter Semiring.
Definition matrix.hpp:3258
size_t number_of_cols() const noexcept
Returns the number of columns of the matrix.
void transpose()
Transpose a matrix in-place.
std::pair< scalar_type, scalar_type > coords(const_iterator it) const
Get the coordinates of an iterator.
iterator end() noexcept
Returns an iterator pointing one beyond the last entry in the matrix.
DynamicRowView & operator=(DynamicRowView &&)=default
Default move assignment operator.
size_t size() const noexcept
Returns the size of the row.
DynamicRowView & operator=(DynamicRowView const &)=default
Default copy assignment operator.
DynamicRowView(DynamicRowView const &)=default
Default copy constructor.
DynamicRowView(DynamicRowView &&)=default
Default move constructor.
DynamicRowView(Row const &r)
Construct a row view from a Row.
Definition matrix.hpp:1571
typename RowViewCommon::iterator iterator
Alias for const iterators pointing at entries of a matrix.
Definition matrix.hpp:1535
typename RowViewCommon::scalar_const_reference scalar_const_reference
Alias for const references to the template parameter Scalar.
Definition matrix.hpp:1546
typename RowViewCommon::scalar_reference scalar_reference
Alias for references to the template parameter Scalar.
Definition matrix.hpp:1541
iterator begin() noexcept
Returns a iterator pointing at the first entry.
iterator cend()
Returns a const iterator pointing one beyond the last entry of the row.
typename matrix_type::Row Row
Alias for the type of a row in the underlying matrix.
Definition matrix.hpp:1553
typename RowViewCommon::const_iterator const_iterator
Alias for const iterators pointing at entries of a matrix.
Definition matrix.hpp:1532
const_iterator cbegin() const noexcept
Returns a const iterator pointing at the first entry.
iterator end()
Returns a iterator pointing one beyond the last entry of the row.
Scalar scalar_type
Alias for the template parameter Scalar.
Definition matrix.hpp:1538
typename RowViewCommon::matrix_type matrix_type
Alias for the type of the underlying matrix.
Definition matrix.hpp:1550
size_t size() const noexcept
Returns the size of the row.
DynamicRowView & operator=(DynamicRowView const &)=default
Default copy assignment operator.
typename RowViewCommon::iterator iterator
Alias for const iterators pointing at entries of a matrix.
Definition matrix.hpp:1689
typename RowViewCommon::scalar_const_reference scalar_const_reference
Alias for const references to the template parameter Scalar.
Definition matrix.hpp:1700
typename RowViewCommon::scalar_reference scalar_reference
Alias for references to the template parameter Scalar.
Definition matrix.hpp:1695
iterator begin() noexcept
Returns a iterator pointing at the first entry.
iterator cend()
Returns a const iterator pointing one beyond the last entry of the row.
typename matrix_type::Row Row
Alias for the type of a row in the underlying matrix.
Definition matrix.hpp:1707
typename RowViewCommon::const_iterator const_iterator
Alias for const iterators pointing at entries of a matrix.
Definition matrix.hpp:1686
const_iterator cbegin() const noexcept
Returns a const iterator pointing at the first entry.
iterator end()
Returns a iterator pointing one beyond the last entry of the row.
Scalar scalar_type
Alias for the template parameter Scalar.
Definition matrix.hpp:1692
typename RowViewCommon::matrix_type matrix_type
Alias for the type of the underlying matrix.
Definition matrix.hpp:1704
DynamicRowView()=default
Default constructor.
Class representing a truncated max-plus semiring.
Definition matrix.hpp:5137
static constexpr Scalar scalar_zero() noexcept
Get the additive identity.
Definition matrix.hpp:5211
Scalar plus_no_checks(Scalar x, Scalar y) const noexcept
Addition in a truncated max-plus semiring.
Definition matrix.hpp:5274
Scalar product_no_checks(Scalar x, Scalar y) const noexcept
Multiplication in a truncated max-plus semiring.
Definition matrix.hpp:5239
MaxPlusTruncSemiring()=delete
Deleted default constructor.
Scalar threshold() const noexcept
Get the threshold.
Definition matrix.hpp:5299
static constexpr Scalar scalar_one() noexcept
Get the multiplicative identity.
Definition matrix.hpp:5197
Class representing a truncated min-plus semiring.
Definition matrix.hpp:5614
static constexpr Scalar scalar_zero() noexcept
Get the additive identity.
Definition matrix.hpp:5687
Scalar plus_no_checks(Scalar x, Scalar y) const noexcept
Addition in a truncated min-plus semiring.
Definition matrix.hpp:5750
Scalar product_no_checks(Scalar x, Scalar y) const noexcept
Multiplication in a truncated min-plus semiring.
Definition matrix.hpp:5715
Scalar threshold() const noexcept
Get the threshold.
Definition matrix.hpp:5775
static constexpr Scalar scalar_one() noexcept
Get the multiplicative identity.
Definition matrix.hpp:5672
MinPlusTruncSemiring()=delete
Deleted default constructor.
NTPSemiring & operator=(NTPSemiring const &)=default
Default copy assignment operator.
static constexpr Scalar scalar_zero() noexcept
Get the additive identity.
Definition matrix.hpp:6238
Scalar plus_no_checks(Scalar x, Scalar y) const noexcept
Addition in an ntp semiring.
Definition matrix.hpp:6296
NTPSemiring()=delete
Deleted default constructor.
Scalar product_no_checks(Scalar x, Scalar y) const noexcept
Multiplication in an ntp semiring.
Definition matrix.hpp:6266
Scalar period() const noexcept
Get the period.
Definition matrix.hpp:6330
Scalar threshold() const noexcept
Get the threshold.
Definition matrix.hpp:6314
static constexpr Scalar scalar_one() noexcept
Get the multiplicative identity.
Definition matrix.hpp:6222
Static matrix class.
Definition matrix.hpp:1864
typename MatrixCommon::iterator iterator
Definition matrix.hpp:1911
StaticMatrix(std::initializer_list< scalar_type > const &c)
Construct a row (from std::initializer_list).
Definition matrix.hpp:1939
typename MatrixCommon::const_iterator const_iterator
Definition matrix.hpp:1914
scalar_const_reference at(size_t r, size_t c) const
Returns a const reference to the specified entry of the matrix.
scalar_reference at(size_t r, size_t c)
Returns a reference to the specified entry of the matrix.
StaticMatrix(StaticMatrix &&)=default
Default move constructor.
typename MatrixCommon::scalar_const_reference scalar_const_reference
Alias for const references to the template parameter Scalar.
Definition matrix.hpp:1889
scalar_reference operator()(size_t r, size_t c)
Returns a reference to the specified entry of the matrix.
void product_inplace_no_checks(StaticMatrix const &x, StaticMatrix const &y)
StaticMatrix(std::initializer_list< std::initializer_list< scalar_type > > const &m)
Construct a matrix.
Definition matrix.hpp:1966
StaticRowView< PlusOp, ProdOp, ZeroOp, OneOp, C, Scalar > RowView
Definition matrix.hpp:1896
static StaticMatrix one() const
Returns an identity matrix.
StaticMatrix(StaticMatrix const &)=default
Default copy constructor.
iterator begin() noexcept
Returns an iterator pointing at the first entry.
StaticMatrix(RowView const &rv)
Construct a row from a row view.
Definition matrix.hpp:2002
size_t number_of_rows() const noexcept
Returns the number of rows of the matrix.
StaticMatrix(std::vector< std::vector< scalar_type > > const &m)
Construct a matrix.
Definition matrix.hpp:1982
StaticMatrix< PlusOp, ProdOp, ZeroOp, OneOp, 1, C, Scalar > Row
Alias for the type of the rows of a StaticMatrix.
Definition matrix.hpp:1893
StaticMatrix()=default
Default constructor.
typename MatrixCommon::scalar_reference scalar_reference
Alias for references to the template parameter Scalar.
Definition matrix.hpp:1884
typename MatrixCommon::scalar_type scalar_type
Alias for the template parameter Scalar.
Definition matrix.hpp:1881
scalar_const_reference operator()(size_t r, size_t c) const
Returns a const reference to the specified entry of the matrix.
StaticMatrix & operator=(StaticMatrix &&)=default
Default move assignment operator.
size_t number_of_cols() const noexcept
Returns the number of columns of the matrix.
StaticMatrix & operator=(StaticMatrix const &)=default
Default copy assignment operator.
std::pair< scalar_type, scalar_type > coords(const_iterator it) const
Class for views into a row of a matrix over a semiring.
Definition matrix.hpp:1079
typename RowViewCommon::iterator iterator
Alias for const iterators pointing at entries of a matrix.
Definition matrix.hpp:1095
typename RowViewCommon::scalar_const_reference scalar_const_reference
Alias for const references to the template parameter Scalar.
Definition matrix.hpp:1106
StaticRowView & operator=(StaticRowView &&)=default
Default move assignment operator.
typename RowViewCommon::scalar_reference scalar_reference
Alias for references to the template parameter Scalar.
Definition matrix.hpp:1101
iterator begin() noexcept
Returns a iterator pointing at the first entry.
iterator cend()
Returns a const iterator pointing one beyond the last entry of the row.
StaticRowView()=default
Default constructor.
typename matrix_type::Row Row
Alias for the type of a row in the underlying matrix.
Definition matrix.hpp:1113
typename RowViewCommon::const_iterator const_iterator
Alias for const iterators pointing at entries of a matrix.
Definition matrix.hpp:1092
const_iterator cbegin() const noexcept
Returns a const iterator pointing at the first entry.
iterator end()
Returns a iterator pointing one beyond the last entry of the row.
Scalar scalar_type
Alias for the template parameter Scalar.
Definition matrix.hpp:1098
StaticRowView(StaticRowView &&)=default
Default move constructor.
typename RowViewCommon::matrix_type matrix_type
Alias for the type of the underlying matrix.
Definition matrix.hpp:1110
StaticRowView(StaticRowView const &)=default
Default copy constructor.
static constexpr size_t size() const noexcept
Returns the size of the row.
StaticRowView & operator=(StaticRowView const &)=default
Default copy assignment operator.
StaticRowView(Row const &r)
Construct a row view from a Row.
T copy(T... args)
T distance(T... args)
T equal(T... args)
T fill(T... args)
T find_if_not(T... args)
T for_each(T... args)
T forward(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.
Bipartition operator*(Bipartition const &x, Bipartition const &y)
Multiply two bipartitions.
std::ostringstream & operator<<(std::ostringstream &os, BMat8 const &x)
Insertion operator.
StaticMatrix< BooleanPlus, BooleanProd, BooleanZero, BooleanOne, R, C, int > StaticBMat
Alias for static boolean matrices.
Definition matrix.hpp:3970
static constexpr bool IsBMat
Helper to check if a type is BMat.
Definition matrix.hpp:4028
DynamicMatrix< BooleanPlus, BooleanProd, BooleanZero, BooleanOne, int > DynamicBMat
Alias for dynamic boolean matrices.
Definition matrix.hpp:3956
std::enable_if_t< IsBMat< Mat > > throw_if_bad_entry(Mat const &m)
Check the entries in a boolean matrix are valid.
Definition matrix.hpp:4073
std::conditional_t< R==0||C==0, DynamicBMat, StaticBMat< R, C > > BMat
Alias template for boolean matrices.
Definition matrix.hpp:3993
NegativeInfinity const NEGATIVE_INFINITY
Value for negative infinity.
Undefined const UNDEFINED
Value for something undefined.
PositiveInfinity const POSITIVE_INFINITY
Value for positive infinity.
#define LIBSEMIGROUPS_EXCEPTION(...)
Throw a LibsemigroupsException.
Definition exception.hpp:99
std::conditional_t< R==0||C==0, DynamicIntMat< Scalar >, StaticIntMat< R, C, Scalar > > IntMat
Alias template for integer matrices.
Definition matrix.hpp:4317
DynamicMatrix< IntegerPlus< Scalar >, IntegerProd< Scalar >, IntegerZero< Scalar >, IntegerOne< Scalar >, Scalar > DynamicIntMat
Alias for dynamic integer matrices.
Definition matrix.hpp:4269
StaticMatrix< IntegerPlus< Scalar >, IntegerProd< Scalar >, IntegerZero< Scalar >, IntegerOne< Scalar >, R, C, Scalar > StaticIntMat
Alias for static integer matrices.
Definition matrix.hpp:4292
enable_if_is_same< Return, Blocks > make(Container const &cont)
Check the arguments, construct a Blocks object, and check it.
Definition bipart.hpp:856
static constexpr bool IsMaxPlusMat
Helper variable template.
Definition matrix.hpp:4643
constexpr bool IsStaticMatrix
Helper variable template.
Definition matrix.hpp:3648
constexpr bool IsDynamicMatrix
Helper variable template.
Definition matrix.hpp:3661
static constexpr bool IsIntMat
Helper variable template.
Definition matrix.hpp:4342
auto operator+(typename Mat::scalar_type a, Mat const &x) -> std::enable_if_t< IsMatrix< Mat >, Mat >
Add a scalar to a matrix.
Definition matrix.hpp:8029
static constexpr bool IsMatWithSemiring
Helper variable template.
Definition matrix.hpp:3675
static constexpr bool IsMinPlusMat
Helper variable template.
Definition matrix.hpp:4951
constexpr bool IsMatrix
Helper variable template.
Definition matrix.hpp:168
StaticMatrix< MaxPlusPlus< Scalar >, MaxPlusProd< Scalar >, MaxPlusZero< Scalar >, IntegerZero< Scalar >, R, C, Scalar > StaticMaxPlusMat
Alias for static max-plus matrices.
Definition matrix.hpp:4592
DynamicMatrix< MaxPlusPlus< Scalar >, MaxPlusProd< Scalar >, MaxPlusZero< Scalar >, IntegerZero< Scalar >, Scalar > DynamicMaxPlusMat
Alias for dynamic max-plus matrices.
Definition matrix.hpp:4573
std::conditional_t< R==0||C==0, DynamicMaxPlusMat< Scalar >, StaticMaxPlusMat< R, C, Scalar > > MaxPlusMat
Alias template for max-plus matrices.
Definition matrix.hpp:4616
DynamicMatrix< MaxPlusPlus< Scalar >, MaxPlusTruncProd< T, Scalar >, MaxPlusZero< Scalar >, IntegerZero< Scalar >, Scalar > DynamicMaxPlusTruncMat
Alias for dynamic truncated max-plus matrices.
Definition matrix.hpp:5320
std::conditional_t< R==0||C==0, std::conditional_t< T==0, DynamicMatrix< MaxPlusTruncSemiring< Scalar >, Scalar >, DynamicMaxPlusTruncMat< T, Scalar > >, StaticMaxPlusTruncMat< T, R, C, Scalar > > MaxPlusTruncMat
Alias template for truncated max-plus matrices.
Definition matrix.hpp:5366
StaticMatrix< MaxPlusPlus< Scalar >, MaxPlusTruncProd< T, Scalar >, MaxPlusZero< Scalar >, IntegerZero< Scalar >, R, C, Scalar > StaticMaxPlusTruncMat
Alias for static truncated max-plus matrices.
Definition matrix.hpp:5340
static constexpr bool IsMaxPlusTruncMat
Helper to check if a type is MaxPlusTruncMat.
Definition matrix.hpp:5409
DynamicMatrix< MinPlusPlus< Scalar >, MinPlusProd< Scalar >, MinPlusZero< Scalar >, IntegerZero< Scalar >, Scalar > DynamicMinPlusMat
Alias for dynamic min-plus matrices.
Definition matrix.hpp:4881
StaticMatrix< MinPlusPlus< Scalar >, MinPlusProd< Scalar >, MinPlusZero< Scalar >, IntegerZero< Scalar >, R, C, Scalar > StaticMinPlusMat
Alias for static min-plus matrices.
Definition matrix.hpp:4900
std::conditional_t< R==0||C==0, DynamicMinPlusMat< Scalar >, StaticMinPlusMat< R, C, Scalar > > MinPlusMat
Alias template for min-plus matrices.
Definition matrix.hpp:4924
DynamicMatrix< MinPlusPlus< Scalar >, MinPlusTruncProd< T, Scalar >, MinPlusZero< Scalar >, IntegerZero< Scalar >, Scalar > DynamicMinPlusTruncMat
Alias for dynamic truncated min-plus matrices.
Definition matrix.hpp:5796
StaticMatrix< MinPlusPlus< Scalar >, MinPlusTruncProd< T, Scalar >, MinPlusZero< Scalar >, IntegerZero< Scalar >, R, C, Scalar > StaticMinPlusTruncMat
Alias for static truncated min-plus matrices.
Definition matrix.hpp:5816
static constexpr bool IsMinPlusTruncMat
Helper to check if a type is MinPlusTruncMat.
Definition matrix.hpp:5886
std::conditional_t< R==0||C==0, std::conditional_t< T==0, DynamicMatrix< MinPlusTruncSemiring< Scalar >, Scalar >, DynamicMinPlusTruncMat< T, Scalar > >, StaticMinPlusTruncMat< T, R, C, Scalar > > MinPlusTruncMat
Alias template for truncated min-plus matrices.
Definition matrix.hpp:5843
DynamicMatrix< NTPSemiring< Scalar >, Scalar > DynamicNTPMatWithSemiring
Alias for ntp matrices with dynamic threshold and period.
Definition matrix.hpp:6350
DynamicMatrix< NTPPlus< T, P, Scalar >, NTPProd< T, P, Scalar >, IntegerZero< Scalar >, IntegerOne< Scalar >, Scalar > DynamicNTPMatWithoutSemiring
Alias for ntp matrices with static threshold and period.
Definition matrix.hpp:6366
static constexpr bool IsNTPMat
Helper to check if a type is NTPMat.
Definition matrix.hpp:6470
std::conditional_t< R==0||C==0, std::conditional_t< T==0 &&P==0, DynamicNTPMatWithSemiring< Scalar >, DynamicNTPMatWithoutSemiring< T, P, Scalar > >, StaticNTPMat< T, P, R, C, Scalar > > NTPMat
Alias template for ntp matrices.
Definition matrix.hpp:6427
StaticMatrix< NTPPlus< T, P, Scalar >, NTPProd< T, P, Scalar >, IntegerZero< Scalar >, IntegerOne< Scalar >, R, C, Scalar > StaticNTPMat
Alias for ntp matrices with static threshold and period, and dimensions.
Definition matrix.hpp:6392
std::conditional_t< R==0||C==0, DynamicProjMaxPlusMat< Scalar >, StaticProjMaxPlusMat< R, C, Scalar > > ProjMaxPlusMat
Alias template for projective max-plus matrices.
Definition matrix.hpp:7054
detail::ProjMaxPlusMat< DynamicMaxPlusMat< Scalar > > DynamicProjMaxPlusMat
Alias for dynamic projective max-plus matrices with run-time dimensions.
Definition matrix.hpp:7037
static constexpr bool IsProjMaxPlusMat
Helper to check if a type is ProjMaxPlusMat.
Definition matrix.hpp:7084
detail::ProjMaxPlusMat< StaticMaxPlusMat< R, C, Scalar > > StaticProjMaxPlusMat
Alias for static projective max-plus matrices with compile-time arithmetic and dimensions.
Definition matrix.hpp:7023
T inner_product(T... args)
T insert(T... args)
T lexicographical_compare(T... args)
T make_pair(T... args)
T max_element(T... args)
T max(T... args)
T min(T... args)
T move(T... args)
Bipartition one(Bipartition const &f)
Return the identity bipartition with the same degree as the given bipartition.
std::vector< uint8_t > rows(BMat8 const &x)
Returns a vector of the rows of a BMat8.
constexpr BMat8 transpose(BMat8 const &x) noexcept
Returns the transpose of a BMat8.
Definition bmat8.hpp:719
Namespace for helper functions for matrices.
Definition matrix.hpp:170
void bitset_row_basis(Container &&rows, std::decay_t< Container > &result)
Appends a basis for the space spanned by some bitsets to a container.
Definition matrix.hpp:7482
void bitset_rows(Container &&views, detail::StaticVector1< BitSet< C >, R > &result)
Converts a container of row views of a boolean matrix to bit sets, and append them to another contain...
Definition matrix.hpp:7310
constexpr Scalar period(StaticNTPMat< T, P, R, C, Scalar > const &) noexcept
Returns the period of a static ntp matrix.
Definition matrix.hpp:6506
auto throw_if_bad_coords(Mat const &x, size_t r, size_t c) -> std::enable_if_t< IsMatrix< Mat > >
Throws the arguments do not index an entry of a matrix.
Definition matrix.hpp:235
constexpr auto threshold(Mat const &) noexcept -> std::enable_if_t<!detail::IsTruncMat< Mat >, typename Mat::scalar_type >
Returns the threshold of a matrix.
Definition matrix.hpp:3754
size_t row_space_size(Mat const &x)
Returns the size of the row space of a boolean matrix.
Definition matrix.hpp:7984
std::vector< typename Mat::RowView > rows(Mat const &x)
Returns a std::vector of row views into the rows of a dynamic matrix.
Definition matrix.hpp:7239
auto throw_if_bad_dim(Mat const &x, Mat const &y) -> std::enable_if_t< IsMatrix< Mat > >
Throws if two matrices do not have the same dimensions.
Definition matrix.hpp:206
Mat pow(Mat const &x, typename Mat::scalar_type e)
Returns a power of a matrix.
Definition matrix.hpp:7174
auto throw_if_not_square(Mat const &x) -> std::enable_if_t< IsMatrix< Mat > >
Throws if a matrix is not square.
Definition matrix.hpp:184
std::enable_if_t< IsMaxPlusTruncMat< Mat > > row_basis(Container &&views, std::decay_t< Container > &result)
Appends a basis for a space spanned by row views or bit sets to a container.
Definition matrix.hpp:7654
Namespace for everything in the libsemigroups library.
Definition action.hpp:44
T push_back(T... args)
T sort(T... args)
Function object for returning the multiplicative identity.
Definition matrix.hpp:3904
constexpr bool operator()() const noexcept
Call operator returning the multiplication identity true of the boolean semiring.
Definition matrix.hpp:3915
Function object for addition in the boolean semiring.
Definition matrix.hpp:3850
constexpr bool operator()(bool x, bool y) const noexcept
Call operator for addition.
Definition matrix.hpp:3863
Function object for multiplication in the boolean semiring.
Definition matrix.hpp:3877
constexpr bool operator()(bool x, bool y) const noexcept
Call operator for multiplication.
Definition matrix.hpp:3890
Function object for returning the additive identity.
Definition matrix.hpp:3929
constexpr bool operator()() const noexcept
Call operator returning the additive identity false of the boolean semiring.
Definition matrix.hpp:3940
constexpr size_t operator()(Mat const &x) const noexcept
Call operator.
Definition matrix.hpp:8526
Adapter for the complexity of multiplication.
Definition adapters.hpp:128
constexpr size_t operator()(Mat const &x) const noexcept
Call operator.
Definition matrix.hpp:8555
Adapter for the degree of an element.
Definition adapters.hpp:166
constexpr size_t operator()(Mat const &x) const
Call operator.
Definition matrix.hpp:8584
Adapter for hashing.
Definition adapters.hpp:451
constexpr void operator()(Mat &, size_t) const noexcept
Call operator.
Definition matrix.hpp:8609
Adapter for increasing the degree of an element.
Definition adapters.hpp:206
Function object for returning the multiplicative identity.
Definition matrix.hpp:4243
constexpr Scalar operator()() const noexcept
Call operator returning the integer 1.
Definition matrix.hpp:4253
Function object for addition in the ring of integers.
Definition matrix.hpp:4159
constexpr Scalar operator()(Scalar x, Scalar y) const noexcept
Call operator for addition.
Definition matrix.hpp:4172
Function object for multiplication in the ring of integers.
Definition matrix.hpp:4190
constexpr Scalar operator()(Scalar x, Scalar y) const noexcept
Call operator for multiplication.
Definition matrix.hpp:4203
Function object for returning the additive identity.
Definition matrix.hpp:4218
constexpr Scalar operator()() const noexcept
Call operator returning the integer 0.
Definition matrix.hpp:4228
Function object for addition in the max-plus semiring.
Definition matrix.hpp:4461
Scalar operator()(Scalar x, Scalar y) const noexcept
Call operator for addition.
Definition matrix.hpp:4476
Function object for multiplication in the max-plus semiring.
Definition matrix.hpp:4508
Scalar operator()(Scalar x, Scalar y) const noexcept
Call operator for multiplication.
Definition matrix.hpp:4523
Function object for multiplication in truncated max-plus semirings.
Definition matrix.hpp:5095
Scalar operator()(Scalar x, Scalar y) const noexcept
Call operator for multiplication.
Definition matrix.hpp:5110
Function object for returning the additive identity of the max-plus semiring.
Definition matrix.hpp:4545
constexpr Scalar operator()() const noexcept
Call operator for additive identity.
Definition matrix.hpp:4557
Function object for addition in the min-plus semiring.
Definition matrix.hpp:4769
Scalar operator()(Scalar x, Scalar y) const noexcept
Call operator for addition.
Definition matrix.hpp:4784
Function object for multiplication in the min-plus semiring.
Definition matrix.hpp:4816
Scalar operator()(Scalar x, Scalar y) const noexcept
Call operator for multiplication.
Definition matrix.hpp:4831
Function object for multiplication in min-plus truncated semirings.
Definition matrix.hpp:5575
Scalar operator()(Scalar x, Scalar y) const noexcept
Call operator for multiplication.
Definition matrix.hpp:5588
Function object for returning the additive identity of the min-plus semiring.
Definition matrix.hpp:4853
constexpr Scalar operator()() const noexcept
Call operator for additive identity.
Definition matrix.hpp:4865
Function object for addition in ntp semirings.
Definition matrix.hpp:6082
constexpr Scalar operator()(Scalar x, Scalar y) const noexcept
Call operator for addition.
Definition matrix.hpp:6094
Function object for multiplication in an ntp semirings.
Definition matrix.hpp:6123
constexpr Scalar operator()(Scalar x, Scalar y) const noexcept
Call operator for multiplication.
Definition matrix.hpp:6137
Mat operator()(Mat const &x) const
Call operator.
Definition matrix.hpp:8643
Adapter for the identity element of the given type.
Definition adapters.hpp:251
void operator()(Mat &xy, Mat const &x, Mat const &y, size_t=0) const
Call operator.
Definition matrix.hpp:8679
Adapter for the product of two elements.
Definition adapters.hpp:289
T swap(T... args)
T tie(T... args)
T unique(T... args)