libsemigroups  v3.5.4
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<SFINAE, std::vector<scalar_type>>::value> {
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<SFINAE, std::vector<scalar_type>>::value> {}
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<U, iterator>::value
635 || std::is_same<U, const_iterator>::value,
636 "the parameter it must be of type iterator or const_iterator");
637 scalar_type const v = std::distance(_container.begin(), it);
638 return std::make_pair(v / number_of_cols(), v % number_of_cols());
639 }
640
642 // Modifiers
644
645 // noexcept because vector::swap and array::swap are noexcept
646 void swap(MatrixCommon& that) noexcept {
647 std::swap(_container, that._container);
648 }
649
650 // noexcept because swap is noexcept, and so too are number_of_rows and
651 // number_of_cols
652 void transpose_no_checks() noexcept {
653 LIBSEMIGROUPS_ASSERT(number_of_rows() == number_of_cols());
654 if (number_of_rows() == 0) {
655 return;
656 }
657 auto& x = *this;
658 for (size_t r = 0; r < number_of_rows() - 1; ++r) {
659 for (size_t c = r + 1; c < number_of_cols(); ++c) {
660 std::swap(x(r, c), x(c, r));
661 }
662 }
663 }
664
665 void transpose() {
666 matrix::throw_if_not_square(static_cast<Subclass&>(*this));
667 transpose_no_checks();
668 }
669
671 // Rows
673
674 // not noexcept because there's an allocation
675 RowView row_no_checks(size_t i) const {
676 auto& container = const_cast<Container&>(_container);
677 return RowView(static_cast<Subclass const*>(this),
678 container.begin() + i * number_of_cols(),
679 number_of_cols());
680 }
681
682 RowView row(size_t i) const {
683 if (i >= number_of_rows()) {
685 "index out of range, expected value in [{}, {}), found {}",
686 0,
687 number_of_rows(),
688 i);
689 }
690 return row_no_checks(i);
691 }
692
693 // not noexcept because there's an allocation
694 template <typename T>
695 void rows(T& x) const {
696 auto& container = const_cast<Container&>(_container);
697 for (auto itc = container.begin(); itc != container.end();
698 itc += number_of_cols()) {
699 x.emplace_back(
700 static_cast<Subclass const*>(this), itc, number_of_cols());
701 }
702 LIBSEMIGROUPS_ASSERT(x.size() == number_of_rows());
703 }
704
706 // Friend functions
708
709 friend std::ostream& operator<<(std::ostream& os, MatrixCommon const& x) {
710 os << detail::to_string(x);
711 return os;
712 }
713
714 private:
716 // Private data
718 container_type _container;
719 };
720
721 template <typename Scalar>
722 class MatrixDynamicDim {
723 public:
724 MatrixDynamicDim() : _number_of_cols(0), _number_of_rows(0) {}
725 MatrixDynamicDim(MatrixDynamicDim const&) = default;
726 MatrixDynamicDim(MatrixDynamicDim&&) = default;
727 MatrixDynamicDim& operator=(MatrixDynamicDim const&) = default;
728 MatrixDynamicDim& operator=(MatrixDynamicDim&&) = default;
729
730 MatrixDynamicDim(size_t r, size_t c)
731 : _number_of_cols(c), _number_of_rows(r) {}
732
733 ~MatrixDynamicDim() = default;
734
735 void swap(MatrixDynamicDim& that) noexcept {
736 std::swap(_number_of_cols, that._number_of_cols);
737 std::swap(_number_of_rows, that._number_of_rows);
738 }
739
740 protected:
741 size_t number_of_rows_impl() const noexcept {
742 return _number_of_rows;
743 }
744
745 size_t number_of_cols_impl() const noexcept {
746 return _number_of_cols;
747 }
748
749 private:
750 size_t _number_of_cols;
751 size_t _number_of_rows;
752 };
753
754 template <typename PlusOp,
755 typename ProdOp,
756 typename ZeroOp,
757 typename OneOp,
758 typename Scalar>
759 struct MatrixStaticArithmetic {
760 MatrixStaticArithmetic() = default;
761 MatrixStaticArithmetic(MatrixStaticArithmetic const&) = default;
762 MatrixStaticArithmetic(MatrixStaticArithmetic&&) = default;
763 MatrixStaticArithmetic& operator=(MatrixStaticArithmetic const&)
764 = default;
765 MatrixStaticArithmetic& operator=(MatrixStaticArithmetic&&) = default;
766
767 // TODO(2) from here to the end of MatrixStaticArithmetic should be
768 // private or protected
769 using scalar_type = Scalar;
770
771 static constexpr scalar_type plus_no_checks_impl(scalar_type x,
772 scalar_type y) noexcept {
773 return PlusOp()(x, y);
774 }
775
776 static constexpr scalar_type
777 product_no_checks_impl(scalar_type x, scalar_type y) noexcept {
778 return ProdOp()(x, y);
779 }
780
781 static constexpr scalar_type one_impl() noexcept {
782 return OneOp()();
783 }
784
785 static constexpr scalar_type zero_impl() noexcept {
786 return ZeroOp()();
787 }
788
789 static constexpr void const* semiring_impl() noexcept {
790 return nullptr;
791 }
792 };
793
795 // RowViews - class for cheaply storing iterators to rows
797
798 template <typename Mat, typename Subclass>
799 class RowViewCommon {
800 static_assert(IsMatrix<Mat>,
801 "the template parameter Mat must be derived from "
802 "MatrixPolymorphicBase");
803
804 public:
805 using const_iterator = typename Mat::const_iterator;
806 using iterator = typename Mat::iterator;
807
808 using scalar_type = typename Mat::scalar_type;
809 using scalar_reference = typename Mat::scalar_reference;
810 using scalar_const_reference = typename Mat::scalar_const_reference;
811
812 using Row = typename Mat::Row;
813 using matrix_type = Mat;
814
815 size_t size() const noexcept {
816 return static_cast<Subclass const*>(this)->length_impl();
817 }
818
819 private:
820 scalar_type plus_no_checks(scalar_type x, scalar_type y) const noexcept {
821 return static_cast<Subclass const*>(this)->plus_no_checks_impl(y, x);
822 }
823
824 scalar_type product_no_checks(scalar_type x,
825 scalar_type y) const noexcept {
826 return static_cast<Subclass const*>(this)->product_no_checks_impl(y, x);
827 }
828
829 public:
830 RowViewCommon() = default;
831 RowViewCommon(RowViewCommon const&) = default;
832 RowViewCommon(RowViewCommon&&) = default;
833 RowViewCommon& operator=(RowViewCommon const&) = default;
834 RowViewCommon& operator=(RowViewCommon&&) = default;
835
836 explicit RowViewCommon(Row const& r)
837 : RowViewCommon(const_cast<Row&>(r).begin()) {}
838
839 // Not noexcept because iterator::operator[] isn't
840 scalar_const_reference operator[](size_t i) const {
841 return _begin[i];
842 }
843
844 // Not noexcept because iterator::operator[] isn't
845 scalar_reference operator[](size_t i) {
846 return _begin[i];
847 }
848
849 // Not noexcept because iterator::operator[] isn't
850 scalar_const_reference operator()(size_t i) const {
851 return (*this)[i];
852 }
853
854 // Not noexcept because iterator::operator[] isn't
855 scalar_reference operator()(size_t i) {
856 return (*this)[i];
857 }
858
859 // noexcept because begin() is
860 const_iterator cbegin() const noexcept {
861 return _begin;
862 }
863
864 // not noexcept because iterator arithmetic isn't
865 const_iterator cend() const {
866 return _begin + size();
867 }
868
869 // noexcept because begin() is
870 const_iterator begin() const noexcept {
871 return _begin;
872 }
873
874 // not noexcept because iterator arithmetic isn't
875 const_iterator end() const {
876 return _begin + size();
877 }
878
879 // noexcept because begin() is
880 iterator begin() noexcept {
881 return _begin;
882 }
883
884 // not noexcept because iterator arithmetic isn't
885 iterator end() noexcept {
886 return _begin + size();
887 }
888
890 // Arithmetic operators
892
893 // not noexcept because operator[] isn't
894 void operator+=(RowViewCommon const& x) {
895 auto& this_ = *this;
896 for (size_t i = 0; i < size(); ++i) {
897 this_[i] = plus_no_checks(this_[i], x[i]);
898 }
899 }
900
901 // not noexcept because iterator arithmeic isn't
902 void operator+=(scalar_type a) {
903 for (auto& x : *this) {
904 x = plus_no_checks(x, a);
905 }
906 }
907
908 // not noexcept because iterator arithmeic isn't
909 void operator*=(scalar_type a) {
910 for (auto& x : *this) {
911 x = product_no_checks(x, a);
912 }
913 }
914
915 // not noexcept because operator*= isn't
916 Row operator*(scalar_type a) const {
917 Row result(*static_cast<Subclass const*>(this));
918 result *= a;
919 return result;
920 }
921
922 // not noexcept because operator+= isn't
923 Row operator+(RowViewCommon const& that) const {
924 Row result(*static_cast<Subclass const*>(this));
925 result += static_cast<Subclass const&>(that);
926 return result;
927 }
928
929 template <typename U>
930 bool operator==(U const& that) const {
931 // TODO(1) static assert that U is Row or RowView
932 return std::equal(begin(), end(), that.begin());
933 }
934
935 template <typename U>
936 bool operator!=(U const& that) const {
937 return !(*this == that);
938 }
939
940 template <typename U>
941 bool operator<(U const& that) const {
943 cbegin(), cend(), that.cbegin(), that.cend());
944 }
945
946 template <typename U>
947 bool operator>(U const& that) const {
948 return that < *this;
949 }
950
951 void swap(RowViewCommon& that) noexcept {
952 std::swap(that._begin, _begin);
953 }
954
955 friend std::ostream& operator<<(std::ostream& os,
956 RowViewCommon const& x) {
957 os << detail::to_string(x);
958 return os;
959 }
960
961 protected:
962 explicit RowViewCommon(iterator first) : _begin(first) {}
963
964 private:
965 iterator _begin;
966 };
967
968 template <typename Container>
969 void throw_if_any_row_wrong_size(Container const& m) {
970 if (m.size() <= 1) {
971 return;
972 }
973 uint64_t const C = m.begin()->size();
974 auto it = std::find_if_not(
975 m.begin() + 1, m.end(), [&C](typename Container::const_reference r) {
976 return r.size() == C;
977 });
978 if (it != m.end()) {
979 LIBSEMIGROUPS_EXCEPTION("invalid argument, expected every item to "
980 "have length {}, found {} in entry {}",
981 C,
982 it->size(),
983 std::distance(m.begin(), it));
984 }
985 }
986
987 template <typename Scalar>
988 void throw_if_any_row_wrong_size(
989 std::initializer_list<std::initializer_list<Scalar>> m) {
990 throw_if_any_row_wrong_size<
991 std::initializer_list<std::initializer_list<Scalar>>>(m);
992 }
993
994 } // namespace detail
995
997 // Matrix forward declarations
999
1000#ifndef LIBSEMIGROUPS_PARSED_BY_DOXYGEN
1001 template <typename PlusOp,
1002 typename ProdOp,
1003 typename ZeroOp,
1004 typename OneOp,
1005 size_t R,
1006 size_t C,
1007 typename Scalar>
1008 class StaticMatrix;
1009
1010 template <typename... Args>
1011 class DynamicMatrix;
1012
1013 template <typename PlusOp,
1014 typename ProdOp,
1015 typename ZeroOp,
1016 typename OneOp,
1017 typename Scalar>
1018 class DynamicMatrix<PlusOp, ProdOp, ZeroOp, OneOp, Scalar>;
1019
1020 template <typename Semiring, typename Scalar>
1021 class DynamicMatrix<Semiring, Scalar>;
1022#endif
1023
1028
1030 // StaticRowViews - static arithmetic
1032
1069 template <typename PlusOp,
1070 typename ProdOp,
1071 typename ZeroOp,
1072 typename OneOp,
1073 size_t C,
1074 typename Scalar>
1076 : public detail::RowViewCommon<
1077 StaticMatrix<PlusOp, ProdOp, ZeroOp, OneOp, 1, C, Scalar>,
1078 StaticRowView<PlusOp, ProdOp, ZeroOp, OneOp, C, Scalar>>,
1079 public detail::
1080 MatrixStaticArithmetic<PlusOp, ProdOp, ZeroOp, OneOp, Scalar> {
1081 private:
1082 using RowViewCommon = detail::RowViewCommon<
1085 friend RowViewCommon;
1086
1087 template <size_t R>
1088 using StaticMatrix_ = ::libsemigroups::
1089 StaticMatrix<PlusOp, ProdOp, ZeroOp, OneOp, R, C, Scalar>;
1090
1091 public:
1093 using const_iterator = typename RowViewCommon::const_iterator;
1094
1096 using iterator = typename RowViewCommon::iterator;
1097
1099 using scalar_type = Scalar;
1100
1102 using scalar_reference = typename RowViewCommon::scalar_reference;
1103
1105 // clang-format off
1106 // NOLINTNEXTLINE(whitespace/line_length)
1107 using scalar_const_reference = typename RowViewCommon::scalar_const_reference;
1108 // clang-format on
1109
1111 using matrix_type = typename RowViewCommon::matrix_type;
1112
1114 using Row = typename matrix_type::Row;
1115
1117 StaticRowView() = default;
1118
1120 StaticRowView(StaticRowView const&) = default;
1121
1124
1127
1130
1131#ifndef LIBSEMIGROUPS_PARSED_BY_DOXYGEN
1132 using RowViewCommon::RowViewCommon;
1133
1134 // TODO(2) This constructor should be private
1135 template <size_t R>
1136 StaticRowView(StaticMatrix_<R> const*,
1137 typename RowViewCommon::iterator it,
1138 size_t)
1139 : RowViewCommon(it) {}
1140
1141 using RowViewCommon::size;
1142#else
1154 explicit StaticRowView(Row const& r);
1155
1168 static constexpr size_t size() const noexcept;
1169
1182 iterator begin() noexcept;
1183
1198
1211 const_iterator cbegin() const noexcept;
1212
1227
1245 scalar_reference operator()(size_t i);
1246
1264 scalar_const_reference operator()(size_t i) const;
1265
1267 scalar_reference operator[](size_t i);
1268
1270 scalar_const_reference operator[](size_t i) const;
1271
1292 template <typename U>
1293 bool operator==(U const& that) const;
1294
1315 template <typename U>
1316 bool operator!=(U const& that) const;
1317
1323 // clang-format off
1324 // NOLINTNEXTLINE(whitespace/line_length)
1328 // clang-format on
1341 template <typename U>
1342 bool operator<(U const& that) const;
1343
1365 template <typename U>
1366 bool operator<(U const& that) const;
1367
1387 Row operator+(StaticRowView const& that);
1388
1407 void operator+=(StaticRowView const& that);
1408
1422 void operator+=(scalar_type a);
1423
1441 Row operator*(scalar_type a) const;
1442
1456 void operator*=(scalar_type a);
1457#endif
1458
1459 private:
1460 static constexpr size_t length_impl() noexcept {
1461 return C;
1462 }
1463 };
1464
1466 // DynamicRowViews - static arithmetic
1468
1469#ifndef LIBSEMIGROUPS_PARSED_BY_DOXYGEN
1470 // Doxygen needs to ignore this so that the actual implementation of
1471 // DynamicRowView gets documented.
1472 template <typename... Args>
1473 class DynamicRowView;
1474#endif
1475
1513 template <typename PlusOp,
1514 typename ProdOp,
1515 typename ZeroOp,
1516 typename OneOp,
1517 typename Scalar>
1518 class DynamicRowView<PlusOp, ProdOp, ZeroOp, OneOp, Scalar>
1519 : public detail::RowViewCommon<
1520 DynamicMatrix<PlusOp, ProdOp, ZeroOp, OneOp, Scalar>,
1521 DynamicRowView<PlusOp, ProdOp, ZeroOp, OneOp, Scalar>>,
1522 public detail::
1523 MatrixStaticArithmetic<PlusOp, ProdOp, ZeroOp, OneOp, Scalar> {
1524 private:
1525 using DynamicMatrix_ = DynamicMatrix<PlusOp, ProdOp, ZeroOp, OneOp, Scalar>;
1526 using RowViewCommon = detail::RowViewCommon<
1527 DynamicMatrix_,
1529 friend RowViewCommon;
1530
1531 public:
1533 using const_iterator = typename RowViewCommon::const_iterator;
1534
1536 using iterator = typename RowViewCommon::iterator;
1537
1539 using scalar_type = Scalar;
1540
1542 using scalar_reference = typename RowViewCommon::scalar_reference;
1543
1545 // clang-format off
1546 // NOLINTNEXTLINE(whitespace/line_length)
1547 using scalar_const_reference = typename RowViewCommon::scalar_const_reference;
1548 // clang-format on
1549
1551 using matrix_type = typename RowViewCommon::matrix_type;
1552
1554 using Row = typename matrix_type::Row;
1555
1557 DynamicRowView() = default;
1558
1561
1564
1567
1570
1572 explicit DynamicRowView(Row const& r)
1573 : RowViewCommon(r), _length(r.number_of_cols()) {}
1574
1575#ifndef LIBSEMIGROUPS_PARSED_BY_DOXYGEN
1576 using RowViewCommon::RowViewCommon;
1577
1578 // TODO(2) This constructor should be private
1579 DynamicRowView(DynamicMatrix_ const*, iterator const& it, size_t N)
1580 : RowViewCommon(it), _length(N) {}
1581
1582 using RowViewCommon::size;
1583#else
1585 size_t size() const noexcept;
1586
1588 iterator begin() noexcept;
1589
1592
1594 const_iterator cbegin() const noexcept;
1595
1598
1600 scalar_reference operator()(size_t i);
1601
1603 scalar_const_reference operator()(size_t i) const;
1604
1606 scalar_reference operator[](size_t i);
1607
1609 scalar_const_reference operator[](size_t i) const;
1610
1612 template <typename U>
1613 bool operator==(U const& that) const;
1614
1616 template <typename U>
1617 bool operator!=(U const& that) const;
1618
1620 template <typename U>
1621 bool operator<(U const& that) const;
1622
1624 template <typename U>
1625 bool operator<(U const& that) const;
1626
1628 Row operator+(DynamicRowView const& that);
1629
1631 void operator+=(DynamicRowView const& that);
1632
1634 void operator+=(scalar_type a);
1635
1637 Row operator*(scalar_type a) const;
1638
1640 void operator*=(scalar_type a);
1641#endif // LIBSEMIGROUPS_PARSED_BY_DOXYGEN
1642
1643 private:
1644 size_t length_impl() const noexcept {
1645 return _length;
1646 }
1647 size_t _length;
1648 };
1649
1651 // DynamicRowViews - dynamic arithmetic
1653
1674 template <typename Semiring, typename Scalar>
1675 class DynamicRowView<Semiring, Scalar>
1676 : public detail::RowViewCommon<DynamicMatrix<Semiring, Scalar>,
1677 DynamicRowView<Semiring, Scalar>> {
1678 private:
1679 using DynamicMatrix_ = DynamicMatrix<Semiring, Scalar>;
1680 friend DynamicMatrix_;
1681 using RowViewCommon
1682 = detail::RowViewCommon<DynamicMatrix_,
1684 friend RowViewCommon;
1685
1686 public:
1688 using const_iterator = typename RowViewCommon::const_iterator;
1689
1691 using iterator = typename RowViewCommon::iterator;
1692
1694 using scalar_type = Scalar;
1695
1697 using scalar_reference = typename RowViewCommon::scalar_reference;
1698
1700 // clang-format off
1701 // NOLINTNEXTLINE(whitespace/line_length)
1702 using scalar_const_reference = typename RowViewCommon::scalar_const_reference;
1703 // clang-format on
1704
1706 using matrix_type = typename RowViewCommon::matrix_type;
1707
1709 using Row = typename matrix_type::Row;
1710
1712 DynamicRowView() = default;
1713
1715 DynamicRowView(DynamicRowView const&) = default;
1716
1718 DynamicRowView(DynamicRowView&&) = default;
1719
1721 DynamicRowView& operator=(DynamicRowView const&) = default;
1722
1725
1727 explicit DynamicRowView(Row const& r) : RowViewCommon(r), _matrix(&r) {}
1728
1729#ifndef LIBSEMIGROUPS_PARSED_BY_DOXYGEN
1730 using RowViewCommon::RowViewCommon;
1731
1732 // TODO(2) This constructor should be private
1733 DynamicRowView(DynamicMatrix_ const* mat, iterator const& it, size_t)
1734 : RowViewCommon(it), _matrix(mat) {}
1735
1736 using RowViewCommon::size;
1737#else
1739 size_t size() const noexcept;
1740
1742 iterator begin() noexcept;
1743
1745 iterator end();
1746
1748 const_iterator cbegin() const noexcept;
1749
1751 iterator cend();
1752
1754 scalar_reference operator()(size_t i);
1755
1757 scalar_const_reference operator()(size_t i) const;
1758
1760 scalar_reference operator[](size_t i);
1761
1763 scalar_const_reference operator[](size_t i) const;
1764
1766 template <typename U>
1767 bool operator==(U const& that) const;
1768
1770 template <typename U>
1771 bool operator!=(U const& that) const;
1772
1774 template <typename U>
1775 bool operator<(U const& that) const;
1776
1778 template <typename U>
1779 bool operator<(U const& that) const;
1780
1782 Row operator+(DynamicRowView const& that);
1783
1785 void operator+=(DynamicRowView const& that);
1786
1788 void operator+=(scalar_type a);
1789
1791 Row operator*(scalar_type a) const;
1792
1794 void operator*=(scalar_type a);
1795#endif
1796
1797 private:
1798 size_t length_impl() const noexcept {
1799 return _matrix->number_of_cols();
1800 }
1801
1802 scalar_type plus_no_checks_impl(scalar_type x,
1803 scalar_type y) const noexcept {
1804 return _matrix->plus_no_checks_impl(x, y);
1805 }
1806
1807 scalar_type product_no_checks_impl(scalar_type x,
1808 scalar_type y) const noexcept {
1809 return _matrix->product_no_checks_impl(x, y);
1810 }
1811
1812 DynamicMatrix_ const* _matrix;
1813 };
1814
1816 // StaticMatrix with compile time semiring arithmetic
1818
1852 template <typename PlusOp,
1853 typename ProdOp,
1854 typename ZeroOp,
1855 typename OneOp,
1856 size_t R,
1857 size_t C,
1858 typename Scalar>
1860 : public detail::
1861 MatrixStaticArithmetic<PlusOp, ProdOp, ZeroOp, OneOp, Scalar>,
1862 public detail::MatrixCommon<
1863 std::array<Scalar, R * C>,
1864 StaticMatrix<PlusOp, ProdOp, ZeroOp, OneOp, R, C, Scalar>,
1865 StaticRowView<PlusOp, ProdOp, ZeroOp, OneOp, C, Scalar>> {
1867 // StaticMatrix - Aliases - private
1869
1870 using MatrixCommon = ::libsemigroups::detail::MatrixCommon<
1874 friend MatrixCommon;
1875
1876 public:
1878 // StaticMatrix - Aliases - public
1880
1882 using scalar_type = typename MatrixCommon::scalar_type;
1883
1885 using scalar_reference = typename MatrixCommon::scalar_reference;
1886
1888 // clang-format off
1889 // NOLINTNEXTLINE(whitespace/line_length)
1890 using scalar_const_reference = typename MatrixCommon::scalar_const_reference;
1891 // clang-format on
1892
1895
1898
1900 using Plus = PlusOp;
1901
1903 using Prod = ProdOp;
1904
1906 using Zero = ZeroOp;
1907
1909 using One = OneOp;
1910
1912 using iterator = typename MatrixCommon::iterator;
1913
1915 using const_iterator = typename MatrixCommon::const_iterator;
1916
1917 static constexpr size_t nr_rows = R;
1918 static constexpr size_t nr_cols = C;
1919
1921 // StaticMatrix - Constructors + destructor - public
1923
1941 : MatrixCommon(c) {
1942 static_assert(R == 1,
1943 "cannot construct Matrix from the given initializer list, "
1944 "incompatible dimensions");
1945 LIBSEMIGROUPS_ASSERT(c.size() == C);
1946 }
1947
1969 : MatrixCommon(m) {}
1970
1984 : MatrixCommon(m) {}
1985
2003 explicit StaticMatrix(RowView const& rv) : MatrixCommon(rv) {
2004 static_assert(
2005 R == 1,
2006 "cannot construct Matrix with more than one row from RowView!");
2007 }
2008
2012 StaticMatrix() = default;
2013
2017 StaticMatrix(StaticMatrix const&) = default;
2018
2023
2028
2033
2034#ifndef LIBSEMIGROUPS_PARSED_BY_DOXYGEN
2035 // For uniformity of interface, the args do nothing
2036 StaticMatrix(size_t r, size_t c) : StaticMatrix() {
2037 (void) r;
2038 (void) c;
2039 LIBSEMIGROUPS_ASSERT(r == number_of_rows());
2040 LIBSEMIGROUPS_ASSERT(c == number_of_cols());
2041 }
2042
2043 // For uniformity of interface, the first arg does nothing
2044 StaticMatrix(void const* ptr, std::initializer_list<scalar_type> const& c)
2045 : StaticMatrix(c) {
2046 (void) ptr;
2047 LIBSEMIGROUPS_ASSERT(ptr == nullptr);
2048 }
2049
2050 // For uniformity of interface, the first arg does nothing
2052 void const* ptr,
2054 : StaticMatrix(m) {
2055 (void) ptr;
2056 LIBSEMIGROUPS_ASSERT(ptr == nullptr);
2057 }
2058
2059 // For uniformity of interface, the first arg does nothing
2060 explicit StaticMatrix(void const* ptr, RowView const& rv)
2061 : StaticMatrix(rv) {
2062 (void) ptr;
2063 LIBSEMIGROUPS_ASSERT(ptr == nullptr);
2064 }
2065
2066 // For uniformity of interface, no arg used for anything
2067 StaticMatrix(void const* ptr, size_t r, size_t c) : StaticMatrix(r, c) {
2068 (void) ptr;
2069 LIBSEMIGROUPS_ASSERT(ptr == nullptr);
2070 }
2071#endif
2072
2073 ~StaticMatrix() = default;
2074
2075#ifndef LIBSEMIGROUPS_PARSED_BY_DOXYGEN
2076 static StaticMatrix one(size_t n) {
2077 // If specified the value of n must equal R or otherwise weirdness will
2078 // ensue...
2079 LIBSEMIGROUPS_ASSERT(n == 0 || n == R);
2080 (void) n;
2081#if defined(__APPLE__) && defined(__clang__) \
2082 && (__clang_major__ == 13 || __clang_major__ == 14)
2083 // With Apple clang version 13.1.6 (clang-1316.0.21.2.5) something goes
2084 // wrong and the value R is optimized away somehow, meaning that the
2085 // values on the diagonal aren't actually set. This only occurs when
2086 // libsemigroups is compiled with -O2 or higher.
2087 size_t volatile const m = R;
2088#else
2089 size_t const m = R;
2090#endif
2091 StaticMatrix x(m, m);
2092 std::fill(x.begin(), x.end(), ZeroOp()());
2093 for (size_t r = 0; r < m; ++r) {
2094 x(r, r) = OneOp()();
2095 }
2096 return x;
2097 }
2098
2099 static StaticMatrix one(void const* ptr, size_t n = 0) {
2100 (void) ptr;
2101 LIBSEMIGROUPS_ASSERT(ptr == nullptr);
2102 LIBSEMIGROUPS_ASSERT(n == 0 || n == R);
2103 return one(n);
2104 }
2105#endif // LIBSEMIGROUPS_PARSED_BY_DOXYGEN
2106
2108 // StaticMatrix - member function aliases - public
2110#ifdef LIBSEMIGROUPS_PARSED_BY_DOXYGEN
2129 scalar_reference operator()(size_t r, size_t c);
2130
2144 scalar_reference at(size_t r, size_t c);
2145
2164 scalar_const_reference operator()(size_t r, size_t c) const;
2165
2179 scalar_const_reference at(size_t r, size_t c) const;
2180
2197 iterator begin() noexcept;
2198
2214
2232 const_iterator cbegin() const noexcept;
2233
2253
2267 bool operator==(StaticMatrix const& that) const;
2268
2270 bool operator==(RowView const& that) const;
2271
2283 bool operator!=(StaticMatrix const& that) const;
2284
2286 bool operator!=(RowView const& that) const;
2287
2301 bool operator<(StaticMatrix const& that) const;
2302
2304 bool operator<(RowView const& that) const;
2305
2319 bool operator>(StaticMatrix const& that) const;
2320
2337
2349 size_t number_of_rows() const noexcept;
2350
2362 size_t number_of_cols() const noexcept;
2363
2381 StaticMatrix operator+(StaticMatrix const& that);
2382
2399 void operator+=(StaticMatrix const& that);
2400
2402 void operator+=(RowView const& that);
2403
2415 void operator+=(scalar_type a);
2416
2434 StaticMatrix operator*(StaticMatrix const& that);
2435
2447 void operator*=(scalar_type a);
2448
2466 StaticMatrix const& y);
2467
2483 RowView row_no_checks(size_t i) const;
2484
2495 RowView row(size_t i) const;
2496
2510 template <typename T>
2511 void rows(T& x) const;
2512
2525 void swap(StaticMatrix& that) noexcept;
2526
2540
2556
2568 static StaticMatrix one() const;
2569
2583 size_t hash_value() const;
2584
2599 template <typename U>
2600 bool operator<=(U const& that) const;
2601
2616 template <typename U>
2617 bool operator>=(U const& that) const;
2618
2632
2647
2658 scalar_type scalar_zero() const noexcept;
2659
2670 scalar_type scalar_one() const noexcept;
2671
2683 semiring_type const* semiring() const noexcept;
2684
2685#else
2686 using MatrixCommon::at;
2687 using MatrixCommon::begin;
2688 using MatrixCommon::cbegin;
2689 using MatrixCommon::cend;
2690 using MatrixCommon::coords;
2691 using MatrixCommon::end;
2692 using MatrixCommon::hash_value;
2693 using MatrixCommon::number_of_cols;
2694 using MatrixCommon::number_of_rows;
2695 using MatrixCommon::one;
2696 using MatrixCommon::operator!=;
2697 using MatrixCommon::operator();
2698 using MatrixCommon::operator*;
2699 using MatrixCommon::operator*=;
2700 using MatrixCommon::operator+;
2701 using MatrixCommon::operator+=;
2702 using MatrixCommon::operator<; // NOLINT(whitespace/operators)
2703 using MatrixCommon::operator<=;
2704 using MatrixCommon::operator==;
2705 using MatrixCommon::operator>; // NOLINT(whitespace/operators)
2706 using MatrixCommon::operator>=;
2707 using MatrixCommon::product_inplace_no_checks;
2708 using MatrixCommon::row;
2709 using MatrixCommon::row_no_checks;
2710 using MatrixCommon::rows;
2711 using MatrixCommon::scalar_one;
2712 using MatrixCommon::scalar_zero;
2713 using MatrixCommon::semiring;
2714 using MatrixCommon::swap;
2715 using MatrixCommon::transpose;
2716 using MatrixCommon::transpose_no_checks;
2717#endif // LIBSEMIGROUPS_PARSED_BY_DOXYGEN
2718
2719 private:
2721 // StaticMatrix - implementation of MatrixCommon requirements - private
2723
2724 static constexpr size_t number_of_rows_impl() noexcept {
2725 return R;
2726 }
2727 static constexpr size_t number_of_cols_impl() noexcept {
2728 return C;
2729 }
2730 };
2731
2733 // DynamicMatrix with compile time semiring arithmetic
2735
2769 template <typename PlusOp,
2770 typename ProdOp,
2771 typename ZeroOp,
2772 typename OneOp,
2773 typename Scalar>
2774 class DynamicMatrix<PlusOp, ProdOp, ZeroOp, OneOp, Scalar>
2775 : public detail::MatrixDynamicDim<Scalar>,
2776 public detail::MatrixCommon<
2777 std::vector<Scalar>,
2778 DynamicMatrix<PlusOp, ProdOp, ZeroOp, OneOp, Scalar>,
2779 DynamicRowView<PlusOp, ProdOp, ZeroOp, OneOp, Scalar>>,
2780 public detail::
2781 MatrixStaticArithmetic<PlusOp, ProdOp, ZeroOp, OneOp, Scalar> {
2782 using MatrixDynamicDim = ::libsemigroups::detail::MatrixDynamicDim<Scalar>;
2783 using MatrixCommon = ::libsemigroups::detail::MatrixCommon<
2786 DynamicRowView<PlusOp, ProdOp, ZeroOp, OneOp, Scalar>>;
2787 friend MatrixCommon;
2788
2789 public:
2791 using scalar_type = typename MatrixCommon::scalar_type;
2792
2794 using scalar_reference = typename MatrixCommon::scalar_reference;
2795
2797 // clang-format off
2798 // NOLINTNEXTLINE(whitespace/line_length)
2799 using scalar_const_reference = typename MatrixCommon::scalar_const_reference;
2800 // clang-format on
2801
2804
2806 using RowView = DynamicRowView<PlusOp, ProdOp, ZeroOp, OneOp, Scalar>;
2807
2809 using Plus = PlusOp;
2810
2812 using Prod = ProdOp;
2813
2815 using Zero = ZeroOp;
2816
2818 using One = OneOp;
2819
2825 using semiring_type = void;
2826
2830 DynamicMatrix() = default;
2831
2835 DynamicMatrix(DynamicMatrix const&) = default;
2836
2841
2846
2851
2870 DynamicMatrix(size_t r, size_t c) : MatrixDynamicDim(r, c), MatrixCommon() {
2871 resize(number_of_rows(), number_of_cols());
2872 }
2873
2894 : MatrixDynamicDim(1, c.size()), MatrixCommon(c) {}
2895
2918 : MatrixDynamicDim(m.size(), std::empty(m) ? 0 : m.begin()->size()),
2919 MatrixCommon(m) {}
2920
2936 : MatrixDynamicDim(m.size(), std::empty(m) ? 0 : m.begin()->size()),
2937 MatrixCommon(m) {}
2938
2950 explicit DynamicMatrix(RowView const& rv)
2951 : MatrixDynamicDim(1, rv.size()), MatrixCommon(rv) {}
2952
2953#ifndef LIBSEMIGROUPS_PARSED_BY_DOXYGEN
2954 // For uniformity of interface, the first arg does nothing
2955 DynamicMatrix(void const* ptr, size_t r, size_t c) : DynamicMatrix(r, c) {
2956 (void) ptr;
2957 LIBSEMIGROUPS_ASSERT(ptr == nullptr);
2958 }
2959
2960 // For uniformity of interface, the first arg does nothing
2961 DynamicMatrix(void const* ptr, std::initializer_list<scalar_type> const& c)
2962 : DynamicMatrix(c) {
2963 (void) ptr;
2964 LIBSEMIGROUPS_ASSERT(ptr == nullptr);
2965 }
2966
2967 // For uniformity of interface, the first arg does nothing
2968 DynamicMatrix(
2969 void const* ptr,
2970 std::initializer_list<std::initializer_list<scalar_type>> const& m)
2971 : DynamicMatrix(m) {
2972 (void) ptr;
2973 LIBSEMIGROUPS_ASSERT(ptr == nullptr);
2974 }
2975
2976 static DynamicMatrix one(void const* ptr, size_t n) {
2977 (void) ptr;
2978 LIBSEMIGROUPS_ASSERT(ptr == nullptr);
2979 return one(n);
2980 }
2981#endif // LIBSEMIGROUPS_PARSED_BY_DOXYGEN
2982
2983 ~DynamicMatrix() = default;
2984
2997 static DynamicMatrix one(size_t n) {
2998 DynamicMatrix x(n, n);
2999 std::fill(x.begin(), x.end(), ZeroOp()());
3000 for (size_t r = 0; r < n; ++r) {
3001 x(r, r) = OneOp()();
3002 }
3003 return x;
3004 }
3005
3006#ifdef LIBSEMIGROUPS_PARSED_BY_DOXYGEN
3008 scalar_reference at(size_t r, size_t c);
3009
3011 scalar_reference at(size_t r, size_t c) const;
3012
3014 iterator begin() noexcept;
3015
3017 const_iterator cbegin() noexcept;
3018
3020 const_iterator cend() noexcept;
3021
3023 std::pair<scalar_type, scalar_type> coords(const_iterator it) const;
3024
3026 iterator end() noexcept;
3027
3029 size_t hash_value() const;
3030
3032 size_t number_of_cols() const noexcept;
3033
3035 size_t number_of_rows() const noexcept;
3036
3038 bool operator!=(DynamicMatrix const& that) const;
3039
3041 bool operator!=(RowView const& that) const;
3042
3044 scalar_reference operator()(size_t r, size_t c);
3045
3047 scalar_const_reference operator()(size_t r, size_t c) const;
3060
3062 DynamicMatrix operator*(DynamicMatrix const& that);
3063
3065 void operator*=(scalar_type a);
3066
3068 DynamicMatrix operator+(DynamicMatrix const& that);
3069
3071 void operator+=(DynamicMatrix const& that);
3072
3074 void operator+=(RowView const& that);
3075
3087 void operator+=(scalar_type a);
3088
3090 bool operator<(DynamicMatrix const& that) const;
3091
3093 bool operator<(RowView const& that) const;
3094
3096 template <typename T>
3097 bool operator<=(T const& that) const;
3098
3100 bool operator==(DynamicMatrix const& that) const;
3101
3103 bool operator==(RowView const& that) const;
3104
3106 bool operator>(DynamicMatrix const& that) const;
3107
3109 template <typename T>
3110 bool operator>=(T const& that) const;
3111
3114 DynamicMatrix const& y);
3115
3117 RowView row(size_t i) const;
3118
3120 RowView row_no_checks(size_t i) const;
3121
3123 template <typename T>
3124 void rows(T& x) const;
3125
3127 scalar_type scalar_one() const noexcept;
3128
3130 scalar_type scalar_zero() const noexcept;
3131
3133 semiring_type const* semiring() const noexcept;
3134
3137
3140#else
3141 using MatrixCommon::at;
3142 using MatrixCommon::begin;
3143 using MatrixCommon::cbegin;
3144 using MatrixCommon::cend;
3145 using MatrixCommon::coords;
3146 using MatrixCommon::end;
3147 using MatrixCommon::hash_value;
3148 using MatrixCommon::number_of_cols;
3149 using MatrixCommon::number_of_rows;
3150 using MatrixCommon::one;
3151 using MatrixCommon::operator!=;
3152 using MatrixCommon::operator();
3153 using MatrixCommon::operator*;
3154 using MatrixCommon::operator*=;
3155 using MatrixCommon::operator+;
3156 using MatrixCommon::operator+=;
3157 using MatrixCommon::operator<; // NOLINT(whitespace/operators)
3158 using MatrixCommon::operator<=;
3159 using MatrixCommon::operator==;
3160 using MatrixCommon::operator>; // NOLINT(whitespace/operators)
3161 using MatrixCommon::operator>=;
3162 using MatrixCommon::product_inplace_no_checks;
3163 using MatrixCommon::row;
3164 using MatrixCommon::row_no_checks;
3165 using MatrixCommon::rows;
3166 using MatrixCommon::scalar_one;
3167 using MatrixCommon::scalar_zero;
3168 using MatrixCommon::semiring;
3169 // using MatrixCommon::swap; // Don't want this use the one below.
3170 using MatrixCommon::transpose;
3171 using MatrixCommon::transpose_no_checks;
3172#endif // LIBSEMIGROUPS_PARSED_BY_DOXYGEN
3173
3175 void swap(DynamicMatrix& that) noexcept {
3176 static_cast<MatrixDynamicDim&>(*this).swap(
3177 static_cast<MatrixDynamicDim&>(that));
3178 static_cast<MatrixCommon&>(*this).swap(static_cast<MatrixCommon&>(that));
3179 }
3180
3181 private:
3182 using MatrixCommon::resize;
3183 };
3184
3186 // DynamicMatrix with runtime semiring arithmetic
3188
3223 template <typename Semiring, typename Scalar>
3224 class DynamicMatrix<Semiring, Scalar>
3225 : public detail::MatrixDynamicDim<Scalar>,
3226 public detail::MatrixCommon<std::vector<Scalar>,
3227 DynamicMatrix<Semiring, Scalar>,
3228 DynamicRowView<Semiring, Scalar>,
3229 Semiring> {
3230 using MatrixCommon = detail::MatrixCommon<std::vector<Scalar>,
3232 DynamicRowView<Semiring, Scalar>,
3233 Semiring>;
3234 friend MatrixCommon;
3235 using MatrixDynamicDim = ::libsemigroups::detail::MatrixDynamicDim<Scalar>;
3236
3237 public:
3239 using scalar_type = typename MatrixCommon::scalar_type;
3240
3242 using scalar_reference = typename MatrixCommon::scalar_reference;
3243
3245 // clang-format off
3246 // NOLINTNEXTLINE(whitespace/line_length)
3247 using scalar_const_reference = typename MatrixCommon::scalar_const_reference;
3248 // clang-format on
3249
3252
3254 using RowView = DynamicRowView<Semiring, Scalar>;
3255
3256 friend RowView;
3257
3259 using semiring_type = Semiring;
3260
3266 DynamicMatrix() = delete;
3267
3269 DynamicMatrix(DynamicMatrix const&) = default;
3270
3273
3276
3279
3294 DynamicMatrix(Semiring const* sr, size_t r, size_t c)
3295 : MatrixDynamicDim(r, c), MatrixCommon(), _semiring(sr) {
3296 resize(number_of_rows(), number_of_cols());
3297 }
3298
3315 Semiring const* sr,
3317 : MatrixDynamicDim(rws.size(),
3318 std::empty(rws) ? 0 : rws.begin()->size()),
3319 MatrixCommon(rws),
3320 _semiring(sr) {}
3321
3337 explicit DynamicMatrix(Semiring const* sr,
3339 : MatrixDynamicDim(rws.size(), (rws.empty() ? 0 : rws.begin()->size())),
3340 MatrixCommon(rws),
3341 _semiring(sr) {}
3342
3356 explicit DynamicMatrix(Semiring const* sr,
3358 : MatrixDynamicDim(1, rw.size()), MatrixCommon(rw), _semiring(sr) {}
3359
3371 explicit DynamicMatrix(RowView const& rv)
3372 : MatrixDynamicDim(1, rv.size()),
3373 MatrixCommon(rv),
3374 _semiring(rv._matrix->semiring()) {}
3375
3390 // No static DynamicMatrix::one(size_t n) because we need a semiring!
3391 static DynamicMatrix one(Semiring const* semiring, size_t n) {
3392 DynamicMatrix x(semiring, n, n);
3393 std::fill(x.begin(), x.end(), x.scalar_zero());
3394 for (size_t r = 0; r < n; ++r) {
3395 x(r, r) = x.scalar_one();
3396 }
3397 return x;
3398 }
3399
3400 ~DynamicMatrix() = default;
3401
3402#ifdef LIBSEMIGROUPS_PARSED_BY_DOXYGEN
3404 scalar_reference at(size_t r, size_t c);
3405
3407 scalar_reference at(size_t r, size_t c) const;
3408
3410 iterator begin() noexcept;
3411
3413 const_iterator cbegin() noexcept;
3414
3416 const_iterator cend() noexcept;
3417
3419 std::pair<scalar_type, scalar_type> coords(const_iterator it) const;
3420
3422 iterator end() noexcept;
3423
3425 size_t hash_value() const;
3426
3428 size_t number_of_cols() const noexcept;
3429
3431 size_t number_of_rows() const noexcept;
3432
3434 bool operator!=(DynamicMatrix const& that) const;
3435
3437 bool operator!=(RowView const& that) const;
3438
3440 scalar_reference operator()(size_t r, size_t c);
3441
3443 scalar_const_reference operator()(size_t r, size_t c) const;
3456
3458 DynamicMatrix operator*(DynamicMatrix const& that);
3459
3461 void operator*=(scalar_type a);
3462
3464 DynamicMatrix operator+(DynamicMatrix const& that);
3465
3467 void operator+=(DynamicMatrix const& that);
3468
3470 void operator+=(RowView const& that);
3471
3483 void operator+=(scalar_type a);
3484
3486 bool operator<(DynamicMatrix const& that) const;
3487
3489 bool operator<(RowView const& that) const;
3490
3492 template <typename T>
3493 bool operator<=(T const& that) const;
3494
3496 bool operator==(DynamicMatrix const& that) const;
3497
3499 bool operator==(RowView const& that) const;
3500
3502 bool operator>(DynamicMatrix const& that) const;
3503
3505 template <typename T>
3506 bool operator>=(T const& that) const;
3507
3510 DynamicMatrix const& y);
3511
3513 RowView row(size_t i) const;
3514
3516 RowView row_no_checks(size_t i) const;
3517
3519 template <typename T>
3520 void rows(T& x) const;
3521
3523 scalar_type scalar_one() const noexcept;
3524
3526 scalar_type scalar_zero() const noexcept;
3527
3529 semiring_type const* semiring() const noexcept;
3530
3533
3536#else
3537 using MatrixCommon::at;
3538 using MatrixCommon::begin;
3539 using MatrixCommon::cbegin;
3540 using MatrixCommon::cend;
3541 using MatrixCommon::coords;
3542 using MatrixCommon::end;
3543 using MatrixCommon::hash_value;
3544 using MatrixCommon::number_of_cols;
3545 using MatrixCommon::number_of_rows;
3546 using MatrixCommon::one;
3547 using MatrixCommon::operator!=;
3548 using MatrixCommon::operator();
3549 using MatrixCommon::operator*;
3550 using MatrixCommon::operator*=;
3551 using MatrixCommon::operator+;
3552 using MatrixCommon::operator+=;
3553 using MatrixCommon::operator<; // NOLINT(whitespace/operators)
3554 using MatrixCommon::operator<=;
3555 using MatrixCommon::operator==;
3556 using MatrixCommon::operator>; // NOLINT(whitespace/operators)
3557 using MatrixCommon::operator>=;
3558 using MatrixCommon::product_inplace_no_checks;
3559 using MatrixCommon::row;
3560 using MatrixCommon::row_no_checks;
3561 using MatrixCommon::rows;
3562 using MatrixCommon::scalar_one;
3563 using MatrixCommon::scalar_zero;
3564 using MatrixCommon::semiring;
3565 // using MatrixCommon::swap; // Don't want this use the one below.
3566 using MatrixCommon::transpose;
3567 using MatrixCommon::transpose_no_checks;
3568#endif // LIBSEMIGROUPS_PARSED_BY_DOXYGEN
3569
3571 void swap(DynamicMatrix& that) noexcept {
3572 static_cast<MatrixDynamicDim&>(*this).swap(
3573 static_cast<MatrixDynamicDim&>(that));
3574 static_cast<MatrixCommon&>(*this).swap(static_cast<MatrixCommon&>(that));
3575 std::swap(_semiring, that._semiring);
3576 }
3577
3578 private:
3579 using MatrixCommon::resize;
3580
3581 scalar_type plus_no_checks_impl(scalar_type x,
3582 scalar_type y) const noexcept {
3583 return _semiring->plus_no_checks(x, y);
3584 }
3585
3586 scalar_type product_no_checks_impl(scalar_type x,
3587 scalar_type y) const noexcept {
3588 return _semiring->product_no_checks(x, y);
3589 }
3590
3591 scalar_type one_impl() const noexcept {
3592 return _semiring->scalar_one();
3593 }
3594
3595 scalar_type zero_impl() const noexcept {
3596 return _semiring->scalar_zero();
3597 }
3598
3599 Semiring const* semiring_impl() const noexcept {
3600 return _semiring;
3601 }
3602
3603 Semiring const* _semiring;
3604 };
3605
3607 // Helper structs to check if matrix is static, or has a pointer to a
3608 // semiring
3610
3611 namespace detail {
3612 template <typename T>
3613 struct IsStaticMatrixHelper : std::false_type {};
3614
3615 template <typename PlusOp,
3616 typename ProdOp,
3617 typename ZeroOp,
3618 typename OneOp,
3619 size_t R,
3620 size_t C,
3621 typename Scalar>
3622 struct IsStaticMatrixHelper<
3623 StaticMatrix<PlusOp, ProdOp, ZeroOp, OneOp, R, C, Scalar>>
3624 : std::true_type {};
3625
3626 template <typename T>
3627 struct IsMatWithSemiringHelper : std::false_type {};
3628
3629 template <typename Semiring, typename Scalar>
3630 struct IsMatWithSemiringHelper<DynamicMatrix<Semiring, Scalar>>
3631 : std::true_type {};
3632
3633 template <typename S, typename T = void>
3634 struct IsTruncMatHelper : std::false_type {};
3635
3636 } // namespace detail
3637
3648 template <typename T>
3649 constexpr bool IsStaticMatrix = detail::IsStaticMatrixHelper<T>::value;
3650
3661 template <typename T>
3663
3674 template <typename T>
3675 static constexpr bool IsMatWithSemiring
3676 = detail::IsMatWithSemiringHelper<T>::value;
3677
3678 namespace detail {
3679
3680 template <typename T>
3681 static constexpr bool IsTruncMat = IsTruncMatHelper<T>::value;
3682
3683 template <typename Mat>
3684 void throw_if_semiring_nullptr(Mat const& m) {
3685 if (IsMatWithSemiring<Mat> && m.semiring() == nullptr) {
3687 "the matrix's pointer to a semiring is nullptr!")
3688 }
3689 }
3690
3691 template <typename Mat, typename Container>
3692 auto throw_if_bad_dim(Container const& m)
3693 -> std::enable_if_t<IsStaticMatrix<Mat>> {
3694 // Only call this if you've already called throw_if_any_row_wrong_size
3695 uint64_t const R = m.size();
3696 uint64_t const C = std::empty(m) ? 0 : m.begin()->size();
3697 if (R != Mat::nr_rows || C != Mat::nr_cols) {
3699 "invalid argument, cannot initialize an {}x{} matrix with compile "
3700 "time dimension, with an {}x{} container",
3701 Mat::nr_rows,
3702 Mat::nr_cols,
3703 R,
3704 C);
3705 }
3706 }
3707
3708 template <typename Mat, typename Container>
3709 auto throw_if_bad_dim(Container const&)
3710 -> std::enable_if_t<IsDynamicMatrix<Mat>> {}
3711 // Not checking dynamic matrices, no compile-time dimensions.
3712
3713 template <typename Mat, typename Container>
3714 auto throw_if_bad_row_dim(Container const& row)
3715 -> std::enable_if_t<IsStaticMatrix<Mat>> {
3716 uint64_t const C = row.size();
3717 if (C != Mat::nr_cols) {
3719 "invalid argument, cannot initialize a row of a matrix with "
3720 "compile time number of columns {} with a container of size {}",
3721 Mat::nr_cols,
3722 C);
3723 }
3724 }
3725
3726 template <typename Mat, typename Container>
3727 auto throw_if_bad_row_dim(Container const&)
3728 -> std::enable_if_t<IsDynamicMatrix<Mat>> {}
3729 } // namespace detail
3730
3739 namespace matrix {
3740
3754 template <typename Mat>
3755 constexpr auto threshold(Mat const&) noexcept
3756 -> std::enable_if_t<!detail::IsTruncMat<Mat>,
3757 typename Mat::scalar_type> {
3758 return UNDEFINED;
3759 }
3760
3774 template <typename Mat>
3775 constexpr auto threshold(Mat const&) noexcept
3776 -> std::enable_if_t<detail::IsTruncMat<Mat> && !IsMatWithSemiring<Mat>,
3777 typename Mat::scalar_type> {
3778 return detail::IsTruncMatHelper<Mat>::threshold;
3779 }
3780
3796 template <typename Mat>
3797 auto threshold(Mat const& x) noexcept
3798 -> std::enable_if_t<detail::IsTruncMat<Mat> && IsMatWithSemiring<Mat>,
3799 typename Mat::scalar_type> {
3800 return x.semiring()->threshold();
3801 }
3802 } // namespace matrix
3803
3805 // Boolean matrices - compile time semiring arithmetic
3807
3841
3864 constexpr bool operator()(bool x, bool y) const noexcept {
3865 return x || y;
3866 }
3867 };
3868
3891 constexpr bool operator()(bool x, bool y) const noexcept {
3892 return x & y;
3893 }
3894 };
3895
3905 struct BooleanOne {
3916 constexpr bool operator()() const noexcept {
3917 return true;
3918 }
3919 };
3920
3941 constexpr bool operator()() const noexcept {
3942 return false;
3943 }
3944 };
3945
3954 // The use of `int` rather than `bool` as the scalar type for dynamic
3955 // boolean matrices is intentional, because the bit iterators implemented in
3956 // std::vector<bool> entail a significant performance penalty.
3958 = DynamicMatrix<BooleanPlus, BooleanProd, BooleanZero, BooleanOne, int>;
3959
3971 template <size_t R, size_t C>
3975 BooleanOne,
3976 R,
3977 C,
3978 int>;
3979
3993 // FLS + JDM considered adding BMat8 and decided it wasn't a good idea.
3994 template <size_t R = 0, size_t C = R>
3995 using BMat
3996 = std::conditional_t<R == 0 || C == 0, DynamicBMat, StaticBMat<R, C>>;
3997
3998 namespace detail {
3999 template <typename T>
4000 struct IsBMatHelper : std::false_type {};
4001
4002 template <size_t R, size_t C>
4003 struct IsBMatHelper<StaticBMat<R, C>> : std::true_type {};
4004
4005 template <>
4006 struct IsBMatHelper<DynamicBMat> : std::true_type {};
4007
4008 template <typename T>
4009 struct BitSetCapacity {
4010 static constexpr size_t value = BitSet<1>::max_size();
4011 };
4012
4013 template <size_t R, size_t C>
4014 struct BitSetCapacity<StaticBMat<R, C>> {
4015 static_assert(R == C, "the number of rows and columns must be equal");
4016 static constexpr size_t value = R;
4017 };
4018 } // namespace detail
4019
4030 template <typename T>
4031 static constexpr bool IsBMat = detail::IsBMatHelper<T>::value;
4032
4033 namespace detail {
4034 // This function is required for exceptions and to_human_readable_repr, so
4035 // that if we encounter an entry of a matrix (Scalar type), then it can be
4036 // printed correctly. If we just did fmt::format("{}", val) and val ==
4037 // POSITIVE_INFINITY, but the type of val is, say, size_t, then this
4038 // wouldn't use the formatter for PositiveInfinity.
4039 //
4040 // Also in fmt v11.1.4 the custom formatter for POSITIVE_INFINITY and
4041 // NEGATIVE_INFINITY stopped working (and I wasn't able to figure out why)
4042 template <typename Scalar>
4043 std::string entry_repr(Scalar a) {
4044 if constexpr (std::is_same_v<Scalar, NegativeInfinity>
4045 || std::is_signed_v<Scalar>) {
4046 if (a == NEGATIVE_INFINITY) {
4047 return u8"-\u221E";
4048 }
4049 }
4050 if (a == POSITIVE_INFINITY) {
4051 return u8"+\u221E";
4052 }
4053 return fmt::format("{}", a);
4054 }
4055 } // namespace detail
4056
4057 namespace matrix {
4058
4074 //! but a matrix shouldn't contain values except \c 0 and \c 1.
4075 template <typename Mat>
4076 std::enable_if_t<IsBMat<Mat>> throw_if_bad_entry(Mat const& m) {
4077 using scalar_type = typename Mat::scalar_type;
4078 auto it = std::find_if_not(
4079 m.cbegin(), m.cend(), [](scalar_type x) { return x == 0 || x == 1; });
4080 if (it != m.cend()) {
4081 auto [r, c] = m.coords(it);
4083 "invalid entry, expected 0 or 1 but found {} in entry ({}, {})",
4084 detail::entry_repr(*it),
4085 r,
4086 c);
4087 }
4088 }
4089
4107 template <typename Mat>
4108 std::enable_if_t<IsBMat<Mat>>
4109 throw_if_bad_entry(Mat const&, typename Mat::scalar_type val) {
4110 if (val != 0 && val != 1) {
4111 LIBSEMIGROUPS_EXCEPTION("invalid entry, expected 0 or 1 but found {}",
4112 detail::entry_repr(val));
4113 }
4114 }
4115 } // namespace matrix
4116
4118 // Integer matrices - compile time semiring arithmetic
4120
4148
4160 //! \tparam Scalar the type of the entries in the matrix.
4161 template <typename Scalar>
4162 struct IntegerPlus {
4173 //! \exceptions
4174 //! \noexcept
4175 constexpr Scalar operator()(Scalar x, Scalar y) const noexcept {
4176 return x + y;
4177 }
4178 };
4179
4191 //! \tparam Scalar the type of the entries in the matrix.
4192 template <typename Scalar>
4193 struct IntegerProd {
4204 //! \exceptions
4205 //! \noexcept
4206 constexpr Scalar operator()(Scalar x, Scalar y) const noexcept {
4207 return x * y;
4208 }
4209 };
4210
4219 //! the additive identity of the integer semiring.
4220 template <typename Scalar>
4221 struct IntegerZero {
4229 //! \exceptions
4230 //! \noexcept
4231 constexpr Scalar operator()() const noexcept {
4232 return 0;
4233 }
4234 };
4235
4244 //! the multiplicative identity of the integer semiring.
4245 template <typename Scalar>
4246 struct IntegerOne {
4254 //! \exceptions
4255 //! \noexcept
4256 constexpr Scalar operator()() const noexcept {
4257 return 1;
4258 }
4259 };
4260
4271 template <typename Scalar>
4272 using DynamicIntMat = DynamicMatrix<IntegerPlus<Scalar>,
4276 Scalar>;
4277
4294 template <size_t R, size_t C, typename Scalar>
4299 R,
4300 C,
4301 Scalar>;
4302
4319 template <size_t R = 0, size_t C = R, typename Scalar = int>
4320 using IntMat = std::conditional_t<R == 0 || C == 0,
4323 namespace detail {
4324 template <typename T>
4325 struct IsIntMatHelper : std::false_type {};
4326
4327 template <size_t R, size_t C, typename Scalar>
4328 struct IsIntMatHelper<StaticIntMat<R, C, Scalar>> : std::true_type {};
4329
4330 template <typename Scalar>
4331 struct IsIntMatHelper<DynamicIntMat<Scalar>> : std::true_type {};
4332 } // namespace detail
4333
4344 template <typename T>
4345 static constexpr bool IsIntMat = detail::IsIntMatHelper<T>::value;
4346
4347 namespace matrix {
4361 //! \param x the matrix to check.
4362 template <typename Mat>
4363 std::enable_if_t<IsIntMat<Mat>> throw_if_bad_entry(Mat const& x) {
4364 using scalar_type = typename Mat::scalar_type;
4365 auto it = std::find_if(x.cbegin(), x.cend(), [](scalar_type val) {
4366 return val == POSITIVE_INFINITY || val == NEGATIVE_INFINITY;
4367 });
4368 if (it != x.cend()) {
4369 auto [r, c] = x.coords(it);
4371 "invalid entry, expected entries to be integers, "
4372 "but found {} in entry ({}, {})",
4373 detail::entry_repr(*it),
4374 r,
4375 c);
4376 }
4377 }
4378
4395 template <typename Mat>
4396 std::enable_if_t<IsIntMat<Mat>>
4397 throw_if_bad_entry(Mat const&, typename Mat::scalar_type val) {
4398 if (val == POSITIVE_INFINITY || val == NEGATIVE_INFINITY) {
4400 "invalid entry, expected entries to be integers, "
4401 "but found {}",
4402 detail::entry_repr(val));
4403 }
4404 }
4405 } // namespace matrix
4406
4408 // Max-plus matrices
4438
4462 // Static arithmetic
4463 template <typename Scalar>
4464 struct MaxPlusPlus {
4465 static_assert(std::is_signed<Scalar>::value,
4466 "MaxPlus requires a signed integer type as parameter!");
4477 //! \exceptions
4478 //! \noexcept
4479 Scalar operator()(Scalar x, Scalar y) const noexcept {
4480 if (x == NEGATIVE_INFINITY) {
4481 return y;
4482 } else if (y == NEGATIVE_INFINITY) {
4483 return x;
4484 }
4485 return std::max(x, y);
4486 }
4487 };
4488
4509 //! integer type).
4510 template <typename Scalar>
4511 struct MaxPlusProd {
4512 static_assert(std::is_signed<Scalar>::value,
4513 "MaxPlus requires a signed integer type as parameter!");
4524 //! \exceptions
4525 //! \noexcept
4526 Scalar operator()(Scalar x, Scalar y) const noexcept {
4527 if (x == NEGATIVE_INFINITY || y == NEGATIVE_INFINITY) {
4528 return NEGATIVE_INFINITY;
4529 }
4530 return x + y;
4531 }
4532 };
4533
4546 //! integer type).
4547 template <typename Scalar>
4548 struct MaxPlusZero {
4549 static_assert(std::is_signed<Scalar>::value,
4550 "MaxPlus requires a signed integer type as parameter!");
4558 //! \exceptions
4559 //! \noexcept
4560 constexpr Scalar operator()() const noexcept {
4561 return NEGATIVE_INFINITY;
4562 }
4563 };
4564
4575 template <typename Scalar>
4576 using DynamicMaxPlusMat = DynamicMatrix<MaxPlusPlus<Scalar>,
4580 Scalar>;
4581
4594 template <size_t R, size_t C, typename Scalar>
4599 R,
4600 C,
4601 Scalar>;
4602
4618 template <size_t R = 0, size_t C = R, typename Scalar = int>
4619 using MaxPlusMat = std::conditional_t<R == 0 || C == 0,
4622
4623 namespace detail {
4624 template <typename T>
4625 struct IsMaxPlusMatHelper : std::false_type {};
4626
4627 template <size_t R, size_t C, typename Scalar>
4628 struct IsMaxPlusMatHelper<StaticMaxPlusMat<R, C, Scalar>> : std::true_type {
4629 };
4630
4631 template <typename Scalar>
4632 struct IsMaxPlusMatHelper<DynamicMaxPlusMat<Scalar>> : std::true_type {};
4633 } // namespace detail
4634
4645 template <typename T>
4646 static constexpr bool IsMaxPlusMat = detail::IsMaxPlusMatHelper<T>::value;
4647
4648 namespace matrix {
4663 //! \ref POSITIVE_INFINITY.
4664 template <typename Mat>
4665 auto throw_if_bad_entry(Mat const& x)
4666 -> std::enable_if_t<IsMaxPlusMat<Mat>> {
4667 using scalar_type = typename Mat::scalar_type;
4668 auto it = std::find_if(x.cbegin(), x.cend(), [](scalar_type val) {
4669 return val == POSITIVE_INFINITY;
4670 });
4671 if (it != x.cend()) {
4672 auto [r, c] = x.coords(it);
4674 "invalid entry, expected entries to be integers or {} (= {}), "
4675 "but found {} (= {}) in entry ({}, {})",
4676 entry_repr(NEGATIVE_INFINITY),
4677 static_cast<scalar_type>(NEGATIVE_INFINITY),
4678 entry_repr(POSITIVE_INFINITY),
4679 static_cast<scalar_type>(POSITIVE_INFINITY),
4680 r,
4681 c);
4682 }
4683 }
4684
4700 template <typename Mat>
4701 std::enable_if_t<IsMaxPlusMat<Mat>>
4702 throw_if_bad_entry(Mat const&, typename Mat::scalar_type val) {
4703 if (val == POSITIVE_INFINITY) {
4704 using scalar_type = typename Mat::scalar_type;
4705 LIBSEMIGROUPS_EXCEPTION("invalid entry, expected entries to be "
4706 "integers or {} (= {}) but found {} (= {})",
4707 entry_repr(NEGATIVE_INFINITY),
4708 static_cast<scalar_type>(NEGATIVE_INFINITY),
4709 entry_repr(POSITIVE_INFINITY),
4710 static_cast<scalar_type>(POSITIVE_INFINITY));
4711 }
4712 }
4713 } // namespace matrix
4714
4716 // Min-plus matrices
4718
4747
4770 // Static arithmetic
4771 template <typename Scalar>
4772 struct MinPlusPlus {
4773 static_assert(std::is_signed<Scalar>::value,
4774 "MinPlus requires a signed integer type as parameter!");
4785 //! \exceptions
4786 //! \noexcept
4787 Scalar operator()(Scalar x, Scalar y) const noexcept {
4788 if (x == POSITIVE_INFINITY) {
4789 return y;
4790 } else if (y == POSITIVE_INFINITY) {
4791 return x;
4792 }
4793 return std::min(x, y);
4794 }
4795 };
4796
4817 //! integer type).
4818 template <typename Scalar>
4819 struct MinPlusProd {
4820 static_assert(std::is_signed<Scalar>::value,
4821 "MinPlus requires a signed integer type as parameter!");
4832 //! \exceptions
4833 //! \noexcept
4834 Scalar operator()(Scalar x, Scalar y) const noexcept {
4835 if (x == POSITIVE_INFINITY || y == POSITIVE_INFINITY) {
4836 return POSITIVE_INFINITY;
4837 }
4838 return x + y;
4839 }
4840 };
4841
4854 //! integer type).
4855 template <typename Scalar>
4856 struct MinPlusZero {
4857 static_assert(std::is_signed<Scalar>::value,
4858 "MinPlus requires a signed integer type as parameter!");
4866 //! \exceptions
4867 //! \noexcept
4868 constexpr Scalar operator()() const noexcept {
4869 return POSITIVE_INFINITY;
4870 }
4871 };
4872
4883 template <typename Scalar>
4884 using DynamicMinPlusMat = DynamicMatrix<MinPlusPlus<Scalar>,
4888 Scalar>;
4889
4902 template <size_t R, size_t C, typename Scalar>
4907 R,
4908 C,
4909 Scalar>;
4926 template <size_t R = 0, size_t C = R, typename Scalar = int>
4927 using MinPlusMat = std::conditional_t<R == 0 || C == 0,
4930
4931 namespace detail {
4932 template <typename T>
4933 struct IsMinPlusMatHelper : std::false_type {};
4934
4935 template <size_t R, size_t C, typename Scalar>
4936 struct IsMinPlusMatHelper<StaticMinPlusMat<R, C, Scalar>> : std::true_type {
4937 };
4938
4939 template <typename Scalar>
4940 struct IsMinPlusMatHelper<DynamicMinPlusMat<Scalar>> : std::true_type {};
4941 } // namespace detail
4942
4953 template <typename T>
4954 static constexpr bool IsMinPlusMat = detail::IsMinPlusMatHelper<T>::value;
4955
4956 namespace matrix {
4971 //! \ref NEGATIVE_INFINITY.
4972 template <typename Mat>
4973 std::enable_if_t<IsMinPlusMat<Mat>> throw_if_bad_entry(Mat const& x) {
4974 using scalar_type = typename Mat::scalar_type;
4975 auto it = std::find_if(x.cbegin(), x.cend(), [](scalar_type val) {
4976 return val == NEGATIVE_INFINITY;
4977 });
4978 if (it != x.cend()) {
4979 auto [r, c] = x.coords(it);
4981 "invalid entry, expected entries to be integers or {} (= {}), "
4982 "but found {} (= {}) in entry ({}, {})",
4983 entry_repr(POSITIVE_INFINITY),
4984 static_cast<scalar_type>(POSITIVE_INFINITY),
4985 entry_repr(NEGATIVE_INFINITY),
4986 static_cast<scalar_type>(NEGATIVE_INFINITY),
4987 r,
4988 c);
4989 }
4990 }
4991
5007 template <typename Mat>
5008 std::enable_if_t<IsMinPlusMat<Mat>>
5009 throw_if_bad_entry(Mat const&, typename Mat::scalar_type val) {
5010 if (val == NEGATIVE_INFINITY) {
5011 using scalar_type = typename Mat::scalar_type;
5012 LIBSEMIGROUPS_EXCEPTION("invalid entry, expected entries to be "
5013 "integers or {} (= {}) but found {} (= {})",
5014 entry_repr(POSITIVE_INFINITY),
5015 static_cast<scalar_type>(POSITIVE_INFINITY),
5016 entry_repr(NEGATIVE_INFINITY),
5017 static_cast<scalar_type>(NEGATIVE_INFINITY));
5018 }
5019 }
5020 } // namespace matrix
5021
5023 // Max-plus matrices with threshold
5025
5071
5096 //! integer type).
5097 template <size_t T, typename Scalar>
5098 struct MaxPlusTruncProd {
5099 static_assert(std::is_signed<Scalar>::value,
5100 "MaxPlus requires a signed integer type as parameter!");
5111 //! \exceptions
5112 //! \noexcept
5113 Scalar operator()(Scalar x, Scalar y) const noexcept {
5114 LIBSEMIGROUPS_ASSERT((x >= 0 && x <= static_cast<Scalar>(T))
5115 || x == NEGATIVE_INFINITY);
5116 LIBSEMIGROUPS_ASSERT((y >= 0 && y <= static_cast<Scalar>(T))
5117 || y == NEGATIVE_INFINITY);
5118 if (x == NEGATIVE_INFINITY || y == NEGATIVE_INFINITY) {
5119 return NEGATIVE_INFINITY;
5120 }
5121 return std::min(x + y, static_cast<Scalar>(T));
5122 }
5123 };
5124
5138 //! signed integer type (defaults to \c int).
5139 template <typename Scalar = int>
5140 class MaxPlusTruncSemiring {
5141 static_assert(std::is_signed<Scalar>::value,
5142 "MaxPlus requires a signed integer type as parameter!");
5143
5144 public:
5148 MaxPlusTruncSemiring() = delete;
5149
5153 MaxPlusTruncSemiring(MaxPlusTruncSemiring const&) noexcept = default;
5154
5158 MaxPlusTruncSemiring(MaxPlusTruncSemiring&&) noexcept = default;
5159
5163 MaxPlusTruncSemiring& operator=(MaxPlusTruncSemiring const&) noexcept
5164 = default;
5165
5169 MaxPlusTruncSemiring& operator=(MaxPlusTruncSemiring&&) noexcept = default;
5170
5171 ~MaxPlusTruncSemiring() = default;
5172
5181 //! \complexity
5182 //! Constant.
5183 explicit MaxPlusTruncSemiring(Scalar threshold) : _threshold(threshold) {
5184 if (threshold < 0) {
5185 LIBSEMIGROUPS_EXCEPTION("expected non-negative value, found {}",
5186 threshold);
5187 }
5188 }
5189
5198 //! \exceptions
5199 //! \noexcept
5200 static constexpr Scalar scalar_one() noexcept {
5201 return 0;
5202 }
5203
5212 //! \exceptions
5213 //! \noexcept
5214 static constexpr Scalar scalar_zero() noexcept {
5215 return NEGATIVE_INFINITY;
5216 }
5217
5240 //! \complexity
5241 //! Constant.
5242 Scalar product_no_checks(Scalar x, Scalar y) const noexcept {
5243 LIBSEMIGROUPS_ASSERT((x >= 0 && x <= _threshold)
5244 || x == NEGATIVE_INFINITY);
5245 LIBSEMIGROUPS_ASSERT((y >= 0 && y <= _threshold)
5246 || y == NEGATIVE_INFINITY);
5247 if (x == NEGATIVE_INFINITY || y == NEGATIVE_INFINITY) {
5248 return NEGATIVE_INFINITY;
5249 }
5250 return std::min(x + y, _threshold);
5251 }
5252
5275 //! \complexity
5276 //! Constant.
5277 Scalar plus_no_checks(Scalar x, Scalar y) const noexcept {
5278 LIBSEMIGROUPS_ASSERT((x >= 0 && x <= _threshold)
5279 || x == NEGATIVE_INFINITY);
5280 LIBSEMIGROUPS_ASSERT((y >= 0 && y <= _threshold)
5281 || y == NEGATIVE_INFINITY);
5282 if (x == NEGATIVE_INFINITY) {
5283 return y;
5284 } else if (y == NEGATIVE_INFINITY) {
5285 return x;
5286 }
5287 return std::max(x, y);
5288 }
5289
5300 //! \complexity
5301 //! Constant.
5302 Scalar threshold() const noexcept {
5303 return _threshold;
5304 }
5305
5306 public:
5307 Scalar const _threshold;
5308 };
5309
5322 template <size_t T, typename Scalar>
5323 using DynamicMaxPlusTruncMat = DynamicMatrix<MaxPlusPlus<Scalar>,
5327 Scalar>;
5328
5342 template <size_t T, size_t R, size_t C, typename Scalar>
5347 R,
5348 C,
5349 Scalar>;
5368 template <size_t T = 0, size_t R = 0, size_t C = R, typename Scalar = int>
5369 using MaxPlusTruncMat = std::conditional_t<
5370 R == 0 || C == 0,
5371 std::conditional_t<T == 0,
5372 DynamicMatrix<MaxPlusTruncSemiring<Scalar>, Scalar>,
5375
5376 namespace detail {
5377 template <typename T>
5378 struct IsMaxPlusTruncMatHelper : std::false_type {};
5379
5380 template <size_t T, size_t R, size_t C, typename Scalar>
5381 struct IsMaxPlusTruncMatHelper<StaticMaxPlusTruncMat<T, R, C, Scalar>>
5382 : std::true_type {
5383 static constexpr Scalar threshold = T;
5384 };
5385
5386 template <size_t T, typename Scalar>
5387 struct IsMaxPlusTruncMatHelper<DynamicMaxPlusTruncMat<T, Scalar>>
5388 : std::true_type {
5389 static constexpr Scalar threshold = T;
5390 };
5391
5392 template <typename Scalar>
5393 struct IsMaxPlusTruncMatHelper<
5394 DynamicMatrix<MaxPlusTruncSemiring<Scalar>, Scalar>> : std::true_type {
5395 static constexpr Scalar threshold = UNDEFINED;
5396 };
5397 } // namespace detail
5398
5410 template <typename T>
5411 static constexpr bool IsMaxPlusTruncMat
5412 = detail::IsMaxPlusTruncMatHelper<T>::value;
5413
5414 namespace detail {
5415 template <typename T>
5416 struct IsTruncMatHelper<T, std::enable_if_t<IsMaxPlusTruncMat<T>>>
5417 : std::true_type {
5418 static constexpr typename T::scalar_type threshold
5419 = IsMaxPlusTruncMatHelper<T>::threshold;
5420 };
5421 } // namespace detail
5422
5423 namespace matrix {
5441 //! (only applies to matrices with run time arithmetic).
5442 template <typename Mat>
5443 std::enable_if_t<IsMaxPlusTruncMat<Mat>> throw_if_bad_entry(Mat const& m) {
5444 // TODO(1) to tpp
5445 detail::throw_if_semiring_nullptr(m);
5446
5447 using scalar_type = typename Mat::scalar_type;
5448 scalar_type const t = matrix::threshold(m);
5449 auto it = std::find_if_not(m.cbegin(), m.cend(), [t](scalar_type x) {
5450 return x == NEGATIVE_INFINITY || (0 <= x && x <= t);
5451 });
5452 if (it != m.cend()) {
5453 auto [r, c] = m.coords(it);
5455 "invalid entry, expected values in {{0, 1, ..., {}, {} (= {})}} "
5456 "but found {} in entry ({}, {})",
5457 t,
5458 entry_repr(NEGATIVE_INFINITY),
5459 static_cast<scalar_type>(NEGATIVE_INFINITY),
5460 detail::entry_repr(*it),
5461 r,
5462 c);
5463 }
5464 }
5465
5485 template <typename Mat>
5486 std::enable_if_t<IsMaxPlusTruncMat<Mat>>
5487 throw_if_bad_entry(Mat const& m, typename Mat::scalar_type val) {
5488 detail::throw_if_semiring_nullptr(m);
5489 using scalar_type = typename Mat::scalar_type;
5490 scalar_type const t = matrix::threshold(m);
5491 if (val == POSITIVE_INFINITY || 0 > val || val > t) {
5493 "invalid entry, expected values in {{0, 1, ..., {}, -{} (= {})}} "
5494 "but found {}",
5495 t,
5496 entry_repr(NEGATIVE_INFINITY),
5497 static_cast<scalar_type>(NEGATIVE_INFINITY),
5498 detail::entry_repr(val));
5499 }
5500 }
5501 } // namespace matrix
5502
5504 // Min-plus matrices with threshold
5506
5552
5576 //! \tparam Scalar the type of the values in the semiring.
5577 template <size_t T, typename Scalar>
5578 struct MinPlusTruncProd {
5589 //! \exceptions
5590 //! \noexcept
5591 Scalar operator()(Scalar x, Scalar y) const noexcept {
5592 LIBSEMIGROUPS_ASSERT((x >= 0 && x <= static_cast<Scalar>(T))
5593 || x == POSITIVE_INFINITY);
5594 LIBSEMIGROUPS_ASSERT((y >= 0 && y <= static_cast<Scalar>(T))
5595 || y == POSITIVE_INFINITY);
5596 if (x == POSITIVE_INFINITY || y == POSITIVE_INFINITY) {
5597 return POSITIVE_INFINITY;
5598 }
5599 return std::min(x + y, static_cast<Scalar>(T));
5600 }
5601 };
5602
5615 //! integral type.
5616 template <typename Scalar = int>
5617 class MinPlusTruncSemiring {
5618 static_assert(std::is_integral<Scalar>::value,
5619 "MinPlus requires an integral type as parameter!");
5620
5621 public:
5625 MinPlusTruncSemiring() = delete;
5626
5630 MinPlusTruncSemiring(MinPlusTruncSemiring const&) noexcept = default;
5631
5635 MinPlusTruncSemiring(MinPlusTruncSemiring&&) noexcept = default;
5636
5640 MinPlusTruncSemiring& operator=(MinPlusTruncSemiring const&) noexcept
5641 = default;
5642
5646 MinPlusTruncSemiring& operator=(MinPlusTruncSemiring&&) noexcept = default;
5647
5656 //! \complexity
5657 //! Constant.
5658 explicit MinPlusTruncSemiring(Scalar threshold) : _threshold(threshold) {
5660 LIBSEMIGROUPS_EXCEPTION("expected non-negative value, found {}",
5661 threshold);
5662 }
5663 }
5664
5673 //! \exceptions
5674 //! \noexcept
5675 static constexpr Scalar scalar_one() noexcept {
5676 return 0;
5677 }
5678
5688 //! \noexcept
5689 // TODO(1) These mem fns (one and zero) aren't needed?
5690 static constexpr Scalar scalar_zero() noexcept {
5691 return POSITIVE_INFINITY;
5692 }
5693
5716 //! \complexity
5717 //! Constant.
5718 Scalar product_no_checks(Scalar x, Scalar y) const noexcept {
5719 LIBSEMIGROUPS_ASSERT((x >= 0 && x <= _threshold)
5720 || x == POSITIVE_INFINITY);
5721 LIBSEMIGROUPS_ASSERT((y >= 0 && y <= _threshold)
5722 || y == POSITIVE_INFINITY);
5723 if (x == POSITIVE_INFINITY || y == POSITIVE_INFINITY) {
5724 return POSITIVE_INFINITY;
5725 }
5726 return std::min(x + y, _threshold);
5727 }
5728
5751 //! \complexity
5752 //! Constant.
5753 Scalar plus_no_checks(Scalar x, Scalar y) const noexcept {
5754 LIBSEMIGROUPS_ASSERT((x >= 0 && x <= _threshold)
5755 || x == POSITIVE_INFINITY);
5756 LIBSEMIGROUPS_ASSERT((y >= 0 && y <= _threshold)
5757 || y == POSITIVE_INFINITY);
5758 if (x == POSITIVE_INFINITY) {
5759 return y;
5760 } else if (y == POSITIVE_INFINITY) {
5761 return x;
5762 }
5763 return std::min(x, y);
5764 }
5765
5776 //! \complexity
5777 //! Constant.
5778 Scalar threshold() const noexcept {
5779 return _threshold;
5780 }
5781
5782 public:
5783 Scalar const _threshold;
5784 };
5785
5798 template <size_t T, typename Scalar>
5799 using DynamicMinPlusTruncMat = DynamicMatrix<MinPlusPlus<Scalar>,
5803 Scalar>;
5804
5818 template <size_t T, size_t R, size_t C, typename Scalar>
5823 R,
5824 C,
5825 Scalar>;
5826
5845 template <size_t T = 0, size_t R = 0, size_t C = R, typename Scalar = int>
5846 using MinPlusTruncMat = std::conditional_t<
5847 R == 0 || C == 0,
5848 std::conditional_t<T == 0,
5849 DynamicMatrix<MinPlusTruncSemiring<Scalar>, Scalar>,
5852
5853 namespace detail {
5854 template <typename T>
5855 struct IsMinPlusTruncMatHelper : std::false_type {};
5856
5857 template <size_t T, size_t R, size_t C, typename Scalar>
5858 struct IsMinPlusTruncMatHelper<StaticMinPlusTruncMat<T, R, C, Scalar>>
5859 : std::true_type {
5860 static constexpr Scalar threshold = T;
5861 };
5862
5863 template <size_t T, typename Scalar>
5864 struct IsMinPlusTruncMatHelper<DynamicMinPlusTruncMat<T, Scalar>>
5865 : std::true_type {
5866 static constexpr Scalar threshold = T;
5867 };
5868
5869 template <typename Scalar>
5870 struct IsMinPlusTruncMatHelper<
5871 DynamicMatrix<MinPlusTruncSemiring<Scalar>, Scalar>> : std::true_type {
5872 static constexpr Scalar threshold = UNDEFINED;
5873 };
5874 } // namespace detail
5875
5887 template <typename T>
5888 static constexpr bool IsMinPlusTruncMat
5889 = detail::IsMinPlusTruncMatHelper<T>::value;
5890
5891 namespace detail {
5892 template <typename T>
5893 struct IsTruncMatHelper<T, std::enable_if_t<IsMinPlusTruncMat<T>>>
5894 : std::true_type {
5895 static constexpr typename T::scalar_type threshold
5896 = IsMinPlusTruncMatHelper<T>::threshold;
5897 };
5898 } // namespace detail
5899
5900 namespace matrix {
5919 // TODO(1) to tpp
5920 template <typename Mat>
5921 std::enable_if_t<IsMinPlusTruncMat<Mat>> throw_if_bad_entry(Mat const& m) {
5922 // Check that the semiring pointer isn't the nullptr if it shouldn't be
5923 detail::throw_if_semiring_nullptr(m);
5924
5925 using scalar_type = typename Mat::scalar_type;
5926 scalar_type const t = matrix::threshold(m);
5927 auto it = std::find_if_not(m.cbegin(), m.cend(), [t](scalar_type x) {
5928 return x == POSITIVE_INFINITY || (0 <= x && x <= t);
5929 });
5930 if (it != m.cend()) {
5931 uint64_t r, c;
5932 std::tie(r, c) = m.coords(it);
5933
5935 "invalid entry, expected values in {{0, 1, ..., {}, {} (= {})}} "
5936 "but found {} in entry ({}, {})",
5937 t,
5938 detail::entry_repr(POSITIVE_INFINITY),
5939 static_cast<scalar_type>(POSITIVE_INFINITY),
5940 detail::entry_repr(*it),
5941 r,
5942 c);
5943 }
5944 }
5945
5965 template <typename Mat>
5966 std::enable_if_t<IsMinPlusTruncMat<Mat>>
5967 throw_if_bad_entry(Mat const& m, typename Mat::scalar_type val) {
5968 detail::throw_if_semiring_nullptr(m);
5969
5970 using scalar_type = typename Mat::scalar_type;
5971 scalar_type const t = matrix::threshold(m);
5972 if (!(val == POSITIVE_INFINITY || (0 <= val && val <= t))) {
5974 "invalid entry, expected values in {{0, 1, ..., {}, {} (= {})}} "
5975 "but found {}",
5976 t,
5977 detail::entry_repr(POSITIVE_INFINITY),
5978 static_cast<scalar_type>(POSITIVE_INFINITY),
5979 detail::entry_repr(val));
5980 }
5981 }
5982 } // namespace matrix
5983
5985 // NTP matrices
5987
6040
6041 namespace detail {
6042 template <size_t T, size_t P, typename Scalar>
6043 constexpr Scalar thresholdperiod(Scalar x) noexcept {
6044 if (x > T) {
6045 return T + (x - T) % P;
6046 }
6047 return x;
6048 }
6049
6050 template <typename Scalar>
6051 inline Scalar thresholdperiod(Scalar x,
6052 Scalar threshold,
6053 Scalar period) noexcept {
6054 if (x > threshold) {
6055 return threshold + (x - threshold) % period;
6056 }
6057 return x;
6058 }
6059 } // namespace detail
6060
6083 // Static arithmetic
6084 template <size_t T, size_t P, typename Scalar>
6085 struct NTPPlus {
6095 //! \exceptions
6096 //! \noexcept
6097 constexpr Scalar operator()(Scalar x, Scalar y) const noexcept {
6098 return detail::thresholdperiod<T, P>(x + y);
6099 }
6100 };
6101
6124 //! \tparam Scalar the type of the values in the semiring.
6125 template <size_t T, size_t P, typename Scalar>
6126 struct NTPProd {
6138 //! \exceptions
6139 //! \noexcept
6140 constexpr Scalar operator()(Scalar x, Scalar y) const noexcept {
6141 return detail::thresholdperiod<T, P>(x * y);
6142 }
6143 };
6144
6158 // Dynamic arithmetic
6159 template <typename Scalar = size_t>
6160 class NTPSemiring {
6161 public:
6165 // Deleted to avoid uninitialised values of period and threshold.
6166 NTPSemiring() = delete;
6167
6171 NTPSemiring(NTPSemiring const&) = default;
6172
6176 NTPSemiring(NTPSemiring&&) = default;
6177
6181 NTPSemiring& operator=(NTPSemiring const&) = default;
6182
6186 NTPSemiring& operator=(NTPSemiring&&) = default;
6187
6188 ~NTPSemiring() = default;
6189
6200 //! \complexity
6201 //! Constant.
6202 NTPSemiring(Scalar t, Scalar p) : _period(p), _threshold(t) {
6203 if constexpr (std::is_signed<Scalar>::value) {
6204 if (t < 0) {
6206 "expected non-negative value for 1st argument, found {}", t);
6207 }
6208 }
6209 if (p <= 0) {
6211 "expected positive value for 2nd argument, found {}", p);
6212 }
6213 }
6214
6223 //! \exceptions
6224 //! \noexcept
6225 static constexpr Scalar scalar_one() noexcept {
6226 return 1;
6227 }
6228
6239 //! \complexity
6240 //! Constant.
6241 static constexpr Scalar scalar_zero() noexcept {
6242 return 0;
6243 }
6244
6267 //! \complexity
6268 //! Constant.
6269 Scalar product_no_checks(Scalar x, Scalar y) const noexcept {
6270 LIBSEMIGROUPS_ASSERT(x >= 0 && x <= _period + _threshold - 1);
6271 LIBSEMIGROUPS_ASSERT(y >= 0 && y <= _period + _threshold - 1);
6272 return detail::thresholdperiod(x * y, _threshold, _period);
6273 }
6274
6297 //! \complexity
6298 //! Constant.
6299 Scalar plus_no_checks(Scalar x, Scalar y) const noexcept {
6300 LIBSEMIGROUPS_ASSERT(x >= 0 && x <= _period + _threshold - 1);
6301 LIBSEMIGROUPS_ASSERT(y >= 0 && y <= _period + _threshold - 1);
6302 return detail::thresholdperiod(x + y, _threshold, _period);
6303 }
6304
6315 //! \complexity
6316 //! Constant.
6317 Scalar threshold() const noexcept {
6318 return _threshold;
6319 }
6320
6331 //! \complexity
6332 //! Constant.
6333 Scalar period() const noexcept {
6334 return _period;
6335 }
6336
6337 private:
6338 Scalar _period;
6339 Scalar _threshold;
6340 };
6341
6352 template <typename Scalar>
6353 using DynamicNTPMatWithSemiring = DynamicMatrix<NTPSemiring<Scalar>, Scalar>;
6354
6368 template <size_t T, size_t P, typename Scalar>
6369 using DynamicNTPMatWithoutSemiring = DynamicMatrix<NTPPlus<T, P, Scalar>,
6373 Scalar>;
6374
6394 template <size_t T, size_t P, size_t R, size_t C, typename Scalar>
6399 R,
6400 C,
6401 Scalar>;
6402
6425 template <size_t T = 0,
6426 size_t P = 0,
6427 size_t R = 0,
6428 size_t C = R,
6429 typename Scalar = size_t>
6430 using NTPMat = std::conditional_t<
6431 R == 0 || C == 0,
6432 std::conditional_t<T == 0 && P == 0,
6436
6437 namespace detail {
6438 template <typename T>
6439 struct IsNTPMatHelper : std::false_type {};
6440
6441 template <typename Scalar>
6442 struct IsNTPMatHelper<DynamicNTPMatWithSemiring<Scalar>> : std::true_type {
6443 static constexpr Scalar threshold = UNDEFINED;
6444 static constexpr Scalar period = UNDEFINED;
6445 };
6446
6447 template <size_t T, size_t P, typename Scalar>
6448 struct IsNTPMatHelper<DynamicNTPMatWithoutSemiring<T, P, Scalar>>
6449 : std::true_type {
6450 static constexpr Scalar threshold = T;
6451 static constexpr Scalar period = P;
6452 };
6453
6454 template <size_t T, size_t P, size_t R, size_t C, typename Scalar>
6455 struct IsNTPMatHelper<StaticNTPMat<T, P, R, C, Scalar>> : std::true_type {
6456 static constexpr Scalar threshold = T;
6457 static constexpr Scalar period = P;
6458 };
6459 } // namespace detail
6460
6472 template <typename U>
6473 static constexpr bool IsNTPMat = detail::IsNTPMatHelper<U>::value;
6474
6475 namespace detail {
6476 template <typename T>
6477 struct IsTruncMatHelper<T, std::enable_if_t<IsNTPMat<T>>> : std::true_type {
6478 static constexpr typename T::scalar_type threshold
6479 = IsNTPMatHelper<T>::threshold;
6480 static constexpr typename T::scalar_type period
6481 = IsNTPMatHelper<T>::period;
6482 };
6483
6484 } // namespace detail
6485
6486 namespace matrix {
6507 //! \noexcept
6508 template <size_t T, size_t P, size_t R, size_t C, typename Scalar>
6509 constexpr Scalar period(StaticNTPMat<T, P, R, C, Scalar> const&) noexcept {
6510 return P;
6511 }
6512
6530 template <size_t T, size_t P, typename Scalar>
6531 constexpr Scalar
6533 return P;
6534 }
6535
6550 //! \noexcept
6551 template <typename Scalar>
6552 Scalar period(DynamicNTPMatWithSemiring<Scalar> const& x) noexcept {
6553 return x.semiring()->period();
6554 }
6555 } // namespace matrix
6556
6557 namespace matrix {
6577 //! defined (only applies to matrices with run time arithmetic).
6578 template <typename Mat>
6579 std::enable_if_t<IsNTPMat<Mat>> throw_if_bad_entry(Mat const& m) {
6580 detail::throw_if_semiring_nullptr(m);
6581
6582 using scalar_type = typename Mat::scalar_type;
6583 scalar_type const t = matrix::threshold(m);
6584 scalar_type const p = matrix::period(m);
6585 auto it = std::find_if_not(m.cbegin(), m.cend(), [t, p](scalar_type x) {
6586 return (0 <= x && x < p + t);
6587 });
6588 if (it != m.cend()) {
6589 uint64_t r, c;
6590 std::tie(r, c) = m.coords(it);
6591
6593 "invalid entry, expected values in {{0, 1, ..., {}}}, but "
6594 "found {} in entry ({}, {})",
6595 p + t,
6596 detail::entry_repr(*it),
6597 r,
6598 c);
6599 }
6600 }
6601
6623 template <typename Mat>
6624 std::enable_if_t<IsNTPMat<Mat>>
6625 throw_if_bad_entry(Mat const& m, typename Mat::scalar_type val) {
6626 detail::throw_if_semiring_nullptr(m);
6627 using scalar_type = typename Mat::scalar_type;
6628 scalar_type const t = matrix::threshold(m);
6629 scalar_type const p = matrix::period(m);
6630 if (val < 0 || val >= p + t) {
6632 "invalid entry, expected values in {{0, 1, ..., {}}}, but "
6633 "found {}",
6634 p + t,
6635 detail::entry_repr(val));
6636 }
6637 }
6638 } // namespace matrix
6639
6641 // Projective max-plus matrices
6643
6644 namespace detail {
6645 template <typename T>
6646 class ProjMaxPlusMat : MatrixPolymorphicBase {
6647 public:
6648 using scalar_type = typename T::scalar_type;
6649 using scalar_reference = typename T::scalar_reference;
6650 using scalar_const_reference = typename T::scalar_const_reference;
6651 using semiring_type = void;
6652
6653 using container_type = typename T::container_type;
6654 using iterator = typename T::iterator;
6655 using const_iterator = typename T::const_iterator;
6656
6657 using underlying_matrix_type = T;
6658
6659 using RowView = typename T::RowView;
6660
6661 // Note that Rows are never normalised, and that's why we use the
6662 // underlying matrix Row type and not 1 x n ProjMaxPlusMat's instead
6663 // (since these will be normalised according to their entries, and
6664 // this might not correspond to the normalised entries of the matrix).
6665 using Row = typename T::Row;
6666
6667 scalar_type scalar_one() const noexcept {
6668 return _underlying_mat.scalar_one();
6669 }
6670
6671 scalar_type scalar_zero() const noexcept {
6672 return _underlying_mat.scalar_zero();
6673 }
6674
6676 // ProjMaxPlusMat - Constructors + destructor - public
6678
6679 ProjMaxPlusMat() : _is_normalized(false), _underlying_mat() {}
6680 ProjMaxPlusMat(ProjMaxPlusMat const&) = default;
6681 ProjMaxPlusMat(ProjMaxPlusMat&&) = default;
6682 ProjMaxPlusMat& operator=(ProjMaxPlusMat const&) = default;
6683 ProjMaxPlusMat& operator=(ProjMaxPlusMat&&) = default;
6684
6685 ProjMaxPlusMat(size_t r, size_t c)
6686 : _is_normalized(false), _underlying_mat(r, c) {}
6687
6688 // TODO(1) other missing constructors
6689 ProjMaxPlusMat(
6690 typename underlying_matrix_type::semiring_type const* semiring,
6691 size_t r,
6692 size_t c)
6693 : _is_normalized(false), _underlying_mat(semiring, r, c) {}
6694
6695 explicit ProjMaxPlusMat(std::vector<std::vector<scalar_type>> const& m)
6696 : _is_normalized(false), _underlying_mat(m) {
6697 normalize();
6698 }
6699
6700 ProjMaxPlusMat(
6701 std::initializer_list<std::initializer_list<scalar_type>> const& m)
6702 : ProjMaxPlusMat(
6703 std::vector<std::vector<scalar_type>>(m.begin(), m.end())) {}
6704
6705 ~ProjMaxPlusMat() = default;
6706
6707 ProjMaxPlusMat one() const {
6708 auto result = ProjMaxPlusMat(_underlying_mat.one());
6709 return result;
6710 }
6711
6712 static ProjMaxPlusMat one(size_t n) {
6713 return ProjMaxPlusMat(T::one(n));
6714 }
6715
6717 // Comparison operators
6719
6720 bool operator==(ProjMaxPlusMat const& that) const {
6721 normalize();
6722 that.normalize();
6723 return _underlying_mat == that._underlying_mat;
6724 }
6725
6726 bool operator!=(ProjMaxPlusMat const& that) const {
6727 return !(_underlying_mat == that._underlying_mat);
6728 }
6729
6730 bool operator<(ProjMaxPlusMat const& that) const {
6731 normalize();
6732 that.normalize();
6733 return _underlying_mat < that._underlying_mat;
6734 }
6735
6736 bool operator>(ProjMaxPlusMat const& that) const {
6737 return that < *this;
6738 }
6739
6740 template <typename Thing>
6741 bool operator>=(Thing const& that) const {
6742 static_assert(IsMatrix<Thing> || std::is_same_v<Thing, RowView>);
6743 return that < *this || that == *this;
6744 }
6745
6746 // not noexcept because operator< isn't
6747 template <typename Thing>
6748 bool operator<=(Thing const& that) const {
6749 static_assert(IsMatrix<Thing> || std::is_same_v<Thing, RowView>);
6750 return *this < that || that == *this;
6751 }
6752
6754 // Attributes
6756
6757 scalar_reference operator()(size_t r, size_t c) {
6758 // to ensure the returned value is normalised
6759 normalize();
6760 // to ensure that the matrix is renormalised if the returned scalar is
6761 // assigned.
6762 _is_normalized = false;
6763 return _underlying_mat(r, c);
6764 }
6765
6766 scalar_reference at(size_t r, size_t c) {
6767 matrix::throw_if_bad_coords(*this, r, c);
6768 return this->operator()(r, c);
6769 }
6770
6771 scalar_const_reference operator()(size_t r, size_t c) const {
6772 normalize();
6773 return _underlying_mat(r, c);
6774 }
6775
6776 scalar_const_reference at(size_t r, size_t c) const {
6777 matrix::throw_if_bad_coords(*this, r, c);
6778 return this->operator()(r, c);
6779 }
6780
6781 size_t number_of_rows() const noexcept {
6782 return _underlying_mat.number_of_rows();
6783 }
6784
6785 size_t number_of_cols() const noexcept {
6786 return _underlying_mat.number_of_cols();
6787 }
6788
6789 size_t hash_value() const {
6790 normalize();
6791 return Hash<T>()(_underlying_mat);
6792 }
6793
6795 // Arithmetic operators - in-place
6797
6798 void product_inplace_no_checks(ProjMaxPlusMat const& A,
6799 ProjMaxPlusMat const& B) {
6800 _underlying_mat.product_inplace_no_checks(A._underlying_mat,
6801 B._underlying_mat);
6802 normalize(true); // force normalize
6803 }
6804
6805 void operator+=(ProjMaxPlusMat const& that) {
6806 _underlying_mat += that._underlying_mat;
6807 normalize(true); // force normalize
6808 }
6809
6810 void operator*=(scalar_type a) {
6811 _underlying_mat *= a;
6812 normalize(true); // force normalize
6813 }
6814
6815 void operator+=(scalar_type a) {
6816 _underlying_mat += a;
6817 normalize(true); // force normalize
6818 }
6819
6820 ProjMaxPlusMat operator*(scalar_type a) const {
6821 ProjMaxPlusMat result(*this);
6822 result *= a;
6823 return result;
6824 }
6825
6826 ProjMaxPlusMat operator+(scalar_type a) const {
6827 ProjMaxPlusMat result(*this);
6828 result += a;
6829 return result;
6830 }
6831
6833 // Arithmetic operators - not in-place
6835
6836 ProjMaxPlusMat operator+(ProjMaxPlusMat const& that) const {
6837 return ProjMaxPlusMat(_underlying_mat + that._underlying_mat);
6838 }
6839
6840 ProjMaxPlusMat operator*(ProjMaxPlusMat const& that) const {
6841 return ProjMaxPlusMat(_underlying_mat * that._underlying_mat);
6842 }
6843
6845 // Iterators
6847
6848 // The following should probably be commented out because I can't
6849 // currently think how to ensure that the matrix is normalised if it's
6850 // changed this way.
6851
6852 iterator begin() noexcept {
6853 // to ensure the returned value is normalised
6854 normalize();
6855 // to ensure that the matrix is renormalised if the returned scalar is
6856 // assigned.
6857 _is_normalized = false;
6858 return _underlying_mat.begin();
6859 }
6860
6861 iterator end() noexcept {
6862 // to ensure the returned value is normalised
6863 normalize();
6864 // to ensure that the matrix is renormalised if the returned scalar is
6865 // assigned.
6866 _is_normalized = false;
6867 return _underlying_mat.end();
6868 }
6869
6870 const_iterator begin() const noexcept {
6871 normalize();
6872 return _underlying_mat.begin();
6873 }
6874
6875 const_iterator end() const noexcept {
6876 normalize();
6877 return _underlying_mat.end();
6878 }
6879
6880 const_iterator cbegin() const noexcept {
6881 normalize();
6882 return _underlying_mat.cbegin();
6883 }
6884
6885 const_iterator cend() const noexcept {
6886 normalize();
6887 return _underlying_mat.cend();
6888 }
6889
6891 // Modifiers
6893
6894 void swap(ProjMaxPlusMat& that) noexcept {
6895 std::swap(_underlying_mat, that._underlying_mat);
6896 }
6897
6898 void transpose() noexcept {
6899 _underlying_mat.transpose();
6900 }
6901
6902 void transpose_no_checks() noexcept {
6903 _underlying_mat.transpose_no_checks();
6904 }
6905
6907 // Rows
6909
6910 RowView row(size_t i) const {
6911 normalize();
6912 return _underlying_mat.row(i);
6913 }
6914
6915 template <typename C>
6916 void rows(C& x) const {
6917 normalize();
6918 return _underlying_mat.rows(x);
6919 }
6920
6922 // Friend functions
6924
6925 friend std::ostream& operator<<(std::ostream& os,
6926 ProjMaxPlusMat const& x) {
6927 x.normalize();
6928 os << detail::to_string(x._underlying_mat);
6929 return os;
6930 }
6931
6932 T const& underlying_matrix() const noexcept {
6933 normalize();
6934 return _underlying_mat;
6935 }
6936
6937 private:
6938 explicit ProjMaxPlusMat(T&& mat)
6939 : _is_normalized(false), _underlying_mat(std::move(mat)) {
6940 normalize();
6941 }
6942
6943 void normalize(bool force = false) const {
6944 if ((_is_normalized && !force)
6945 || (_underlying_mat.number_of_rows() == 0)
6946 || (_underlying_mat.number_of_cols() == 0)) {
6947 _is_normalized = true;
6948 return;
6949 }
6950 scalar_type const n = *std::max_element(_underlying_mat.cbegin(),
6951 _underlying_mat.cend());
6952 std::for_each(_underlying_mat.begin(),
6953 _underlying_mat.end(),
6954 [&n](scalar_type& s) {
6955 if (s != NEGATIVE_INFINITY) {
6956 s -= n;
6957 }
6958 });
6959 _is_normalized = true;
6960 }
6961
6962 mutable bool _is_normalized;
6963 mutable T _underlying_mat;
6964 };
6965 } // namespace detail
6966
7011
7025 template <size_t R, size_t C, typename Scalar>
7027 = detail::ProjMaxPlusMat<StaticMaxPlusMat<R, C, Scalar>>;
7028
7040 template <typename Scalar>
7042 = detail::ProjMaxPlusMat<DynamicMaxPlusMat<Scalar>>;
7043
7058 template <size_t R = 0, size_t C = R, typename Scalar = int>
7059 using ProjMaxPlusMat = std::conditional_t<R == 0 || C == 0,
7062
7063 namespace detail {
7064 template <typename T>
7065 struct IsProjMaxPlusMatHelper : std::false_type {};
7066
7067 template <size_t R, size_t C, typename Scalar>
7068 struct IsProjMaxPlusMatHelper<StaticProjMaxPlusMat<R, C, Scalar>>
7069 : std::true_type {};
7070
7071 template <typename Scalar>
7072 struct IsProjMaxPlusMatHelper<DynamicProjMaxPlusMat<Scalar>>
7073 : std::true_type {};
7074 } // namespace detail
7075
7087 template <typename T>
7088 static constexpr bool IsProjMaxPlusMat
7089 = detail::IsProjMaxPlusMatHelper<T>::value;
7090
7091 namespace matrix {
7092 // \ingroup projmaxplus_group
7093 //
7107 //! \throws LibsemigroupsException if
7108 //! `throw_if_bad_entry(x.underlying_matrix())` throws.
7109 template <typename Mat>
7110 constexpr std::enable_if_t<IsProjMaxPlusMat<Mat>>
7111 throw_if_bad_entry(Mat const& x) {
7112 throw_if_bad_entry(x.underlying_matrix());
7113 }
7114
7115 // \ingroup projmaxplus_group
7116 //
7130 //! \throws LibsemigroupsException if
7131 //! `throw_if_bad_entry(x.underlying_matrix(), val)` throws.
7132 template <typename Mat>
7133 constexpr std::enable_if_t<IsProjMaxPlusMat<Mat>>
7134 throw_if_bad_entry(Mat const& x, typename Mat::scalar_type val) {
7135 throw_if_bad_entry(x.underlying_matrix(), val);
7136 }
7137
7139 // Matrix helpers - pow
7141
7175 //! \endcode
7176 // TODO(1) pow_no_checks
7177 // TODO(2) version that changes x in-place
7178 template <typename Mat>
7179 Mat pow(Mat const& x, typename Mat::scalar_type e) {
7180 using scalar_type = typename Mat::scalar_type;
7181
7182 if constexpr (std::is_signed<scalar_type>::value) {
7183 if (e < 0) {
7185 "negative exponent, expected value >= 0, found {}", e);
7186 }
7187 }
7188
7190
7191 typename Mat::semiring_type const* sr = nullptr;
7192
7193 if constexpr (IsMatWithSemiring<Mat>) {
7194 sr = x.semiring();
7195 }
7196
7197 if (e == 0) {
7198 return x.one();
7199 }
7200
7201 auto y = Mat(x);
7202 if (e == 1) {
7203 return y;
7204 }
7205 auto z = (e % 2 == 0 ? x.one() : y);
7206
7207 Mat tmp(sr, x.number_of_rows(), x.number_of_cols());
7208 while (e > 1) {
7209 tmp.product_inplace_no_checks(y, y);
7210 std::swap(y, tmp);
7211 e /= 2;
7212 if (e % 2 == 1) {
7213 tmp.product_inplace_no_checks(z, y);
7214 std::swap(z, tmp);
7215 }
7216 }
7217 return z;
7218 }
7219
7221 // Matrix helpers - rows
7223
7240 //!
7241 //! \complexity
7242 //! \f$O(m)\f$ where \f$m\f$ is the number of rows in the matrix \p x.
7243 template <typename Mat, typename = std::enable_if_t<IsDynamicMatrix<Mat>>>
7246 x.rows(container);
7247 return container;
7248 }
7249
7268 //! \complexity
7269 //! \f$O(m)\f$ where \f$m\f$ is the number of rows in the matrix \p x.
7270 template <typename Mat, typename = std::enable_if_t<IsStaticMatrix<Mat>>>
7271 detail::StaticVector1<typename Mat::RowView, Mat::nr_rows>
7272 rows(Mat const& x) {
7273 detail::StaticVector1<typename Mat::RowView, Mat::nr_rows> container;
7274 x.rows(container);
7275 return container;
7276 }
7277
7279 // Matrix helpers - bitset_rows
7281
7282 // The main function
7311 //! \complexity
7312 //! \f$O(mn)\f$ where \f$m\f$ is the number of rows in `views` and
7313 //! and \f$n\f$ is the number of columns in any vector in `views`.
7314 template <typename Mat, size_t R, size_t C, typename Container>
7315 void bitset_rows(Container&& views,
7316 detail::StaticVector1<BitSet<C>, R>& result) {
7317 using RowView = typename Mat::RowView;
7318 using value_type = typename std::decay_t<Container>::value_type;
7319 // std::vector<bool> is used as value_type in the benchmarks
7320 static_assert(IsBMat<Mat>, "IsBMat<Mat> must be true!");
7323 "Container::value_type must equal Mat::RowView or "
7324 "std::vector<bool>!!");
7325 static_assert(R <= BitSet<1>::max_size(),
7326 "R must be at most BitSet<1>::max_size()!");
7327 static_assert(C <= BitSet<1>::max_size(),
7328 "C must be at most BitSet<1>::max_size()!");
7329 LIBSEMIGROUPS_ASSERT(views.size() <= R);
7330 LIBSEMIGROUPS_ASSERT(views.empty() || views[0].size() <= C);
7331 for (auto const& v : views) {
7332 result.emplace_back(v.cbegin(), v.cend());
7333 }
7334 }
7335
7365 //! \complexity
7366 //! \f$O(mn)\f$ where \f$m\f$ is the number of rows in \p views and
7367 //! and \f$n\f$ is the number of columns in any vector in \p views.
7368 template <typename Mat, size_t R, size_t C, typename Container>
7369 auto bitset_rows(Container&& views) {
7370 using RowView = typename Mat::RowView;
7371 using value_type = typename std::decay_t<Container>::value_type;
7372 static_assert(IsBMat<Mat>, "IsBMat<Mat> must be true!");
7375 "Container::value_type must equal Mat::RowView or "
7376 "std::vector<bool>!!");
7377 static_assert(R <= BitSet<1>::max_size(),
7378 "R must be at most BitSet<1>::max_size()!");
7379 static_assert(C <= BitSet<1>::max_size(),
7380 "C must be at most BitSet<1>::max_size()!");
7381 LIBSEMIGROUPS_ASSERT(views.size() <= R);
7382 LIBSEMIGROUPS_ASSERT(views.empty() || views[0].size() <= C);
7383 detail::StaticVector1<BitSet<C>, R> result;
7385 return result;
7386 }
7387
7388 // Helper
7414 //! \complexity
7415 //! \f$O(mn)\f$ where \f$m\f$ is the number of rows in \p x and and
7416 //! \f$n\f$ is the number of columns in \p x.
7417 template <typename Mat, size_t R, size_t C>
7418 void bitset_rows(Mat const& x,
7419 detail::StaticVector1<BitSet<C>, R>& result) {
7420 static_assert(IsBMat<Mat>, "IsBMat<Mat> must be true!");
7421 static_assert(R <= BitSet<1>::max_size(),
7422 "R must be at most BitSet<1>::max_size()!");
7423 static_assert(C <= BitSet<1>::max_size(),
7424 "C must be at most BitSet<1>::max_size()!");
7425 LIBSEMIGROUPS_ASSERT(x.number_of_cols() <= C);
7426 LIBSEMIGROUPS_ASSERT(x.number_of_rows() <= R);
7427 bitset_rows<Mat>(std::move(rows(x)), result);
7428 }
7429
7430 // Helper
7446 //! \complexity
7447 //! \f$O(mn)\f$ where \f$m\f$ is the number of rows in \p x and
7448 //! and \f$n\f$ is the number of columns in \p x.
7449 template <typename Mat>
7450 auto bitset_rows(Mat const& x) {
7451 static_assert(IsBMat<Mat>, "IsBMat<Mat> must be true!");
7452 LIBSEMIGROUPS_ASSERT(x.number_of_rows() <= BitSet<1>::max_size());
7453 LIBSEMIGROUPS_ASSERT(x.number_of_cols() <= BitSet<1>::max_size());
7454 size_t const M = detail::BitSetCapacity<Mat>::value;
7456 }
7457
7459 // Matrix helpers - bitset_row_basis
7461
7483 //! \f$c\f$ is the size of each bitset in `rows`.
7484 // This works with std::vector and StaticVector1, with value_type equal
7485 // to std::bitset and BitSet.
7486 template <typename Mat, typename Container>
7487 void bitset_row_basis(Container&& rows, std::decay_t<Container>& result) {
7488 using value_type = typename std::decay_t<Container>::value_type;
7489 static_assert(IsBMat<Mat>, "IsBMat<Mat> must be true!");
7490 static_assert(IsBitSet<value_type> || detail::IsStdBitSet<value_type>,
7491 "Container::value_type must be BitSet or std::bitset");
7492 LIBSEMIGROUPS_ASSERT(rows.size() <= BitSet<1>::max_size());
7493 LIBSEMIGROUPS_ASSERT(rows.empty()
7494 || rows[0].size() <= BitSet<1>::max_size());
7495
7496 std::sort(rows.begin(), rows.end(), detail::LessBitSet());
7497 // Remove duplicates
7498 rows.erase(std::unique(rows.begin(), rows.end()), rows.end());
7499 for (size_t i = 0; i < rows.size(); ++i) {
7500 value_type cup;
7501 cup.reset();
7502 for (size_t j = 0; j < i; ++j) {
7503 if ((rows[i] & rows[j]) == rows[j]) {
7504 cup |= rows[j];
7505 }
7506 }
7507 for (size_t j = i + 1; j < rows.size(); ++j) {
7508 if ((rows[i] & rows[j]) == rows[j]) {
7509 cup |= rows[j];
7510 }
7511 }
7512 if (cup != rows[i]) {
7513 result.push_back(std::move(rows[i]));
7514 }
7515 }
7516 }
7517
7538 //! \complexity
7539 //! \f$O(r ^ 2 c)\f$ where \f$r\f$ is the size of \p rows and
7540 //! \f$c\f$ is the size of each bitset in \p rows.
7541 template <typename Mat, typename Container>
7542 std::decay_t<Container> bitset_row_basis(Container&& rows) {
7543 using value_type = typename std::decay_t<Container>::value_type;
7544 static_assert(IsBMat<Mat>, "IsBMat<Mat> must be true!");
7545 static_assert(IsBitSet<value_type> || detail::IsStdBitSet<value_type>,
7546 "Container::value_type must be BitSet or std::bitset");
7547 LIBSEMIGROUPS_ASSERT(rows.size() <= BitSet<1>::max_size());
7548 LIBSEMIGROUPS_ASSERT(rows.empty()
7549 || rows[0].size() <= BitSet<1>::max_size());
7550 std::decay_t<Container> result;
7552 return result;
7553 }
7554
7579 //! \complexity
7580 //! \f$O(r ^ 2 c)\f$ where \f$r\f$ is the number of rows in \p x and
7581 //! \f$c\f$ is the number of columns in \p x.
7582 template <typename Mat, size_t M = detail::BitSetCapacity<Mat>::value>
7583 detail::StaticVector1<BitSet<M>, M> bitset_row_basis(Mat const& x) {
7584 static_assert(IsBMat<Mat>, "IsBMat<Mat> must be true!");
7585 LIBSEMIGROUPS_ASSERT(x.number_of_rows() <= BitSet<1>::max_size());
7586 LIBSEMIGROUPS_ASSERT(x.number_of_cols() <= BitSet<1>::max_size());
7587 detail::StaticVector1<BitSet<M>, M> result;
7589 return result;
7590 }
7591
7612 //! \complexity
7613 //! \f$O(r ^ 2 c)\f$ where \f$r\f$ is the number of rows in \p x
7614 //! and \f$c\f$ is the number of columns in \p x.
7615 template <typename Mat, typename Container>
7616 void bitset_row_basis(Mat const& x, Container& result) {
7617 using value_type = typename Container::value_type;
7618 static_assert(IsBMat<Mat>, "IsBMat<Mat> must be true!");
7619 static_assert(IsBitSet<value_type> || detail::IsStdBitSet<value_type>,
7620 "Container::value_type must be BitSet or std::bitset");
7621 LIBSEMIGROUPS_ASSERT(x.number_of_rows() <= BitSet<1>::max_size());
7622 LIBSEMIGROUPS_ASSERT(x.number_of_cols() <= BitSet<1>::max_size());
7624 }
7625
7627 // Matrix helpers - row_basis - MaxPlusTruncMat
7629
7655 //! \f$O(r ^ 2 c)\f$ where \f$r\f$ is the size of \p views and
7656 //! \f$c\f$ is the size of each row view or bit set in \p views.
7657 template <typename Mat, typename Container>
7658 std::enable_if_t<IsMaxPlusTruncMat<Mat>>
7659 row_basis(Container&& views, std::decay_t<Container>& result) {
7660 using value_type = typename std::decay_t<Container>::value_type;
7662 "Container::value_type must be Mat::RowView");
7663 using scalar_type = typename Mat::scalar_type;
7664 using Row = typename Mat::Row;
7665
7666 if (views.empty()) {
7667 return;
7668 }
7669
7670 LIBSEMIGROUPS_ASSERT(result.empty());
7671
7672 std::sort(views.begin(), views.end());
7673 Row tmp1(views[0]);
7674
7675 for (size_t r1 = 0; r1 < views.size(); ++r1) {
7676 if (r1 == 0 || views[r1] != views[r1 - 1]) {
7677 std::fill(tmp1.begin(), tmp1.end(), tmp1.scalar_zero());
7678 for (size_t r2 = 0; r2 < r1; ++r2) {
7679 scalar_type max_scalar = matrix::threshold(tmp1);
7680 for (size_t c = 0; c < tmp1.number_of_cols(); ++c) {
7681 if (views[r2][c] == tmp1.scalar_zero()) {
7682 continue;
7683 }
7684 if (views[r1][c] >= views[r2][c]) {
7685 if (views[r1][c] != matrix::threshold(tmp1)) {
7686 max_scalar
7687 = std::min(max_scalar, views[r1][c] - views[r2][c]);
7688 }
7689 } else {
7690 max_scalar = tmp1.scalar_zero();
7691 break;
7692 }
7693 }
7694 if (max_scalar != tmp1.scalar_zero()) {
7695 tmp1 += views[r2] * max_scalar;
7696 }
7697 }
7698 if (tmp1 != views[r1]) {
7699 result.push_back(views[r1]);
7700 }
7701 }
7702 }
7703 }
7704
7706 // Matrix helpers - row_basis - BMat
7708
7709 // This version of row_basis for BMat's is for used for compatibility
7710 // with the MatrixCommon framework, i.e. so that BMat's exhibit the same
7711 // interface/behaviour as other matrices.
7712 //
7713 // This version takes a container of row views of BMat, converts it to a
7714 // container of BitSets, computes the row basis using the BitSets, then
7715 // selects those row views in views that belong to the computed basis.
7716
7732 //! \exceptions
7733 //! \no_libsemigroups_except
7734 // TODO(2) complexity
7735 template <typename Mat, typename Container>
7736 std::enable_if_t<IsBMat<Mat>> row_basis(Container&& views,
7737 std::decay_t<Container>& result) {
7738 using RowView = typename Mat::RowView;
7739 using value_type = typename std::decay_t<Container>::value_type;
7740 // std::vector<bool> is used as value_type in the benchmarks
7743 "Container::value_type must equal Mat::RowView or "
7744 "std::vector<bool>!!");
7745
7746 if (views.empty()) {
7747 return;
7748 }
7749
7750 // Convert RowViews to BitSets
7751 size_t const M = detail::BitSetCapacity<Mat>::value;
7753 using bitset_type = typename decltype(br)::value_type;
7754
7755 // Map for converting bitsets back to row views
7757 LIBSEMIGROUPS_ASSERT(br.size() == views.size());
7758 for (size_t i = 0; i < br.size(); ++i) {
7759 lookup.insert({br[i], i});
7760 }
7761
7762 // Compute rowbasis using bitsets + convert back to rowviews
7763 for (auto const& bs : bitset_row_basis<Mat>(br)) {
7764 auto it = lookup.find(bs);
7765 LIBSEMIGROUPS_ASSERT(it != lookup.end());
7766 result.push_back(views[it->second]);
7767 }
7768 }
7769
7771 // Matrix helpers - row_basis - generic helpers
7773
7795 // Row basis of rowspace of matrix <x> appended to <result>
7796 template <typename Mat,
7797 typename Container,
7798 typename = std::enable_if_t<IsMatrix<Mat>>>
7799 void row_basis(Mat const& x, Container& result) {
7800 row_basis<Mat>(std::move(rows(x)), result);
7801 }
7802
7820 //! \f$O(r ^ 2 c)\f$ where \f$r\f$ is the number of rows in \p x
7821 //! and \f$c\f$ is the number of columns in \p x.
7822 // Row basis of rowspace of matrix <x>
7823 template <typename Mat, typename = std::enable_if_t<IsDynamicMatrix<Mat>>>
7826 row_basis(x, container);
7827 return container;
7828 }
7829
7847 //! \f$O(r ^ 2 c)\f$ where \f$r\f$ is the number of rows in \p x
7848 //! and \f$c\f$ is the number of columns in \p x.
7849 template <typename Mat, typename = std::enable_if_t<IsStaticMatrix<Mat>>>
7850 detail::StaticVector1<typename Mat::RowView, Mat::nr_rows>
7851 row_basis(Mat const& x) {
7852 detail::StaticVector1<typename Mat::RowView, Mat::nr_rows> container;
7853 row_basis(x, container);
7854 return container;
7855 }
7856
7873 //! \exceptions
7874 //! \no_libsemigroups_except
7875 // TODO(2) complexity
7876 template <typename Mat, typename Container>
7877 std::decay_t<Container> row_basis(Container&& rows) {
7878 using value_type = typename std::decay_t<Container>::value_type;
7879 static_assert(IsMatrix<Mat>, "IsMatrix<Mat> must be true!");
7881 "Container::value_type must be Mat::RowView");
7882
7883 std::decay_t<Container> result;
7885 return result;
7886 }
7887
7889 // Matrix helpers - row_space_size
7891
7919 //! auto x = make<BMat<>>({{1, 0, 0}, {0, 0, 1}, {0, 1, 0}});
7920 //! matrix::row_space_size(x); // returns 7
7921 //! \endcode
7922 template <typename Mat, typename = std::enable_if_t<IsBMat<Mat>>>
7923 size_t row_space_size(Mat const& x) {
7924 size_t const M = detail::BitSetCapacity<Mat>::value;
7925 auto bitset_row_basis_ = bitset_row_basis<Mat>(
7927
7929 st.insert(bitset_row_basis_.cbegin(), bitset_row_basis_.cend());
7930 std::vector<BitSet<M>> orb(bitset_row_basis_.cbegin(),
7931 bitset_row_basis_.cend());
7932 for (size_t i = 0; i < orb.size(); ++i) {
7933 for (auto& row : bitset_row_basis_) {
7934 auto cup = orb[i];
7935 for (size_t j = 0; j < x.number_of_rows(); ++j) {
7936 cup.set(j, cup[j] || row[j]);
7937 }
7938 if (st.insert(cup).second) {
7939 orb.push_back(std::move(cup));
7940 }
7941 }
7942 }
7943 return orb.size();
7944 }
7945
7946 } // namespace matrix
7947
7964 //! \no_libsemigroups_except
7965 //!
7966 //! \warning This function does not detect overflows of `Mat::scalar_type`.
7967 template <typename Mat>
7968 auto operator+(typename Mat::scalar_type a, Mat const& x)
7969 -> std::enable_if_t<IsMatrix<Mat>, Mat> {
7970 return x + a;
7971 }
7972
7989 //! \no_libsemigroups_except
7990 //!
7991 //! \warning This function does not detect overflows of `Mat::scalar_type`.
7992 template <typename Mat>
7993 auto operator*(typename Mat::scalar_type a, Mat const& x)
7994 -> std::enable_if_t<IsMatrix<Mat>, Mat> {
7995 return x * a;
7996 }
7997
8008
8033 //! \f$n\f$ is the number of columns of the matrix.
8034 template <typename Mat,
8035 typename
8036 = std::enable_if_t<IsMatrix<Mat> && !IsMatWithSemiring<Mat>>>
8038 detail::throw_if_any_row_wrong_size(rows);
8039 detail::throw_if_bad_dim<Mat>(rows);
8040 Mat m(rows);
8042 return m;
8043 }
8044
8069 //! \f$n\f$ is the number of columns of the matrix.
8070 template <typename Mat,
8071 typename
8072 = std::enable_if_t<IsMatrix<Mat> && !IsMatWithSemiring<Mat>>>
8074 rows) {
8076 }
8077
8103 //! parameter \c R is \c 1.
8104 template <typename Mat,
8105 typename
8106 = std::enable_if_t<IsMatrix<Mat> && !IsMatWithSemiring<Mat>>>
8108 detail::throw_if_bad_row_dim<Mat>(row);
8109 Mat m(row);
8111 return m;
8112 }
8113 // TODO(1) vector version of above
8114
8146 template <typename Mat,
8147 typename Semiring,
8148 typename = std::enable_if_t<IsMatrix<Mat>>>
8149 // TODO(1) pass Semiring by reference, this is hard mostly due to the way
8150 // the tests are written, which is not optimal.
8151 Mat make(Semiring const* semiring,
8154 detail::throw_if_any_row_wrong_size(rows);
8155 detail::throw_if_bad_dim<Mat>(rows);
8156 Mat m(semiring, rows);
8158 return m;
8159 }
8160
8191 //! \f$n\f$ is the number of columns of the matrix.
8192 template <typename Mat,
8193 typename Semiring,
8194 typename = std::enable_if_t<IsMatrix<Mat>>>
8195 Mat make(Semiring const* semiring,
8197 detail::throw_if_any_row_wrong_size(rows);
8198 detail::throw_if_bad_dim<Mat>(rows);
8199 Mat m(semiring, rows);
8201 return m;
8202 }
8203
8225 //! \f$O(n)\f$ where \f$n\f$ is the number of columns of the matrix.
8226 template <typename Mat,
8227 typename Semiring,
8228 typename = std::enable_if_t<IsMatrix<Mat>>>
8229 Mat make(Semiring const* semiring,
8231 detail::throw_if_bad_row_dim<Mat>(row);
8232 Mat m(semiring, row);
8234 return m;
8235 }
8236
8262 //! \f$O(mn)\f$ where \f$m\f$ is the number of rows and \f$n\f$ is the
8263 //! number of columns of the matrix.
8264 template <size_t R, size_t C, typename Scalar>
8269 }
8270
8272 // Printing etc...
8274
8284 //!
8285 //! \exceptions
8286 //! \no_libsemigroups_except
8287 template <typename S, typename T>
8289 detail::RowViewCommon<S, T> const& x) {
8290 os << "{";
8291 for (auto it = x.cbegin(); it != x.cend(); ++it) {
8292 os << *it;
8293 if (it != x.cend() - 1) {
8294 os << ", ";
8295 }
8296 }
8297 os << "}";
8298 return os;
8299 }
8300
8314 //!
8315 //! \exceptions
8316 //! \no_libsemigroups_except
8317 template <typename Mat>
8318 auto operator<<(std::ostringstream& os, Mat const& x)
8319 -> std::enable_if_t<IsMatrix<Mat>, std::ostringstream&> {
8320 size_t n = 0;
8321 if (x.number_of_rows() != 1) {
8322 os << "{";
8323 }
8324 for (auto&& r : matrix::rows(x)) {
8325 os << r;
8326 if (n != x.number_of_rows() - 1) {
8327 os << ", ";
8328 }
8329 n++;
8330 }
8331 if (x.number_of_rows() != 1) {
8332 os << "}";
8333 }
8334 return os;
8335 }
8336
8350 //! (default: \c 72).
8351 //!
8352 //! \throws LibsemigroupsException if \p braces does not have size \c 2.
8353 template <typename Mat>
8354 auto to_human_readable_repr(Mat const& x,
8355 std::string const& prefix,
8356 std::string const& short_name = "",
8357 std::string const& braces = "{}",
8358 size_t max_width = 72)
8359 -> std::enable_if_t<IsMatrix<Mat>, std::string> {
8360 if (braces.size() != 2) {
8362 "the 4th argument (braces) must have size 2, found {}",
8363 braces.size());
8364 }
8365
8366 size_t const R = x.number_of_rows();
8367 size_t const C = x.number_of_cols();
8368
8369 std::vector<size_t> max_col_widths(C, 0);
8370 std::vector<size_t> row_widths(C, prefix.size() + 1);
8371 for (size_t r = 0; r < R; ++r) {
8372 for (size_t c = 0; c < C; ++c) {
8373 size_t width
8374 = detail::unicode_string_length(detail::entry_repr(x(r, c)));
8375 row_widths[r] += width;
8376 if (width > max_col_widths[c]) {
8377 max_col_widths[c] = width;
8378 }
8379 }
8380 }
8381 auto col_width
8382 = *std::max_element(max_col_widths.begin(), max_col_widths.end());
8383 // The total width if we pad the entries according to the widest column.
8384 auto const total_width = col_width * C + prefix.size() + 1;
8385 if (total_width > max_width) {
8386 // Padding according to the widest column is too wide!
8387 if (*std::max_element(row_widths.begin(), row_widths.end()) > max_width) {
8388 // If the widest row is too wide, then use the short name
8389 return fmt::format(
8390 "<{}x{} {}>", x.number_of_rows(), x.number_of_cols(), short_name);
8391 }
8392 // If the widest row is not too wide, then just don't pad the entries
8393 col_width = 0;
8394 }
8395
8396 std::string result = fmt::format("{}", prefix);
8397 std::string rindent;
8398 auto const lbrace = braces[0], rbrace = braces[1];
8399 if (R != 0 && C != 0) {
8400 result += lbrace;
8401 for (size_t r = 0; r < R; ++r) {
8402 result += fmt::format("{}{}", rindent, lbrace);
8403 rindent = std::string(prefix.size() + 1, ' ');
8404 std::string csep = "";
8405 for (size_t c = 0; c < C; ++c) {
8406 result += fmt::format(
8407 "{}{:>{}}", csep, detail::entry_repr(x(r, c)), col_width);
8408 csep = ", ";
8409 }
8410 result += fmt::format("{}", rbrace);
8411 if (r != R - 1) {
8412 result += ",\n";
8413 }
8414 }
8415 result += rbrace;
8416 }
8417 result += ")";
8418 return result;
8419 }
8420
8422 // Adapters
8424
8439
8447 //! satisfying \ref IsMatrix<Mat>.
8448 //!
8449 //! \tparam Mat the type of matrices.
8450 template <typename Mat>
8451 struct Complexity<Mat, std::enable_if_t<IsMatrix<Mat>>> {
8461 //! \noexcept
8462 //!
8463 //! \complexity
8464 //! Constant.
8465 constexpr size_t operator()(Mat const& x) const noexcept {
8466 return x.number_of_rows() * x.number_of_rows() * x.number_of_rows();
8467 }
8468 };
8469
8477 //! \ref IsMatrix<Mat>.
8478 //!
8479 //! \tparam Mat the type of matrices.
8480 template <typename Mat>
8481 struct Degree<Mat, std::enable_if_t<IsMatrix<Mat>>> {
8490 //! \noexcept
8491 //!
8492 //! \complexity
8493 //! Constant.
8494 constexpr size_t operator()(Mat const& x) const noexcept {
8495 return x.number_of_rows();
8496 }
8497 };
8498
8506 //! \ref IsMatrix<Mat>.
8507 //!
8508 //! \tparam Mat the type of matrices.
8509 template <typename Mat>
8510 struct Hash<Mat, std::enable_if_t<IsMatrix<Mat>>> {
8519 //! \no_libsemigroups_except
8520 //!
8521 //! \complexity
8522 //! Constant.
8523 constexpr size_t operator()(Mat const& x) const {
8524 return x.hash_value();
8525 }
8526 };
8527
8540 //! It is not possible to increase the degree of any of the types
8541 //! satisfying \ref IsMatrix, and as such the call operator of this type
8542 //! does nothing.
8543 template <typename Mat>
8544 struct IncreaseDegree<Mat, std::enable_if_t<IsMatrix<Mat>>> {
8548 constexpr void operator()(Mat&, size_t) const noexcept {
8549 // static_assert(false, "Cannot increase degree for Matrix");
8550 LIBSEMIGROUPS_ASSERT(false);
8551 }
8552 };
8553
8564 //!
8565 //! \note There is no `operator()(size_t)` meaning it isn't possible to use
8566 //! elements of type \p Mat with \ref SchreierSims.
8567 template <typename Mat>
8568 struct One<Mat, std::enable_if_t<IsMatrix<Mat>>> {
8578 //!
8579 //! \complexity
8580 //! \f$O(m ^ 2)\f$ where \f$m\f$ is the number of rows of the
8581 //! matrix \p x.
8582 inline Mat operator()(Mat const& x) const {
8583 return x.one();
8584 }
8585 };
8586
8594 //! \ref IsMatrix<Mat>.
8595 //!
8596 //! \tparam Mat the type of matrices.
8597 template <typename Mat>
8598 struct Product<Mat, std::enable_if_t<IsMatrix<Mat>>> {
8614 //!
8615 //! \warning
8616 //! This function only works for square matrices.
8617 inline void
8618 operator()(Mat& xy, Mat const& x, Mat const& y, size_t = 0) const {
8619 xy.product_inplace_no_checks(x, y);
8620 }
8621 };
8622} // namespace libsemigroups
8623
8624namespace std {
8625 template <size_t N,
8626 typename Mat,
8627 std::enable_if_t<libsemigroups::IsMatrix<Mat>>>
8628 inline void swap(Mat& x, Mat& y) noexcept {
8629 x.swap(y);
8630 }
8631} // namespace std
8632
8633#endif // LIBSEMIGROUPS_MATRIX_HPP_
DynamicMatrix(std::initializer_list< scalar_type > const &c)
Construct a vector from a std::initializer_list.
Definition matrix.hpp:2893
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:2916
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:2812
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:2803
PlusOp Plus
Alias for the template parameter PlusOp.
Definition matrix.hpp:2809
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:2799
void swap(DynamicMatrix &that) noexcept
Swaps the contents of *this with the contents of that.
Definition matrix.hpp:3175
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:2997
DynamicMatrix(size_t r, size_t c)
Construct a matrix with given dimensions.
Definition matrix.hpp:2870
ZeroOp Zero
Alias for the template parameter ZeroOp.
Definition matrix.hpp:2815
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:2818
void semiring_type
Alias for the semiring type (void).
Definition matrix.hpp:2825
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:2950
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:2806
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:2935
typename MatrixCommon::scalar_reference scalar_reference
The type of references to the entries in the matrix.
Definition matrix.hpp:2794
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:2791
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:3314
DynamicMatrix & operator=(DynamicMatrix &&)=default
Default move assignment operator.
static DynamicMatrix one(Semiring const *semiring, size_t n)
Construct the identity matrix.
Definition matrix.hpp:3391
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:3337
DynamicMatrix(Semiring const *sr, size_t r, size_t c)
Construct a matrix over a given semiring with given dimensions.
Definition matrix.hpp:3294
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:3251
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:3247
void swap(DynamicMatrix &that) noexcept
Swaps the contents of *this with the contents of that.
Definition matrix.hpp:3571
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:3371
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:3356
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:3242
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:3239
DynamicRowView< Semiring, Scalar > RowView
Alias for the type of row views of a DynamicMatrix.
Definition matrix.hpp:3254
Semiring semiring_type
Alias for the template parameter Semiring.
Definition matrix.hpp:3259
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:1572
typename RowViewCommon::iterator iterator
Alias for const iterators pointing at entries of a matrix.
Definition matrix.hpp:1536
typename RowViewCommon::scalar_const_reference scalar_const_reference
Alias for const references to the template parameter Scalar.
Definition matrix.hpp:1547
typename RowViewCommon::scalar_reference scalar_reference
Alias for references to the template parameter Scalar.
Definition matrix.hpp:1542
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:1554
typename RowViewCommon::const_iterator const_iterator
Alias for const iterators pointing at entries of a matrix.
Definition matrix.hpp:1533
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:1539
typename RowViewCommon::matrix_type matrix_type
Alias for the type of the underlying matrix.
Definition matrix.hpp:1551
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:1690
typename RowViewCommon::scalar_const_reference scalar_const_reference
Alias for const references to the template parameter Scalar.
Definition matrix.hpp:1701
typename RowViewCommon::scalar_reference scalar_reference
Alias for references to the template parameter Scalar.
Definition matrix.hpp:1696
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:1708
typename RowViewCommon::const_iterator const_iterator
Alias for const iterators pointing at entries of a matrix.
Definition matrix.hpp:1687
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:1693
typename RowViewCommon::matrix_type matrix_type
Alias for the type of the underlying matrix.
Definition matrix.hpp:1705
DynamicRowView()=default
Default constructor.
Class representing a truncated max-plus semiring.
Definition matrix.hpp:5138
static constexpr Scalar scalar_zero() noexcept
Get the additive identity.
Definition matrix.hpp:5212
Scalar plus_no_checks(Scalar x, Scalar y) const noexcept
Addition in a truncated max-plus semiring.
Definition matrix.hpp:5275
Scalar product_no_checks(Scalar x, Scalar y) const noexcept
Multiplication in a truncated max-plus semiring.
Definition matrix.hpp:5240
MaxPlusTruncSemiring()=delete
Deleted default constructor.
Scalar threshold() const noexcept
Get the threshold.
Definition matrix.hpp:5300
static constexpr Scalar scalar_one() noexcept
Get the multiplicative identity.
Definition matrix.hpp:5198
Class representing a truncated min-plus semiring.
Definition matrix.hpp:5615
static constexpr Scalar scalar_zero() noexcept
Get the additive identity.
Definition matrix.hpp:5688
Scalar plus_no_checks(Scalar x, Scalar y) const noexcept
Addition in a truncated min-plus semiring.
Definition matrix.hpp:5751
Scalar product_no_checks(Scalar x, Scalar y) const noexcept
Multiplication in a truncated min-plus semiring.
Definition matrix.hpp:5716
Scalar threshold() const noexcept
Get the threshold.
Definition matrix.hpp:5776
static constexpr Scalar scalar_one() noexcept
Get the multiplicative identity.
Definition matrix.hpp:5673
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:6239
Scalar plus_no_checks(Scalar x, Scalar y) const noexcept
Addition in an ntp semiring.
Definition matrix.hpp:6297
NTPSemiring()=delete
Deleted default constructor.
Scalar product_no_checks(Scalar x, Scalar y) const noexcept
Multiplication in an ntp semiring.
Definition matrix.hpp:6267
Scalar period() const noexcept
Get the period.
Definition matrix.hpp:6331
Scalar threshold() const noexcept
Get the threshold.
Definition matrix.hpp:6315
static constexpr Scalar scalar_one() noexcept
Get the multiplicative identity.
Definition matrix.hpp:6223
Static matrix class.
Definition matrix.hpp:1865
typename MatrixCommon::iterator iterator
Definition matrix.hpp:1912
StaticMatrix(std::initializer_list< scalar_type > const &c)
Construct a row (from std::initializer_list).
Definition matrix.hpp:1940
typename MatrixCommon::const_iterator const_iterator
Definition matrix.hpp:1915
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:1890
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:1967
StaticRowView< PlusOp, ProdOp, ZeroOp, OneOp, C, Scalar > RowView
Definition matrix.hpp:1897
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:2003
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:1983
StaticMatrix< PlusOp, ProdOp, ZeroOp, OneOp, 1, C, Scalar > Row
Alias for the type of the rows of a StaticMatrix.
Definition matrix.hpp:1894
StaticMatrix()=default
Default constructor.
typename MatrixCommon::scalar_reference scalar_reference
Alias for references to the template parameter Scalar.
Definition matrix.hpp:1885
typename MatrixCommon::scalar_type scalar_type
Alias for the template parameter Scalar.
Definition matrix.hpp:1882
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:1080
typename RowViewCommon::iterator iterator
Alias for const iterators pointing at entries of a matrix.
Definition matrix.hpp:1096
typename RowViewCommon::scalar_const_reference scalar_const_reference
Alias for const references to the template parameter Scalar.
Definition matrix.hpp:1107
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:1102
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:1114
typename RowViewCommon::const_iterator const_iterator
Alias for const iterators pointing at entries of a matrix.
Definition matrix.hpp:1093
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:1099
StaticRowView(StaticRowView &&)=default
Default move constructor.
typename RowViewCommon::matrix_type matrix_type
Alias for the type of the underlying matrix.
Definition matrix.hpp:1111
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:3971
static constexpr bool IsBMat
Helper to check if a type is BMat.
Definition matrix.hpp:4029
DynamicMatrix< BooleanPlus, BooleanProd, BooleanZero, BooleanOne, int > DynamicBMat
Alias for dynamic boolean matrices.
Definition matrix.hpp:3957
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:4074
std::conditional_t< R==0||C==0, DynamicBMat, StaticBMat< R, C > > BMat
Alias template for boolean matrices.
Definition matrix.hpp:3994
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:4318
DynamicMatrix< IntegerPlus< Scalar >, IntegerProd< Scalar >, IntegerZero< Scalar >, IntegerOne< Scalar >, Scalar > DynamicIntMat
Alias for dynamic integer matrices.
Definition matrix.hpp:4270
StaticMatrix< IntegerPlus< Scalar >, IntegerProd< Scalar >, IntegerZero< Scalar >, IntegerOne< Scalar >, R, C, Scalar > StaticIntMat
Alias for static integer matrices.
Definition matrix.hpp:4293
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:4644
constexpr bool IsStaticMatrix
Helper variable template.
Definition matrix.hpp:3649
constexpr bool IsDynamicMatrix
Helper variable template.
Definition matrix.hpp:3662
static constexpr bool IsIntMat
Helper variable template.
Definition matrix.hpp:4343
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:7964
static constexpr bool IsMatWithSemiring
Helper variable template.
Definition matrix.hpp:3676
static constexpr bool IsMinPlusMat
Helper variable template.
Definition matrix.hpp:4952
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:4593
DynamicMatrix< MaxPlusPlus< Scalar >, MaxPlusProd< Scalar >, MaxPlusZero< Scalar >, IntegerZero< Scalar >, Scalar > DynamicMaxPlusMat
Alias for dynamic max-plus matrices.
Definition matrix.hpp:4574
std::conditional_t< R==0||C==0, DynamicMaxPlusMat< Scalar >, StaticMaxPlusMat< R, C, Scalar > > MaxPlusMat
Alias template for max-plus matrices.
Definition matrix.hpp:4617
DynamicMatrix< MaxPlusPlus< Scalar >, MaxPlusTruncProd< T, Scalar >, MaxPlusZero< Scalar >, IntegerZero< Scalar >, Scalar > DynamicMaxPlusTruncMat
Alias for dynamic truncated max-plus matrices.
Definition matrix.hpp:5321
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:5367
StaticMatrix< MaxPlusPlus< Scalar >, MaxPlusTruncProd< T, Scalar >, MaxPlusZero< Scalar >, IntegerZero< Scalar >, R, C, Scalar > StaticMaxPlusTruncMat
Alias for static truncated max-plus matrices.
Definition matrix.hpp:5341
static constexpr bool IsMaxPlusTruncMat
Helper to check if a type is MaxPlusTruncMat.
Definition matrix.hpp:5410
DynamicMatrix< MinPlusPlus< Scalar >, MinPlusProd< Scalar >, MinPlusZero< Scalar >, IntegerZero< Scalar >, Scalar > DynamicMinPlusMat
Alias for dynamic min-plus matrices.
Definition matrix.hpp:4882
StaticMatrix< MinPlusPlus< Scalar >, MinPlusProd< Scalar >, MinPlusZero< Scalar >, IntegerZero< Scalar >, R, C, Scalar > StaticMinPlusMat
Alias for static min-plus matrices.
Definition matrix.hpp:4901
std::conditional_t< R==0||C==0, DynamicMinPlusMat< Scalar >, StaticMinPlusMat< R, C, Scalar > > MinPlusMat
Alias template for min-plus matrices.
Definition matrix.hpp:4925
DynamicMatrix< MinPlusPlus< Scalar >, MinPlusTruncProd< T, Scalar >, MinPlusZero< Scalar >, IntegerZero< Scalar >, Scalar > DynamicMinPlusTruncMat
Alias for dynamic truncated min-plus matrices.
Definition matrix.hpp:5797
StaticMatrix< MinPlusPlus< Scalar >, MinPlusTruncProd< T, Scalar >, MinPlusZero< Scalar >, IntegerZero< Scalar >, R, C, Scalar > StaticMinPlusTruncMat
Alias for static truncated min-plus matrices.
Definition matrix.hpp:5817
static constexpr bool IsMinPlusTruncMat
Helper to check if a type is MinPlusTruncMat.
Definition matrix.hpp:5887
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:5844
DynamicMatrix< NTPSemiring< Scalar >, Scalar > DynamicNTPMatWithSemiring
Alias for ntp matrices with dynamic threshold and period.
Definition matrix.hpp:6351
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:6367
static constexpr bool IsNTPMat
Helper to check if a type is NTPMat.
Definition matrix.hpp:6471
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:6428
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:6393
std::conditional_t< R==0||C==0, DynamicProjMaxPlusMat< Scalar >, StaticProjMaxPlusMat< R, C, Scalar > > ProjMaxPlusMat
Alias template for projective max-plus matrices.
Definition matrix.hpp:7055
detail::ProjMaxPlusMat< DynamicMaxPlusMat< Scalar > > DynamicProjMaxPlusMat
Alias for dynamic projective max-plus matrices with run-time dimensions.
Definition matrix.hpp:7038
static constexpr bool IsProjMaxPlusMat
Helper to check if a type is ProjMaxPlusMat.
Definition matrix.hpp:7085
detail::ProjMaxPlusMat< StaticMaxPlusMat< R, C, Scalar > > StaticProjMaxPlusMat
Alias for static projective max-plus matrices with compile-time arithmetic and dimensions.
Definition matrix.hpp:7024
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.
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:7483
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:7311
constexpr Scalar period(StaticNTPMat< T, P, R, C, Scalar > const &) noexcept
Returns the period of a static ntp matrix.
Definition matrix.hpp:6507
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:3755
size_t row_space_size(Mat const &x)
Returns the size of the row space of a boolean matrix.
Definition matrix.hpp:7919
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:7240
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:7175
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:7655
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:3905
constexpr bool operator()() const noexcept
Call operator returning the multiplication identity true of the boolean semiring.
Definition matrix.hpp:3916
Function object for addition in the boolean semiring.
Definition matrix.hpp:3851
constexpr bool operator()(bool x, bool y) const noexcept
Call operator for addition.
Definition matrix.hpp:3864
Function object for multiplication in the boolean semiring.
Definition matrix.hpp:3878
constexpr bool operator()(bool x, bool y) const noexcept
Call operator for multiplication.
Definition matrix.hpp:3891
Function object for returning the additive identity.
Definition matrix.hpp:3930
constexpr bool operator()() const noexcept
Call operator returning the additive identity false of the boolean semiring.
Definition matrix.hpp:3941
constexpr size_t operator()(Mat const &x) const noexcept
Call operator.
Definition matrix.hpp:8461
Adapter for the complexity of multiplication.
Definition adapters.hpp:128
constexpr size_t operator()(Mat const &x) const noexcept
Call operator.
Definition matrix.hpp:8490
Adapter for the degree of an element.
Definition adapters.hpp:166
constexpr size_t operator()(Mat const &x) const
Call operator.
Definition matrix.hpp:8519
Adapter for hashing.
Definition adapters.hpp:451
constexpr void operator()(Mat &, size_t) const noexcept
Call operator.
Definition matrix.hpp:8544
Adapter for increasing the degree of an element.
Definition adapters.hpp:206
Function object for returning the multiplicative identity.
Definition matrix.hpp:4244
constexpr Scalar operator()() const noexcept
Call operator returning the integer 1.
Definition matrix.hpp:4254
Function object for addition in the ring of integers.
Definition matrix.hpp:4160
constexpr Scalar operator()(Scalar x, Scalar y) const noexcept
Call operator for addition.
Definition matrix.hpp:4173
Function object for multiplication in the ring of integers.
Definition matrix.hpp:4191
constexpr Scalar operator()(Scalar x, Scalar y) const noexcept
Call operator for multiplication.
Definition matrix.hpp:4204
Function object for returning the additive identity.
Definition matrix.hpp:4219
constexpr Scalar operator()() const noexcept
Call operator returning the integer 0.
Definition matrix.hpp:4229
Function object for addition in the max-plus semiring.
Definition matrix.hpp:4462
Scalar operator()(Scalar x, Scalar y) const noexcept
Call operator for addition.
Definition matrix.hpp:4477
Function object for multiplication in the max-plus semiring.
Definition matrix.hpp:4509
Scalar operator()(Scalar x, Scalar y) const noexcept
Call operator for multiplication.
Definition matrix.hpp:4524
Function object for multiplication in truncated max-plus semirings.
Definition matrix.hpp:5096
Scalar operator()(Scalar x, Scalar y) const noexcept
Call operator for multiplication.
Definition matrix.hpp:5111
Function object for returning the additive identity of the max-plus semiring.
Definition matrix.hpp:4546
constexpr Scalar operator()() const noexcept
Call operator for additive identity.
Definition matrix.hpp:4558
Function object for addition in the min-plus semiring.
Definition matrix.hpp:4770
Scalar operator()(Scalar x, Scalar y) const noexcept
Call operator for addition.
Definition matrix.hpp:4785
Function object for multiplication in the min-plus semiring.
Definition matrix.hpp:4817
Scalar operator()(Scalar x, Scalar y) const noexcept
Call operator for multiplication.
Definition matrix.hpp:4832
Function object for multiplication in min-plus truncated semirings.
Definition matrix.hpp:5576
Scalar operator()(Scalar x, Scalar y) const noexcept
Call operator for multiplication.
Definition matrix.hpp:5589
Function object for returning the additive identity of the min-plus semiring.
Definition matrix.hpp:4854
constexpr Scalar operator()() const noexcept
Call operator for additive identity.
Definition matrix.hpp:4866
Function object for addition in ntp semirings.
Definition matrix.hpp:6083
constexpr Scalar operator()(Scalar x, Scalar y) const noexcept
Call operator for addition.
Definition matrix.hpp:6095
Function object for multiplication in an ntp semirings.
Definition matrix.hpp:6124
constexpr Scalar operator()(Scalar x, Scalar y) const noexcept
Call operator for multiplication.
Definition matrix.hpp:6138
Mat operator()(Mat const &x) const
Call operator.
Definition matrix.hpp:8578
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:8614
Adapter for the product of two elements.
Definition adapters.hpp:289
T swap(T... args)
T tie(T... args)
T unique(T... args)