Eigen  5.0.1
 
Loading...
Searching...
No Matches
BlockSparseMatrix.h
1// This file is part of Eigen, a lightweight C++ template library
2// for linear algebra.
3//
4// This Source Code Form is subject to the terms of the Mozilla
5// Public License v. 2.0. If a copy of the MPL was not distributed
6// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
7// SPDX-FileCopyrightText: The Eigen Authors
8// SPDX-License-Identifier: MPL-2.0
9
10#ifndef EIGEN_BLOCKSPARSEMATRIX_H
11#define EIGEN_BLOCKSPARSEMATRIX_H
12
13// IWYU pragma: private
14#include "./InternalHeaderCheck.h"
15
16#include <algorithm>
17#include <numeric>
18#include <utility>
19
20namespace Eigen {
21
22// Forward declarations
23template <typename, int, bool>
25template <typename, int, bool>
27template <typename Scalar_, int Options_, int BlockRows_, int BlockCols_, typename StorageIndex_>
29
31struct BlockSparse {};
32
35 static std::string debugName() { return "BlockSparseShape"; }
36};
37
38namespace internal {
39// Returns m.adjoint() when Conj==true, m.transpose() otherwise.
40// SFINAE overloads keep the return type concrete under C++14 (no if constexpr).
41template <bool Conj, typename T>
42std::enable_if_t<Conj, decltype(std::declval<const T&>().adjoint())> adjoint_if(const T& m) {
43 return m.adjoint();
44}
45
46template <bool Conj, typename T>
47std::enable_if_t<!Conj, decltype(std::declval<const T&>().transpose())> adjoint_if(const T& m) {
48 return m.transpose();
49}
50template <>
51struct storage_kind_to_evaluator_kind<BlockSparse> {
52 using Kind = IndexBased;
53};
54
55template <>
56struct storage_kind_to_shape<BlockSparse> {
57 using Shape = BlockSparseShape;
58};
59
60template <typename Scalar_, int Options_, int BlockRows_, int BlockCols_, typename StorageIndex_>
61struct traits<BlockSparseMatrix<Scalar_, Options_, BlockRows_, BlockCols_, StorageIndex_>> {
62 using Scalar = Scalar_;
63 using StorageIndex = StorageIndex_;
64 using StorageKind = BlockSparse;
65 using XprKind = MatrixXpr;
66
67 static constexpr Index RowsAtCompileTime = Dynamic;
68 static constexpr Index ColsAtCompileTime = Dynamic;
69 static constexpr Index MaxRowsAtCompileTime = Dynamic;
70 static constexpr Index MaxColsAtCompileTime = Dynamic;
71 static constexpr int Options = Options_;
72 static constexpr unsigned int Flags = Options_ | NestByRefBit | LvalueBit;
73};
74
75} // namespace internal
76
88template <typename Scalar_, int BlockRows_, int BlockCols_, int Options_ = ColMajor, typename StorageIndex_ = int>
89class BlockTriplet {
90 public:
91 using Scalar = Scalar_;
92 using StorageIndex = StorageIndex_;
94 using BlockMapType = Map<BlockType, Unaligned>;
95 using ConstBlockMapType = Map<const BlockType, Unaligned>;
96
97 static constexpr int BlockSize = BlockRows_ * BlockCols_;
98
99 BlockTriplet() = default;
100
101 BlockTriplet(StorageIndex blockRow, StorageIndex blockCol, const BlockType& block)
102 : m_row(blockRow), m_col(blockCol) {
103 BlockMapType{m_value} = block;
104 }
105
106 StorageIndex row() const { return m_row; }
107 StorageIndex col() const { return m_col; }
108 // Implicitly usable wherever a MatrixBase expression is expected.
109 ConstBlockMapType value() const { return ConstBlockMapType(m_value); }
110
111 private:
112 StorageIndex m_row = 0;
113 StorageIndex m_col = 0;
114 // Flat array avoids the alignment padding that a Matrix<> member would incur.
115 Scalar m_value[BlockSize];
116};
117
158template <typename Scalar_, int Options_, int BlockRows_, int BlockCols_, typename StorageIndex_ = int>
159class BlockSparseMatrix
160 : public EigenBase<BlockSparseMatrix<Scalar_, Options_, BlockRows_, BlockCols_, StorageIndex_>> {
161 EIGEN_STATIC_ASSERT(BlockRows_ >= 1, BLOCKROWS_MUST_BE_A_POSITIVE_COMPILE_TIME_SIZE)
162 EIGEN_STATIC_ASSERT(BlockCols_ >= 1, BLOCKCOLS_MUST_BE_A_POSITIVE_COMPILE_TIME_SIZE)
163 EIGEN_STATIC_ASSERT(std::is_integral<StorageIndex_>::value&& std::is_signed<StorageIndex_>::value,
164 STORAGEINDEX_MUST_BE_A_SIGNED_INTEGRAL_TYPE)
165 // Eigen's Matrix<> requires: a column vector (Cols==1, Rows>1) must be ColMajor;
166 // a row vector (Rows==1, Cols>1) must be RowMajor. Guard those cases here so
167 // the error fires at BlockSparseMatrix instantiation rather than inside BlockType.
168 EIGEN_STATIC_ASSERT(!(BlockCols_ == 1 && BlockRows_ != 1 && bool(Options_ & RowMajorBit)),
169 INVALID_MATRIX_TEMPLATE_PARAMETERS)
170 EIGEN_STATIC_ASSERT(!(BlockRows_ == 1 && BlockCols_ != 1 && !bool(Options_ & RowMajorBit)),
171 INVALID_MATRIX_TEMPLATE_PARAMETERS)
172
173 public:
174 // -------------------------------------------------------------------------
175 // Type aliases & compile-time constants
176 // -------------------------------------------------------------------------
177 using Scalar = Scalar_;
178 using StorageIndex = StorageIndex_;
181
182 static constexpr int Options = Options_;
183 static constexpr Index BlockRows = BlockRows_;
184 static constexpr Index BlockCols = BlockCols_;
185 static constexpr bool IsRowMajor = Options_ & RowMajorBit;
186 static constexpr Index BlockSize = BlockRows_ * BlockCols_;
187
188 // If one block occupies a power-of-two number of bytes, and the values array
189 // is Eigen-allocated (guaranteed aligned to EIGEN_MAX_ALIGN_BYTES), then every
190 // block pointer is aligned to min(BlockBytes, EIGEN_MAX_ALIGN_BYTES).
191 static constexpr std::size_t BlockBytes = std::size_t(BlockSize) * sizeof(Scalar);
192 static constexpr int BlockMapAlignment = ((BlockBytes & (BlockBytes - 1)) == 0 && BlockBytes >= 8)
193 ? int(numext::mini(BlockBytes, std::size_t(EIGEN_MAX_ALIGN_BYTES)))
194 : 0;
195
196 using BlockMap = Map<BlockType, BlockMapAlignment>;
197 using ConstBlockMap = Map<const BlockType, BlockMapAlignment>;
198
199 // -------------------------------------------------------------------------
200 // Constructors / copy / move
201 // -------------------------------------------------------------------------
202
204 BlockSparseMatrix() = default;
205
208 : m_blockOuterSize(IsRowMajor ? blockRows : blockCols), m_blockInnerSize(IsRowMajor ? blockCols : blockRows) {}
209
210 // -------------------------------------------------------------------------
211 // Dimensions
212 // -------------------------------------------------------------------------
213
215 Index rows() const noexcept { return (IsRowMajor ? m_blockOuterSize : m_blockInnerSize) * BlockRows_; }
217 Index cols() const noexcept { return (IsRowMajor ? m_blockInnerSize : m_blockOuterSize) * BlockCols_; }
218
220 Index blockRows() const { return IsRowMajor ? m_blockOuterSize : m_blockInnerSize; }
222 Index blockCols() const { return IsRowMajor ? m_blockInnerSize : m_blockOuterSize; }
223
225 Index blockOuterSize() const { return m_blockOuterSize; }
227 Index blockInnerSize() const { return m_blockInnerSize; }
228
230 Index outerSize() const { return IsRowMajor ? rows() : cols(); }
232 Index innerSize() const { return IsRowMajor ? cols() : rows(); }
233
235 Index nonZeroBlocks() const { return m_outerIndex(m_blockOuterSize); }
237 Index nonZeros() const { return nonZeroBlocks() * BlockSize; }
239 Index allocatedBlocks() const { return Index(m_innerIndex.size()); }
240
241 // -------------------------------------------------------------------------
242 // Raw pointer access (for interoperability)
243 // -------------------------------------------------------------------------
244 const StorageIndex* outerIndexPtr() const { return m_outerIndex.data(); }
245 StorageIndex* outerIndexPtr() { return m_outerIndex.data(); }
246 const StorageIndex* innerIndexPtr() const { return m_innerIndex.data(); }
247 StorageIndex* innerIndexPtr() { return m_innerIndex.data(); }
248 const Scalar* valuePtr() const { return m_values.data(); }
249 Scalar* valuePtr() { return m_values.data(); }
250
251 // -------------------------------------------------------------------------
252 // Block access by sequential nonzero index
253 // -------------------------------------------------------------------------
254
256 ConstBlockMap blockRef(Index k) const { return ConstBlockMap(m_values.data() + k * BlockSize); }
258 BlockMap blockRef(Index k) { return BlockMap(m_values.data() + k * BlockSize); }
259
260 // -------------------------------------------------------------------------
261 // Inner iterator over blocks within one outer vector
262 // -------------------------------------------------------------------------
263
269 class InnerIterator {
270 public:
271 EIGEN_STRONG_INLINE InnerIterator(const BlockSparseMatrix& mat, Index outer)
272 : m_mat(mat), m_id(mat.m_outerIndex(outer)), m_end(mat.m_outerIndex(outer + 1)), m_outer(outer) {}
273
274 EIGEN_STRONG_INLINE operator bool() const { return m_id < m_end; }
275 EIGEN_STRONG_INLINE InnerIterator& operator++() {
276 ++m_id;
277 return *this;
278 }
279
281 EIGEN_STRONG_INLINE Index outer() const { return m_outer; }
283 EIGEN_STRONG_INLINE Index index() const { return m_mat.m_innerIndex(m_id); }
285 EIGEN_STRONG_INLINE Index blockRow() const { return IsRowMajor ? m_outer : index(); }
287 EIGEN_STRONG_INLINE Index blockCol() const { return IsRowMajor ? index() : m_outer; }
288
290 EIGEN_STRONG_INLINE ConstBlockMap value() const { return m_mat.blockRef(m_id); }
292 EIGEN_STRONG_INLINE BlockMap valueRef() {
293 return BlockMap(const_cast<Scalar*>(m_mat.m_values.data()) + m_id * BlockSize);
294 }
295
296 private:
297 const BlockSparseMatrix& m_mat;
298 Index m_id;
299 Index m_end;
300 Index m_outer;
301 };
302
303 // -------------------------------------------------------------------------
304 // Resize / clear
305 // -------------------------------------------------------------------------
306
310 m_blockOuterSize = IsRowMajor ? blockRows : blockCols;
311 m_blockInnerSize = IsRowMajor ? blockCols : blockRows;
312 m_outerIndex.resize(m_blockOuterSize + 1);
313 m_outerIndex.setZero();
314 }
315
317 void setZero() {
318 m_outerIndex.resize(m_blockOuterSize + 1);
319 m_outerIndex.setZero();
320 }
321
324 void reserve(Index n) {
325 if (n > Index(m_innerIndex.size())) conservativeResizeBlockStorage_(n);
326 }
327
329 void squeeze() {
330 Index nnz = nonZeroBlocks();
331 if (nnz < Index(m_innerIndex.size())) conservativeResizeBlockStorage_(nnz);
332 }
333
339 void setIdentity() {
340 EIGEN_STATIC_ASSERT(BlockRows_ == BlockCols_, THIS_METHOD_IS_ONLY_FOR_SQUARE_BLOCK_MATRICES)
341 Index n = (std::min)(m_blockOuterSize, m_blockInnerSize);
342 m_outerIndex.resize(m_blockOuterSize + 1);
343 resizeBlockStorage_(n);
344 for (Index i = 0; i <= m_blockOuterSize; ++i) m_outerIndex(i) = StorageIndex((std::min)(i, n));
345 for (StorageIndex i = 0; i < n; ++i) {
346 m_innerIndex(i) = i;
347 blockRef(i).setIdentity();
348 }
349 }
350
357 void setFromOuterInner(Index blockRows, Index blockCols, Index nnzBlocks, const StorageIndex_* outerPtr,
358 const StorageIndex_* innerPtr) {
359 m_blockOuterSize = IsRowMajor ? blockRows : blockCols;
360 m_blockInnerSize = IsRowMajor ? blockCols : blockRows;
361 m_outerIndex = Map<const decltype(m_outerIndex)>(outerPtr, m_blockOuterSize + 1);
362 m_innerIndex = Map<const decltype(m_innerIndex)>(innerPtr, nnzBlocks);
363 resizeBlockStorage_(nnzBlocks);
364 m_values.setZero();
365 }
366
367 // -------------------------------------------------------------------------
368 // Assembly
369 // -------------------------------------------------------------------------
370
381 template <typename InputIterator>
382 void setFromTriplets(InputIterator begin, InputIterator end);
383
384 // -------------------------------------------------------------------------
385 // Conversion to / from SparseMatrix
386 // -------------------------------------------------------------------------
387
395
406
409
410 // -------------------------------------------------------------------------
411 // Element access
412 // -------------------------------------------------------------------------
413
415 Scalar coeff(Index row, Index col) const {
416 eigen_assert(row >= 0 && row < rows() && col >= 0 && col < cols());
417 Index bOuter = IsRowMajor ? (row / BlockRows_) : (col / BlockCols_);
418 Index bInner = IsRowMajor ? (col / BlockCols_) : (row / BlockRows_);
419 Index localRow = row % BlockRows_;
420 Index localCol = col % BlockCols_;
421 const StorageIndex* beg = m_innerIndex.data() + m_outerIndex(bOuter);
422 const StorageIndex* fin = m_innerIndex.data() + m_outerIndex(bOuter + 1);
423 const StorageIndex* it = std::lower_bound(beg, fin, StorageIndex(bInner));
424 if (it == fin || *it != bInner) return Scalar(0);
425 return blockRef(static_cast<Index>(it - m_innerIndex.data()))(localRow, localCol);
426 }
427
436 constexpr Index OuterB = IsRowMajor ? BlockRows_ : BlockCols_;
437 constexpr Index InnerB = IsRowMajor ? BlockCols_ : BlockRows_;
438 const Index diagSize = numext::mini(rows(), cols());
440
441 for (Index out = 0; out < m_blockOuterSize; ++out) {
442 const Index scalarOuterBegin = out * OuterB;
443 if (scalarOuterBegin >= diagSize) break;
444 const Index scalarOuterEnd = numext::mini(scalarOuterBegin + OuterB, diagSize);
445
446 // Group consecutive scalar positions that share the same inner block, then
447 // binary-search once per group rather than once per scalar element.
448 // For square blocks this loop runs exactly once per outer slice.
449 Index i = scalarOuterBegin;
450 while (i < scalarOuterEnd) {
451 const Index bInner = i / InnerB;
452 const Index groupEnd = numext::mini((bInner + 1) * InnerB, scalarOuterEnd);
453
454 const StorageIndex* beg = m_innerIndex.data() + m_outerIndex(out);
455 const StorageIndex* fin = m_innerIndex.data() + m_outerIndex(out + 1);
456 const StorageIndex* it = std::lower_bound(beg, fin, StorageIndex(bInner));
457 if (it != fin && *it == StorageIndex(bInner)) {
458 const ConstBlockMap blk = blockRef(static_cast<Index>(it - m_innerIndex.data()));
459 for (Index j = i; j < groupEnd; ++j) {
460 const Index localRow = IsRowMajor ? (j % OuterB) : (j % InnerB);
461 const Index localCol = IsRowMajor ? (j % InnerB) : (j % OuterB);
462 diag(j) = blk(localRow, localCol);
463 }
464 }
465 i = groupEnd;
466 }
467 }
468 return diag;
469 }
470
471 // -------------------------------------------------------------------------
472 // Arithmetic
473 // -------------------------------------------------------------------------
474
476 BlockSparseMatrix operator+(const BlockSparseMatrix& other) const { return disjunctionWith_(other, AddOp_{}); }
477
479 BlockSparseMatrix operator-(const BlockSparseMatrix& other) const { return disjunctionWith_(other, SubOp_{}); }
480
482 BlockSparseMatrix cwiseProduct(const BlockSparseMatrix& other) const {
483 return conjunctionWith_(other, CwiseMulOp_{});
484 }
485
487 template <typename ScalarFunc>
488 BlockSparseMatrix unaryExpr(ScalarFunc func) const {
489 return withValues_([&func](const auto& v) { return v.unaryExpr(func); });
490 }
491
501 template <typename ScalarFunc>
502 BlockSparseMatrix disjunctionExpr(const BlockSparseMatrix& other, ScalarFunc func) const {
503 return disjunctionWith_(other, DisjExprAdapter_<ScalarFunc>{func});
504 }
505
508 template <typename ScalarFunc>
509 BlockSparseMatrix conjunctionExpr(const BlockSparseMatrix& other, ScalarFunc func) const {
510 return conjunctionWith_(other, [&func](const auto& a, const auto& b) { return a.binaryExpr(b, func); });
511 }
512
514 BlockSparseMatrix operator-() const {
515 return withValues_([](const auto& v) { return -v; });
516 }
517
518 BlockSparseMatrix& operator+=(const BlockSparseMatrix& other) { return *this = *this + other; }
519 BlockSparseMatrix& operator-=(const BlockSparseMatrix& other) { return *this = *this - other; }
520
522 BlockSparseMatrix operator*(const Scalar& s) const {
523 return withValues_([&s](const auto& v) { return v * s; });
524 }
525 BlockSparseMatrix& operator*=(const Scalar& s) {
526 m_values.head(nonZeros()) *= s;
527 return *this;
528 }
529 BlockSparseMatrix operator/(const Scalar& s) const {
530 return withValues_([&s](const auto& v) { return v / s; });
531 }
532 BlockSparseMatrix& operator/=(const Scalar& s) { return *this *= (Scalar(1) / s); }
533
535 friend BlockSparseMatrix operator*(const Scalar& s, const BlockSparseMatrix& m) { return m * s; }
536
553 template <typename OtherDerived>
555 EIGEN_STATIC_ASSERT(
556 (std::is_same<Scalar, typename OtherDerived::Scalar>::value),
557 YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY)
559 }
560
571 template <typename OtherDerived>
573 const BlockSparseMatrix& bsm) {
574 EIGEN_STATIC_ASSERT(
575 (std::is_same<Scalar_, typename OtherDerived::Scalar>::value),
576 YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY)
578 }
579
592 template <int RhsBlockCols>
595
596 // -------------------------------------------------------------------------
597 // Approximate equality (useful for testing)
598 // -------------------------------------------------------------------------
599 bool isApprox(const BlockSparseMatrix& other,
600 const typename NumTraits<Scalar>::Real& prec = NumTraits<Scalar>::dummy_precision()) const {
601 using RealScalar = typename NumTraits<Scalar>::Real;
602 // Frobenius-norm comparison, matching SparseMatrixBase::isApprox semantics
603 // but computed directly from the block values — no scalar-level SparseMatrix
604 // materialization of either operand. Explicit zero blocks contribute 0 to
605 // every norm, so the result is independent of structural differences.
606 RealScalar n2a = m_values.head(nonZeros()).matrix().squaredNorm();
607 RealScalar n2b = other.m_values.head(other.nonZeros()).matrix().squaredNorm();
608 BlockSparseMatrix diff = *this - other;
609 RealScalar d2 = diff.m_values.head(diff.nonZeros()).matrix().squaredNorm();
610 return d2 <= prec * prec * numext::mini(n2a, n2b);
611 }
612
613 // -------------------------------------------------------------------------
614 // Transpose / adjoint
615 // -------------------------------------------------------------------------
616
619 BlockSparseMatrix<Scalar_, Options_, BlockCols_, BlockRows_, StorageIndex_> transpose() const;
620
623 BlockSparseMatrix<Scalar_, Options_, BlockCols_, BlockRows_, StorageIndex_> adjoint() const;
624
625 // -------------------------------------------------------------------------
626 // View factories
627 // -------------------------------------------------------------------------
628
639 template <int Mode, bool DiagIsTriangular = false>
640 BlockSparseTriangularView<BlockSparseMatrix, Mode, DiagIsTriangular> triangularView() const {
641 return BlockSparseTriangularView<BlockSparseMatrix, Mode, DiagIsTriangular>(*this);
642 }
643
660 template <int UpLo, bool DiagIsSelfAdjoint = false>
661 BlockSparseSelfAdjointView<BlockSparseMatrix, UpLo, DiagIsSelfAdjoint> selfadjointView() const {
662 EIGEN_STATIC_ASSERT(BlockRows_ == BlockCols_, THIS_METHOD_IS_ONLY_FOR_SQUARE_BLOCK_MATRICES)
663 return BlockSparseSelfAdjointView<BlockSparseMatrix, UpLo, DiagIsSelfAdjoint>(*this);
664 }
665
666 private:
667 struct AddOp_ {
668 template <typename A, typename B>
669 BlockType operator()(const A& a, const B& b) const {
670 return a + b;
671 }
672 template <typename A>
673 BlockType lhs(const A& a) const {
674 return a;
675 }
676 template <typename B>
677 BlockType rhs(const B& b) const {
678 return b;
679 }
680 };
681
682 struct SubOp_ {
683 template <typename A, typename B>
684 BlockType operator()(const A& a, const B& b) const {
685 return a - b;
686 }
687 template <typename A>
688 BlockType lhs(const A& a) const {
689 return a;
690 }
691 template <typename B>
692 BlockType rhs(const B& b) const {
693 return -b;
694 }
695 };
696
697 // Scalar-to-block adapter for disjunctionExpr: translates scalar functor with lhs/rhs methods
698 // to the block level.
699 template <typename ScalarFunc>
700 struct DisjExprAdapter_ {
701 ScalarFunc func_;
702 template <typename A, typename B>
703 BlockType operator()(const A& a, const B& b) const {
704 return a.binaryExpr(b, func_);
705 }
706 template <typename A>
707 BlockType lhs(const A& a) const {
708 return a.unaryExpr([this](const Scalar& x) { return func_.lhs(x); });
709 }
710 template <typename B>
711 BlockType rhs(const B& b) const {
712 return b.unaryExpr([this](const Scalar& x) { return func_.rhs(x); });
713 }
714 };
715
716 struct CwiseMulOp_ {
717 template <typename A, typename B>
718 BlockType operator()(const A& a, const B& b) const {
719 return a.cwiseProduct(b);
720 }
721 };
722
723 // Returns a copy with the same sparsity structure but m_values replaced by f(m_values).
724 // f receives the flat Eigen Array of the logical (non-zero) coefficients and returns any
725 // compatible expression. Only the structure is copied — the source values are never
726 // duplicated, and unused tail capacity is neither copied nor evaluated.
727 template <typename F>
728 BlockSparseMatrix withValues_(F f) const {
729 Index nnz = nonZeroBlocks();
730 BlockSparseMatrix result(blockRows(), blockCols());
731 result.m_outerIndex = m_outerIndex;
732 result.m_innerIndex = m_innerIndex.head(nnz);
733 result.m_values = f(m_values.head(nnz * BlockSize));
734 return result;
735 }
736
737 // Disjunction (union-pattern): result has a block wherever *this OR other has one.
738 // lhs-only: block copied from *this unchanged
739 // rhs-only: op(b) — unary overload of op
740 // both: op(a, b) — binary overload of op
741 template <typename Op>
742 BlockSparseMatrix disjunctionWith_(const BlockSparseMatrix& other, Op op) const {
743 eigen_assert(blockRows() == other.blockRows() && blockCols() == other.blockCols() &&
744 "BlockSparseMatrix size mismatch");
745 BlockSparseMatrix result(blockRows(), blockCols());
746 result.resizeBlockStorage_(nonZeroBlocks() + other.nonZeroBlocks());
747 Index nnz = 0;
748 for (Index j = 0; j < m_blockOuterSize; ++j) {
749 result.m_outerIndex(j) = StorageIndex_(nnz);
750 Index aId = m_outerIndex(j);
751 Index aEnd = m_outerIndex(j + 1);
752 Index bId = other.m_outerIndex(j);
753 Index bEnd = other.m_outerIndex(j + 1);
754 while (aId < aEnd || bId < bEnd) {
755 bool hasA = aId < aEnd;
756 bool hasB = bId < bEnd;
757 Index aInner = hasA ? Index(m_innerIndex(aId)) : -1;
758 Index bInner = hasB ? Index(other.m_innerIndex(bId)) : -1;
759 // Write the result block in place; the op overloads return a BlockType,
760 // which is assigned straight into the result's storage Map.
761 if (hasA && (!hasB || aInner < bInner)) {
762 result.m_innerIndex(nnz) = StorageIndex_(aInner);
763 result.blockRef(nnz) = op.lhs(blockRef(aId++));
764 } else if (hasB && (!hasA || bInner < aInner)) {
765 result.m_innerIndex(nnz) = StorageIndex_(bInner);
766 result.blockRef(nnz) = op.rhs(other.blockRef(bId++));
767 } else {
768 result.m_innerIndex(nnz) = StorageIndex_(aInner);
769 result.blockRef(nnz) = op(blockRef(aId++), other.blockRef(bId++));
770 }
771 ++nnz;
772 }
773 }
774 result.m_outerIndex(m_blockOuterSize) = StorageIndex_(nnz);
775 result.conservativeResizeBlockStorage_(nnz);
776 return result;
777 }
778
779 // Conjunction (intersection-pattern): result has a block only where *this AND other both have one.
780 template <typename BinaryOp>
781 BlockSparseMatrix conjunctionWith_(const BlockSparseMatrix& other, BinaryOp func) const {
782 eigen_assert(blockRows() == other.blockRows() && blockCols() == other.blockCols() &&
783 "BlockSparseMatrix size mismatch");
784 BlockSparseMatrix result(blockRows(), blockCols());
785 result.resizeBlockStorage_((std::min)(nonZeroBlocks(), other.nonZeroBlocks()));
786 Index nnz = 0;
787 for (Index j = 0; j < m_blockOuterSize; ++j) {
788 result.m_outerIndex(j) = StorageIndex_(nnz);
789 Index aId = m_outerIndex(j);
790 Index aEnd = m_outerIndex(j + 1);
791 Index bId = other.m_outerIndex(j);
792 Index bEnd = other.m_outerIndex(j + 1);
793 while (aId < aEnd && bId < bEnd) {
794 Index aInner = m_innerIndex(aId);
795 Index bInner = other.m_innerIndex(bId);
796 if (aInner < bInner) {
797 ++aId;
798 } else if (bInner < aInner) {
799 ++bId;
800 } else {
801 result.m_innerIndex(nnz) = StorageIndex_(aInner);
802 result.blockRef(nnz) = func(blockRef(aId++), other.blockRef(bId++));
803 ++nnz;
804 }
805 }
806 }
807 result.m_outerIndex(m_blockOuterSize) = StorageIndex_(nnz);
808 result.conservativeResizeBlockStorage_(nnz);
809 return result;
810 }
811
812 template <bool Conjugate>
813 BlockSparseMatrix<Scalar_, Options_, BlockCols_, BlockRows_, StorageIndex_> transposeImpl() const;
814
815 // Resize both block storage arrays non-conservatively and update the capacity counter.
816 void resizeBlockStorage_(Index n) {
817 m_innerIndex.resize(n);
818 m_values.resize(n * BlockSize);
819 }
820
821 void conservativeResizeBlockStorage_(Index n) {
822 m_innerIndex.conservativeResize(n);
823 m_values.conservativeResize(n * BlockSize);
824 }
825
826 // -------------------------------------------------------------------------
827 // Storage
828 // -------------------------------------------------------------------------
829 Index m_blockOuterSize = 0; // block-cols (ColMajor) or block-rows (RowMajor)
830 Index m_blockInnerSize = 0; // block-rows (ColMajor) or block-cols (RowMajor)
831
832 Array<StorageIndex, Dynamic, 1> m_outerIndex = // size: m_blockOuterSize + 1
833 decltype(m_outerIndex)::Zero(m_blockOuterSize + 1);
834 Array<StorageIndex, Dynamic, 1> m_innerIndex;
835 // Block values stored consecutively; block k occupies
836 // m_values[k*BlockSize .. (k+1)*BlockSize - 1].
837 Array<Scalar, Dynamic, 1> m_values;
838
839 // -------------------------------------------------------------------------
840 // MultiInnerIterator
841 //
842 // Simultaneously walks BlockOuterSize_ consecutive outer vectors of a
843 // compressed SparseMatrix, yielding scalar entries one at a time in
844 // non-decreasing inner-index order. On each step the sub-iterator with
845 // the smallest current inner index is the "active" one.
846 //
847 // BlockOuterSize_ is BlockCols_ for ColMajor (one block-column at a time)
848 // and BlockRows_ for RowMajor (one block-row at a time).
849 // -------------------------------------------------------------------------
850 template <typename SparseMatrixType>
851 class MultiInnerIterator {
852 using StorageIndex = typename SparseMatrixType::StorageIndex;
853 static constexpr int BlockOuterSize_ = IsRowMajor ? BlockRows_ : BlockCols_;
854
855 public:
856 MultiInnerIterator(const SparseMatrixType& mat, Index outerBase)
857 : m_outerPtr(mat.outerIndexPtr()),
858 m_innerPtr(mat.innerIndexPtr()),
859 m_valuePtr(mat.valuePtr()),
860 m_outerBase(outerBase) {
861 for (int k = 0; k < BlockOuterSize_; ++k) m_pos[k] = m_outerPtr[outerBase + k];
862 advance();
863 }
864
865 EIGEN_STRONG_INLINE operator bool() const { return m_valid; }
866
867 EIGEN_STRONG_INLINE MultiInnerIterator& operator++() {
868 ++m_pos[m_active];
869 advance();
870 return *this;
871 }
872
873 // Absolute outer index of the current entry.
874 EIGEN_STRONG_INLINE Index outer() const { return m_outerBase + m_active; }
875 // Inner index of the current entry.
876 EIGEN_STRONG_INLINE StorageIndex index() const { return m_innerPtr[m_pos[m_active]]; }
877 // Scalar value of the current entry.
878 EIGEN_STRONG_INLINE Scalar value() const { return m_valuePtr[m_pos[m_active]]; }
879
880 private:
881 // Find the sub-iterator with the smallest inner index.
882 void advance() {
883 m_valid = false;
884 for (int k = 0; k < BlockOuterSize_; ++k) {
885 if (m_pos[k] < m_outerPtr[m_outerBase + k + 1]) {
886 if (!m_valid || m_innerPtr[m_pos[k]] < m_innerPtr[m_pos[m_active]]) {
887 m_active = k;
888 m_valid = true;
889 }
890 }
891 }
892 }
893
894 const StorageIndex* m_outerPtr;
895 const StorageIndex* m_innerPtr;
896 const Scalar* m_valuePtr;
897 Index m_outerBase;
898 StorageIndex m_pos[BlockOuterSize_];
899 int m_active = 0;
900 bool m_valid = false;
901 };
902
903 // Allow other instantiations of BlockSparseMatrix to access private members
904 // (needed by operator*).
905 template <typename, int, int, int, typename>
906 friend class BlockSparseMatrix;
907 template <typename, int, bool>
908 friend class BlockSparseTriangularView;
909 template <typename, int, bool>
910 friend class BlockSparseSelfAdjointView;
911};
912
913// =============================================================================
914// Out-of-line method definitions
915// =============================================================================
916
917// -----------------------------------------------------------------------------
918// setFromTriplets
919// -----------------------------------------------------------------------------
920
921template <typename Scalar_, int Options_, int BlockRows_, int BlockCols_, typename StorageIndex_>
922template <typename InputIterator>
924 InputIterator end) {
925 Index n = static_cast<Index>(std::distance(begin, end));
926
927 // Copy triplet coordinates and block values into Eigen arrays.
928 Array<StorageIndex, Dynamic, 1> tOuter(n), tInner(n);
929 Array<Scalar, Dynamic, 1> tValues(n * BlockSize);
930
931 Index k = 0;
932 for (InputIterator it = begin; it != end; ++it, ++k) {
933 eigen_assert(it->row() >= 0 && it->row() < blockRows() && "setFromTriplets: block row out of range");
934 eigen_assert(it->col() >= 0 && it->col() < blockCols() && "setFromTriplets: block col out of range");
935 tOuter(k) = IsRowMajor ? StorageIndex(it->row()) : StorageIndex(it->col());
936 tInner(k) = IsRowMajor ? StorageIndex(it->col()) : StorageIndex(it->row());
937 BlockMap(tValues.data() + k * BlockSize) = it->value();
938 }
939
940 // Order the triplet indices by (outer, inner) using a stable LSD radix sort:
941 // pass 1 buckets by inner, pass 2 by outer. This runs in
942 // O(n + blockInnerSize + blockOuterSize) with sequential writes, avoiding the
943 // cache-unfriendly indirect comparison sort.
944 Array<Index, Dynamic, 1> order(n), scratch(n);
945 {
946 Array<Index, Dynamic, 1> count = Array<Index, Dynamic, 1>::Zero(m_blockInnerSize + 1);
947 for (Index i = 0; i < n; ++i) count(tInner(i) + 1)++;
948 for (Index i = 0; i < m_blockInnerSize; ++i) count(i + 1) += count(i);
949 for (Index i = 0; i < n; ++i) order(count(tInner(i))++) = i;
950 }
951 {
952 Array<Index, Dynamic, 1> count = Array<Index, Dynamic, 1>::Zero(m_blockOuterSize + 1);
953 for (Index i = 0; i < n; ++i) count(tOuter(i) + 1)++;
954 for (Index i = 0; i < m_blockOuterSize; ++i) count(i + 1) += count(i);
955 for (Index i = 0; i < n; ++i) {
956 Index idx = order(i);
957 scratch(count(tOuter(idx))++) = idx;
958 }
959 order.swap(scratch);
960 }
961
962 // Reset and pre-allocate (worst case: all n triplets are distinct blocks).
963 m_outerIndex.resize(m_blockOuterSize + 1);
964 m_outerIndex.setZero();
965 resizeBlockStorage_(n);
966
967 Index nnz = 0;
968 k = 0;
969 while (k < n) {
970 Index pi = order(k);
971 StorageIndex outer = tOuter(pi);
972 StorageIndex inner = tInner(pi);
973
974 BlockType block = ConstBlockMap(tValues.data() + pi * BlockSize);
975 ++k;
976
977 // Accumulate duplicate entries at the same (outer, inner) position.
978 while (k < n) {
979 Index pk = order(k);
980 if (tOuter(pk) != outer || tInner(pk) != inner) break;
981 block += ConstBlockMap(tValues.data() + pk * BlockSize);
982 ++k;
983 }
984
985 m_innerIndex(nnz) = inner;
986 blockRef(nnz) = block;
987 m_outerIndex(outer + 1)++;
988 ++nnz;
989 }
990
991 // Trim to actual number of unique blocks.
992 conservativeResizeBlockStorage_(nnz);
993
994 // Convert per-outer block counts to prefix sums.
995 for (Index j = 0; j < m_blockOuterSize; ++j) {
996 m_outerIndex(j + 1) += m_outerIndex(j);
997 }
998}
999
1000// -----------------------------------------------------------------------------
1001// toSparse
1002// -----------------------------------------------------------------------------
1003
1004template <typename Scalar_, int Options_, int BlockRows_, int BlockCols_, typename StorageIndex_>
1008 result.reserve(nonZeroBlocks() * BlockSize);
1009
1010 if (!IsRowMajor) {
1011 // ColMajor: outer = block-column j. Emit scalar columns j*BlockCols+c
1012 // in order c = 0..BlockCols-1. Within each scalar column, blocks are
1013 // sorted by bi (block-row), so scalar rows bi*BlockRows+r are increasing.
1014 for (Index j = 0; j < m_blockOuterSize; ++j) {
1015 for (Index c = 0; c < BlockCols_; ++c) {
1016 result.startVec(j * BlockCols_ + c);
1017 for (Index id = m_outerIndex(j); id < m_outerIndex(j + 1); ++id) {
1018 Index bi = m_innerIndex(id);
1019 ConstBlockMap blk = blockRef(id);
1020 for (Index r = 0; r < BlockRows_; ++r) {
1021 result.insertBack(bi * BlockRows_ + r, j * BlockCols_ + c) = blk(r, c);
1022 }
1023 }
1024 }
1025 }
1026 } else {
1027 // RowMajor: outer = block-row bi. Emit scalar rows bi*BlockRows+r
1028 // in order r = 0..BlockRows-1. Within each scalar row, blocks are
1029 // sorted by j (block-col), so scalar cols j*BlockCols+c are increasing.
1030 for (Index bi = 0; bi < m_blockOuterSize; ++bi) {
1031 for (Index r = 0; r < BlockRows_; ++r) {
1032 result.startVec(bi * BlockRows_ + r);
1033 for (Index id = m_outerIndex(bi); id < m_outerIndex(bi + 1); ++id) {
1034 Index j = m_innerIndex(id);
1035 ConstBlockMap blk = blockRef(id);
1036 for (Index c = 0; c < BlockCols_; ++c) {
1037 result.insertBack(bi * BlockRows_ + r, j * BlockCols_ + c) = blk(r, c);
1038 }
1039 }
1040 }
1041 }
1042 }
1043
1044 result.finalize();
1045 return result;
1046}
1047
1048// -----------------------------------------------------------------------------
1049// fromSparse
1050// -----------------------------------------------------------------------------
1051
1052template <typename Scalar_, int Options_, int BlockRows_, int BlockCols_, typename StorageIndex_>
1056 eigen_assert(sp.rows() % BlockRows_ == 0 && "matrix rows not divisible by BlockRows");
1057 eigen_assert(sp.cols() % BlockCols_ == 0 && "matrix cols not divisible by BlockCols");
1058 eigen_assert(sp.isCompressed() && "fromSparse requires a compressed SparseMatrix");
1059
1060 Index bRows = sp.rows() / BlockRows_;
1061 Index bCols = sp.cols() / BlockCols_;
1062
1063 // BlockOuterSize: how many consecutive outer vectors form one block-outer strip.
1064 // BlockInnerSize: the inner dimension of each block.
1065 constexpr Index BlockOuterSize = IsRowMajor ? BlockRows_ : BlockCols_;
1066 constexpr Index BlockInnerSize = IsRowMajor ? BlockCols_ : BlockRows_;
1067 constexpr StorageIndex_ kEmptyIndex = -1;
1068
1070
1071 BlockSparseMatrix result(bRows, bCols);
1072
1073 // Pass 1: count the number of unique block-inner indices per block-outer,
1074 // by scanning each group of BlockOuterSize consecutive outer vectors together.
1075 for (Index outerBlock = 0; outerBlock < result.m_blockOuterSize; ++outerBlock) {
1076 StorageIndex_ prevInnerBlock = kEmptyIndex;
1077 for (MultiInnerIterator<SpMat> it(sp, outerBlock * BlockOuterSize); it; ++it) {
1078 StorageIndex_ innerBlock = it.index() / StorageIndex_(BlockInnerSize);
1079 if (innerBlock != prevInnerBlock) {
1080 result.m_outerIndex(outerBlock + 1)++;
1081 prevInnerBlock = innerBlock;
1082 }
1083 }
1084 }
1085
1086 // Prefix sum → result.m_outerIndex becomes the standard CSC/CSR outer pointer.
1087 for (Index j = 0; j < result.m_blockOuterSize; ++j) result.m_outerIndex(j + 1) += result.m_outerIndex(j);
1088
1089 Index nBlocks = result.m_outerIndex(result.m_blockOuterSize);
1090 result.resizeBlockStorage_(nBlocks);
1091 result.m_values.setZero();
1092
1093 // Pass 2: scatter each scalar entry directly into its position within the
1094 // pre-zeroed block value array.
1095 for (Index outerBlock = 0; outerBlock < result.m_blockOuterSize; ++outerBlock) {
1096 Index blockId = result.m_outerIndex(outerBlock) - 1; // incremented on first new block
1097 StorageIndex_ prevInnerBlock = kEmptyIndex;
1098
1099 for (MultiInnerIterator<SpMat> it(sp, outerBlock * BlockOuterSize); it; ++it) {
1100 Index absOuter = it.outer(); // absolute outer index in sp
1101 StorageIndex_ innerIdx = it.index(); // inner index in sp
1102 StorageIndex_ innerBlock = innerIdx / StorageIndex_(BlockInnerSize);
1103
1104 if (innerBlock != prevInnerBlock) {
1105 ++blockId;
1106 result.m_innerIndex(blockId) = innerBlock;
1107 prevInnerBlock = innerBlock;
1108 }
1109
1110 // Scatter into block storage (layout matches the BSM's Options_).
1111 // ColMajor blocks: col * BlockRows_ + row = localOuter * BlockInnerSize + localInner
1112 // RowMajor blocks: row * BlockCols_ + col = localOuter * BlockInnerSize + localInner
1113 Index localOuter = absOuter % BlockOuterSize;
1114 Index localInner = innerIdx % BlockInnerSize;
1115 Index offset = localOuter * BlockInnerSize + localInner;
1116
1117 result.m_values(blockId * BlockSize + offset) = it.value();
1118 }
1119 }
1120
1121 return result;
1122}
1123
1124// -----------------------------------------------------------------------------
1125// operator* (block-sparse product)
1126// -----------------------------------------------------------------------------
1127
1128template <typename Scalar_, int Options_, int BlockRows_, int BlockCols_, typename StorageIndex_>
1129template <int RhsBlockCols>
1132 const BlockSparseMatrix<Scalar_, Options_, BlockCols_, RhsBlockCols, StorageIndex_>& rhs) const {
1133 using RhsMatrix = BlockSparseMatrix<Scalar_, Options_, BlockCols_, RhsBlockCols, StorageIndex_>;
1134 using ResultMatrix = BlockSparseMatrix<Scalar_, Options_, BlockRows_, RhsBlockCols, StorageIndex_>;
1136 constexpr int ResultBlockSize = BlockRows_ * RhsBlockCols;
1137
1138 eigen_assert(blockCols() == rhs.blockRows() && "BlockSparseMatrix product: lhs.blockCols() != rhs.blockRows()");
1139
1140 Index cBlockRows = blockRows();
1141 Index cBlockCols = rhs.blockCols();
1142 ResultMatrix result(cBlockRows, cBlockCols);
1143
1144 // For ColMajor: mask / accum indexed by block-row (size = cBlockRows).
1145 // For RowMajor: mask / accum indexed by block-col (size = cBlockCols).
1146 Index maskSize = IsRowMajor ? cBlockCols : cBlockRows;
1148 Array<Scalar_, Dynamic, 1> accumData(maskSize * ResultBlockSize);
1149 Array<Index, Dynamic, 1> indices(maskSize);
1150 Index nIndices = 0;
1151 // The mask is read and written through coeff/coeffRef below: the block indices
1152 // are in range by construction, and operator()'s bounds check gives GCC 14+ a
1153 // false -Wstringop-overflow on a negative StorageIndex_.
1154
1155 // Grow result storage geometrically rather than pre-allocating the dense
1156 // worst case (cBlockRows*cBlockCols blocks): the product is typically far
1157 // sparser than that, so the dense bound would blow up peak memory.
1158 // capacity is always kept <= maxResultNnz (the true upper bound), and since
1159 // any single outer emits at most maskSize blocks, the initial estimate of
1160 // maskSize guarantees the first outer fits before the first grow check.
1161 Index cOuterSize = result.m_blockOuterSize;
1162 Index maxResultNnz = cBlockRows * cBlockCols;
1163 Index capacity = numext::mini(maxResultNnz, numext::maxi(maskSize, nonZeroBlocks() + rhs.nonZeroBlocks()));
1164 result.resizeBlockStorage_(capacity);
1165 Index nnz = 0;
1166
1167 for (Index out = 0; out < cOuterSize; ++out) {
1168 result.m_outerIndex(out) = StorageIndex_(nnz);
1169
1170 if (!IsRowMajor) {
1171 // ColMajor: out is block-column j of the result.
1172 // For each block B(k,j) and each block A(bi,k): C(bi,j) += A(bi,k)*B(k,j).
1173 Index j = out;
1174 for (Index rhsId = rhs.m_outerIndex(j); rhsId < rhs.m_outerIndex(j + 1); ++rhsId) {
1175 Index k = rhs.m_innerIndex(rhsId);
1176 typename RhsMatrix::ConstBlockMap Bkj = rhs.blockRef(rhsId);
1177 for (Index lhsId = m_outerIndex(k); lhsId < m_outerIndex(k + 1); ++lhsId) {
1178 Index bi = m_innerIndex(lhsId);
1179 if (!mask.coeff(bi)) {
1180 mask.coeffRef(bi) = 1;
1181 Map<ResultBlock>(accumData.data() + bi * ResultBlockSize).noalias() = blockRef(lhsId) * Bkj;
1182 indices(nIndices++) = bi;
1183 } else {
1184 Map<ResultBlock>(accumData.data() + bi * ResultBlockSize).noalias() += blockRef(lhsId) * Bkj;
1185 }
1186 }
1187 }
1188 } else {
1189 // RowMajor: out is block-row bi of the result.
1190 // For each block A(bi,k) and each block B(k,j): C(bi,j) += A(bi,k)*B(k,j).
1191 Index bi = out;
1192 for (Index lhsId = m_outerIndex(bi); lhsId < m_outerIndex(bi + 1); ++lhsId) {
1193 Index k = m_innerIndex(lhsId);
1194 ConstBlockMap Aik = blockRef(lhsId);
1195 for (Index rhsId = rhs.m_outerIndex(k); rhsId < rhs.m_outerIndex(k + 1); ++rhsId) {
1196 Index j = rhs.m_innerIndex(rhsId);
1197 if (!mask.coeff(j)) {
1198 mask.coeffRef(j) = 1;
1199 Map<ResultBlock>(accumData.data() + j * ResultBlockSize).noalias() = Aik * rhs.blockRef(rhsId);
1200 indices(nIndices++) = j;
1201 } else {
1202 Map<ResultBlock>(accumData.data() + j * ResultBlockSize).noalias() += Aik * rhs.blockRef(rhsId);
1203 }
1204 }
1205 }
1206 }
1207
1208 // Sort the accumulated indices so the result's inner index array is sorted.
1209 std::sort(indices.data(), indices.data() + nIndices);
1210 if (nnz + nIndices > capacity) {
1211 capacity = numext::mini(maxResultNnz, numext::maxi(2 * capacity, nnz + nIndices));
1212 result.conservativeResizeBlockStorage_(capacity);
1213 }
1214 for (Index ki = 0; ki < nIndices; ++ki) {
1215 Index idx = indices(ki);
1216 result.m_innerIndex(nnz) = StorageIndex_(idx);
1217 result.blockRef(nnz) = Map<ResultBlock>(accumData.data() + idx * ResultBlockSize);
1218 mask.coeffRef(idx) = 0;
1219 ++nnz;
1220 }
1221 nIndices = 0;
1222 }
1223 result.m_outerIndex(cOuterSize) = StorageIndex_(nnz);
1224
1225 // Trim to actual number of result blocks.
1226 result.conservativeResizeBlockStorage_(nnz);
1227
1228 return result;
1229}
1230
1231// =============================================================================
1232// BlockSparseTriangularView
1233// =============================================================================
1234
1250template <typename BSM, int Mode, bool DiagIsTriangular = false>
1251class BlockSparseTriangularView {
1252 public:
1253 using Scalar = typename BSM::Scalar;
1254 using StorageIndex = typename BSM::StorageIndex;
1255 using BlockType = typename BSM::BlockType;
1256 using BlockMap = typename BSM::BlockMap;
1257 using ConstBlockMap = typename BSM::ConstBlockMap;
1258 static constexpr int BlockRows = BSM::BlockRows;
1259 static constexpr int BlockCols = BSM::BlockCols;
1260 static constexpr int BlockSize = BSM::BlockSize;
1261 static constexpr bool IsRowMajor = BSM::IsRowMajor;
1262 static constexpr bool IsUpper = (Mode & Upper) != 0;
1263
1264 explicit BlockSparseTriangularView(const BSM& m) : m_matrix(m) {}
1265
1266 Index rows() const { return m_matrix.rows(); }
1267 Index cols() const { return m_matrix.cols(); }
1268
1269 // ---- Materialize ---------------------------------------------------------
1270
1274 BSM eval() const {
1275 constexpr int ZeroMode = IsUpper ? StrictlyLower : StrictlyUpper;
1276 const BSM& m = m_matrix;
1277 BSM result(m.blockRows(), m.blockCols());
1278 result.resizeBlockStorage_(m.nonZeroBlocks());
1279 Index nnz = 0;
1280
1281 for (Index out = 0; out < m.m_blockOuterSize; ++out) {
1282 result.m_outerIndex(out) = StorageIndex(nnz);
1283 for (Index id = m.m_outerIndex(out); id < m.m_outerIndex(out + 1); ++id) {
1284 Index inner = m.m_innerIndex(id);
1285 Index bi = IsRowMajor ? out : inner;
1286 Index bj = IsRowMajor ? inner : out;
1287 if (IsUpper ? (bj < bi) : (bj > bi)) continue;
1288 result.m_innerIndex(nnz) = StorageIndex(inner);
1289 result.m_values.template segment<BlockSize>(nnz * BlockSize) =
1290 m.m_values.template segment<BlockSize>(id * BlockSize);
1291 EIGEN_IF_CONSTEXPR (!DiagIsTriangular) {
1292 if (bi == bj)
1293 BlockMap(result.m_values.data() + nnz * BlockSize).template triangularView<ZeroMode>().setZero();
1294 }
1295 ++nnz;
1296 }
1297 }
1298 result.m_outerIndex(m.m_blockOuterSize) = StorageIndex(nnz);
1299 result.conservativeResizeBlockStorage_(nnz);
1300 return result;
1301 }
1302
1305
1306 // ---- Arithmetic ----------------------------------------------------------
1307
1308 BSM operator+(const BlockSparseTriangularView& other) const { return eval() + other.eval(); }
1309 BSM operator-(const BlockSparseTriangularView& other) const { return eval() - other.eval(); }
1310
1312 template <int RhsBlockCols>
1317
1318 // ---- Dense products (no intermediate materialisation) --------------------
1319
1320 template <typename OtherDerived>
1322 EIGEN_STATIC_ASSERT(
1323 (std::is_same<Scalar, typename OtherDerived::Scalar>::value),
1324 YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY)
1325 eigen_assert(m_matrix.cols() == rhs.rows() && "BlockSparseTriangularView * Dense: dimension mismatch");
1327 ResultType result = ResultType::Zero(m_matrix.rows(), rhs.cols());
1328 for (Index out = 0; out < m_matrix.m_blockOuterSize; ++out) {
1329 for (Index id = m_matrix.m_outerIndex(out); id < m_matrix.m_outerIndex(out + 1); ++id) {
1330 Index inner = m_matrix.m_innerIndex(id);
1331 Index bi = IsRowMajor ? out : inner;
1332 Index bj = IsRowMajor ? inner : out;
1333 if (IsUpper ? (bj < bi) : (bj > bi)) continue;
1334 constexpr int DiagMode = IsUpper ? Upper : Lower;
1335 if (!DiagIsTriangular && bi == bj)
1336 result.template middleRows<BlockRows>(bi * BlockRows).noalias() +=
1337 m_matrix.blockRef(id).template triangularView<DiagMode>() *
1338 rhs.template middleRows<BlockCols>(bj * BlockCols);
1339 else
1340 result.template middleRows<BlockRows>(bi * BlockRows).noalias() +=
1341 m_matrix.blockRef(id) * rhs.template middleRows<BlockCols>(bj * BlockCols);
1342 }
1343 }
1344 return result;
1345 }
1346
1347 template <typename OtherDerived>
1348 friend Matrix<Scalar, OtherDerived::RowsAtCompileTime, Dynamic> operator*(const MatrixBase<OtherDerived>& lhs,
1349 const BlockSparseTriangularView& tri) {
1350 EIGEN_STATIC_ASSERT(
1351 (std::is_same<Scalar, typename OtherDerived::Scalar>::value),
1352 YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY)
1353 eigen_assert(lhs.cols() == tri.m_matrix.rows() && "Dense * BlockSparseTriangularView: dimension mismatch");
1354 constexpr bool isRM = BSM::IsRowMajor;
1355 using ResultType = Matrix<Scalar, OtherDerived::RowsAtCompileTime, Dynamic>;
1356 // BSM befriends BlockSparseTriangularView, but GCC <= 11 and MSVC do not
1357 // extend that friendship to the body of a friend function defined inside
1358 // the view (CWG 1699), so only BSM's public interface may be used here.
1359 const BSM& m = tri.m_matrix;
1360 const StorageIndex* outerPtr = m.outerIndexPtr();
1361 const StorageIndex* innerPtr = m.innerIndexPtr();
1362 ResultType result = ResultType::Zero(lhs.rows(), m.cols());
1363 for (Index out = 0; out < m.blockOuterSize(); ++out) {
1364 for (Index id = outerPtr[out]; id < outerPtr[out + 1]; ++id) {
1365 Index inner = innerPtr[id];
1366 Index bi = isRM ? out : inner;
1367 Index bj = isRM ? inner : out;
1368 if (IsUpper ? (bj < bi) : (bj > bi)) continue;
1369 constexpr int DiagMode = IsUpper ? Upper : Lower;
1370 if (!DiagIsTriangular && bi == bj)
1371 result.template middleCols<BlockCols>(bj * BlockCols).noalias() +=
1372 lhs.template middleCols<BlockRows>(bi * BlockRows) * m.blockRef(id).template triangularView<DiagMode>();
1373 else
1374 result.template middleCols<BlockCols>(bj * BlockCols).noalias() +=
1375 lhs.template middleCols<BlockRows>(bi * BlockRows) * m.blockRef(id);
1376 }
1377 }
1378 return result;
1379 }
1380
1381 // ---- Triangular solve -------------------------------------------------------
1382
1389 template <typename Derived>
1391 doSolveImpl<false, false>(x.derived());
1392 }
1393
1396 const BlockSparseTriangularView& m_tri;
1397 template <typename Derived>
1398 void solveInPlace(MatrixBase<Derived>& x) const {
1399 m_tri.template doSolveImpl<true, false>(x.derived());
1400 }
1401 };
1402
1405 const BlockSparseTriangularView& m_tri;
1406 template <typename Derived>
1407 void solveInPlace(MatrixBase<Derived>& x) const {
1408 m_tri.template doSolveImpl<true, true>(x.derived());
1409 }
1410 };
1411
1412 TransposeReturnType transpose() const { return {*this}; }
1413 AdjointReturnType adjoint() const { return {*this}; }
1414
1415 private:
1416 const BSM& m_matrix;
1417
1418 // Non-transposed solve for both storage orders.
1419 //
1420 // diagFirst = (IsUpper == IsRowMajor): ColMajor Lower→first, ColMajor Upper→last,
1421 // RowMajor Lower→last, RowMajor Upper→first.
1422 // Loop direction: forward for Lower, backward for Upper (same for both storage orders).
1423 // ColMajor: solve diagonal first, then scatter x[inner] -= blk * x[k].
1424 // RowMajor: gather x[k] -= blk * x[inner] first, then solve diagonal.
1425 template <typename Derived>
1426 void doSolveDirect(Derived& x) const {
1427 EIGEN_STATIC_ASSERT(BlockRows == BlockCols, THIS_METHOD_IS_ONLY_FOR_SQUARE_BLOCK_MATRICES)
1428 constexpr int DiagMode = IsUpper ? Upper : Lower;
1429 constexpr bool diagFirst = (IsUpper == BSM::IsRowMajor);
1430 Index nb = m_matrix.blockCols(); // == blockRows() for square matrices
1431 eigen_assert(x.rows() == m_matrix.rows() && "solveInPlace: size mismatch");
1432
1433 const StorageIndex* innerPtr = m_matrix.innerIndexPtr();
1434 const StorageIndex* outerPtr = m_matrix.outerIndexPtr();
1435
1436 Index outerStart = IsUpper ? nb - 1 : 0;
1437 Index outerEnd = IsUpper ? -1 : nb;
1438 constexpr Index kStep = IsUpper ? -1 : 1;
1439
1440 for (Index k = outerStart; k != outerEnd; k += kStep) {
1441 const StorageIndex* beg = innerPtr + outerPtr[k];
1442 const StorageIndex* end = innerPtr + outerPtr[k + 1];
1443 if (beg == end) continue;
1444 const StorageIndex* diag_ptr = diagFirst ? beg : end - 1;
1445 const StorageIndex* off_beg = diagFirst ? beg + 1 : beg;
1446 const StorageIndex* off_end = diagFirst ? end : end - 1;
1447 eigen_assert(*diag_ptr == k);
1448 EIGEN_IF_CONSTEXPR (!BSM::IsRowMajor) {
1449 m_matrix.blockRef(diag_ptr - innerPtr)
1450 .template triangularView<DiagMode>()
1451 .solveInPlace(x.template middleRows<BlockRows>(k * BlockRows));
1452 for (const StorageIndex* it = off_beg; it != off_end; ++it)
1453 x.template middleRows<BlockRows>(*it * BlockRows).noalias() -=
1454 m_matrix.blockRef(it - innerPtr) * x.template middleRows<BlockRows>(k * BlockRows);
1455 } else {
1456 for (const StorageIndex* it = off_beg; it != off_end; ++it)
1457 x.template middleRows<BlockRows>(k * BlockRows).noalias() -=
1458 m_matrix.blockRef(it - innerPtr) * x.template middleRows<BlockRows>(*it * BlockRows);
1459 m_matrix.blockRef(diag_ptr - innerPtr)
1460 .template triangularView<DiagMode>()
1461 .solveInPlace(x.template middleRows<BlockRows>(k * BlockRows));
1462 }
1463 }
1464 }
1465
1466 // Transposed/adjoint solve for both storage orders.
1467 //
1468 // Loop direction: forward for Upper, backward for Lower (same for both storage orders).
1469 // ColMajor: gather x[k] -= adj(blk) * x[inner] first, then solve adj(diagonal).
1470 // RowMajor: solve adj(diagonal) first, then scatter x[inner] -= adj(blk) * x[k].
1471 template <bool Conjugate, typename Derived>
1472 void doSolveTransposed(Derived& x) const {
1473 EIGEN_STATIC_ASSERT(BlockRows == BlockCols, THIS_METHOD_IS_ONLY_FOR_SQUARE_BLOCK_MATRICES)
1474 constexpr int DiagMode = IsUpper ? Upper : Lower;
1475 constexpr bool diagFirst = (IsUpper == BSM::IsRowMajor);
1476 Index nb = m_matrix.blockCols(); // == blockRows() for square matrices
1477 eigen_assert(x.rows() == m_matrix.rows() && "solveInPlace: size mismatch");
1478
1479 const StorageIndex* innerPtr = m_matrix.innerIndexPtr();
1480 const StorageIndex* outerPtr = m_matrix.outerIndexPtr();
1481
1482 Index outerStart = IsUpper ? 0 : nb - 1;
1483 Index outerEnd = IsUpper ? nb : -1;
1484 constexpr Index kStep = IsUpper ? 1 : -1;
1485
1486 for (Index k = outerStart; k != outerEnd; k += kStep) {
1487 const StorageIndex* beg = innerPtr + outerPtr[k];
1488 const StorageIndex* end = innerPtr + outerPtr[k + 1];
1489 if (beg == end) continue;
1490 const StorageIndex* diag_ptr = diagFirst ? beg : end - 1;
1491 const StorageIndex* off_beg = diagFirst ? beg + 1 : beg;
1492 const StorageIndex* off_end = diagFirst ? end : end - 1;
1493 eigen_assert(*diag_ptr == k);
1494 EIGEN_IF_CONSTEXPR (!BSM::IsRowMajor) {
1495 for (const StorageIndex* it = off_beg; it != off_end; ++it)
1496 x.template middleRows<BlockRows>(k * BlockRows).noalias() -=
1497 internal::adjoint_if<Conjugate>(m_matrix.blockRef(it - innerPtr)) *
1498 x.template middleRows<BlockRows>(*it * BlockRows);
1499 internal::adjoint_if<Conjugate>(m_matrix.blockRef(diag_ptr - innerPtr).template triangularView<DiagMode>())
1500 .solveInPlace(x.template middleRows<BlockRows>(k * BlockRows));
1501 } else {
1502 internal::adjoint_if<Conjugate>(m_matrix.blockRef(diag_ptr - innerPtr).template triangularView<DiagMode>())
1503 .solveInPlace(x.template middleRows<BlockRows>(k * BlockRows));
1504 for (const StorageIndex* it = off_beg; it != off_end; ++it)
1505 x.template middleRows<BlockRows>(*it * BlockRows).noalias() -=
1506 internal::adjoint_if<Conjugate>(m_matrix.blockRef(it - innerPtr)) *
1507 x.template middleRows<BlockRows>(k * BlockRows);
1508 }
1509 }
1510 }
1511
1512 template <bool Transposed, bool Conjugate, typename Derived>
1513 void doSolveImpl(Derived& x) const {
1514 EIGEN_IF_CONSTEXPR (!Transposed)
1515 doSolveDirect(x);
1516 else
1517 doSolveTransposed<Conjugate>(x);
1518 }
1519};
1520
1521// =============================================================================
1522// BlockSparseSelfAdjointView
1523// =============================================================================
1524
1538template <typename BSM, int UpLo, bool DiagIsSelfAdjoint>
1539class BlockSparseSelfAdjointView {
1540 public:
1541 using Scalar = typename BSM::Scalar;
1542 using StorageIndex = typename BSM::StorageIndex;
1543 using BlockType = typename BSM::BlockType;
1544 using BlockMap = typename BSM::BlockMap;
1545 using ConstBlockMap = typename BSM::ConstBlockMap;
1546 static constexpr int BlockRows = BSM::BlockRows; // == BlockCols
1547 static constexpr int BlockCols = BSM::BlockCols;
1548 static constexpr int BlockSize = BSM::BlockSize;
1549 static constexpr bool IsRowMajor = BSM::IsRowMajor;
1550 static constexpr bool IsUpper = (UpLo & Upper) != 0;
1551 // UpLo passed to Eigen's dense selfadjointView on diagonal blocks:
1552 static constexpr int DiagUpLo = IsUpper ? Upper : Lower;
1553
1554 explicit BlockSparseSelfAdjointView(const BSM& m) : m_matrix(m) {}
1555
1556 Index rows() const { return m_matrix.rows(); }
1557 Index cols() const { return m_matrix.cols(); }
1558
1559 // ---- Materialize ---------------------------------------------------------
1560
1565 BSM eval() const {
1566 const BSM& m = m_matrix;
1567
1568 Index nDiag = 0, nOff = 0;
1569 for (Index out = 0; out < m.m_blockOuterSize; ++out)
1570 for (Index id = m.m_outerIndex(out); id < m.m_outerIndex(out + 1); ++id) {
1571 Index inner = m.m_innerIndex(id);
1572 Index bi = IsRowMajor ? out : inner;
1573 Index bj = IsRowMajor ? inner : out;
1574 if (IsUpper ? (bj < bi) : (bj > bi)) continue;
1575 if (bi == bj)
1576 ++nDiag;
1577 else
1578 ++nOff;
1579 }
1580
1581 Index nTotal = nDiag + 2 * nOff;
1582
1583 Array<StorageIndex, Dynamic, 1> brows(nTotal), bcols(nTotal);
1584 Array<Scalar, Dynamic, 1> bvals(nTotal * BlockSize);
1585
1586 Index k = 0;
1587 for (Index out = 0; out < m.m_blockOuterSize; ++out)
1588 for (Index id = m.m_outerIndex(out); id < m.m_outerIndex(out + 1); ++id) {
1589 Index inner = m.m_innerIndex(id);
1590 Index bi = IsRowMajor ? out : inner;
1591 Index bj = IsRowMajor ? inner : out;
1592 if (IsUpper ? (bj < bi) : (bj > bi)) continue;
1593
1594 brows(k) = StorageIndex(bi);
1595 bcols(k) = StorageIndex(bj);
1596 if (!DiagIsSelfAdjoint && bi == bj)
1597 BlockMap(bvals.data() + k * BlockSize) = m.blockRef(id).template selfadjointView<DiagUpLo>();
1598 else
1599 BlockMap(bvals.data() + k * BlockSize) = m.blockRef(id);
1600 ++k;
1601
1602 if (bi != bj) {
1603 brows(k) = StorageIndex(bj);
1604 bcols(k) = StorageIndex(bi);
1605 BlockMap(bvals.data() + k * BlockSize) = m.blockRef(id).adjoint();
1606 ++k;
1607 }
1608 }
1609
1610 // Sort by (outer, inner) then build BSM directly (no duplicates by construction).
1611 Array<Index, Dynamic, 1> perm(nTotal);
1612 std::iota(perm.data(), perm.data() + nTotal, Index(0));
1613 std::sort(perm.data(), perm.data() + nTotal, [&](Index a, Index b) {
1614 StorageIndex ao = IsRowMajor ? brows(a) : bcols(a);
1615 StorageIndex bo = IsRowMajor ? brows(b) : bcols(b);
1616 if (ao != bo) return ao < bo;
1617 return (IsRowMajor ? bcols(a) : brows(a)) < (IsRowMajor ? bcols(b) : brows(b));
1618 });
1619
1620 BSM result(m.blockRows(), m.blockCols());
1621 result.resizeBlockStorage_(nTotal);
1622
1623 for (Index ki = 0; ki < nTotal; ++ki) {
1624 Index pi = perm(ki);
1625 StorageIndex outer = IsRowMajor ? brows(pi) : bcols(pi);
1626 StorageIndex inner = IsRowMajor ? bcols(pi) : brows(pi);
1627 result.m_outerIndex(outer + 1)++;
1628 result.m_innerIndex(ki) = inner;
1629 result.m_values.template segment<BlockSize>(ki * BlockSize) = bvals.template segment<BlockSize>(pi * BlockSize);
1630 }
1631 for (Index j = 0; j < result.m_blockOuterSize; ++j) result.m_outerIndex(j + 1) += result.m_outerIndex(j);
1632
1633 return result;
1634 }
1635
1638
1639 // ---- Arithmetic ----------------------------------------------------------
1640
1641 BSM operator+(const BlockSparseSelfAdjointView& other) const { return eval() + other.eval(); }
1642 BSM operator-(const BlockSparseSelfAdjointView& other) const { return eval() - other.eval(); }
1643
1645 template <int RhsBlockCols>
1650
1651 // ---- Dense products (no materialisation; exploits both triangles) ---------
1652
1664 template <typename OtherDerived>
1666 EIGEN_STATIC_ASSERT(
1667 (std::is_same<Scalar, typename OtherDerived::Scalar>::value),
1668 YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY)
1669 eigen_assert(m_matrix.cols() == rhs.rows() && "BlockSparseSelfAdjointView * Dense: dimension mismatch");
1671 ResultType result = ResultType::Zero(m_matrix.rows(), rhs.cols());
1672
1673 for (Index out = 0; out < m_matrix.m_blockOuterSize; ++out)
1674 for (Index id = m_matrix.m_outerIndex(out); id < m_matrix.m_outerIndex(out + 1); ++id) {
1675 Index inner = m_matrix.m_innerIndex(id);
1676 Index bi = IsRowMajor ? out : inner;
1677 Index bj = IsRowMajor ? inner : out;
1678 if (IsUpper ? (bj < bi) : (bj > bi)) continue;
1679
1680 if (bi == bj) {
1681 EIGEN_IF_CONSTEXPR (DiagIsSelfAdjoint) {
1682 result.template middleRows<BlockRows>(bi * BlockRows).noalias() +=
1683 m_matrix.blockRef(id) * rhs.template middleRows<BlockCols>(bj * BlockCols);
1684 } else {
1685 // Materialize the tiny diagonal block as a fixed-size Hermitian matrix, then use the
1686 // ordinary (coeff-based for a vector rhs) product. This avoids the runtime-sized,
1687 // EIGEN_DONT_INLINE selfadjoint_matrix_vector_product kernel, which is tuned for large
1688 // matrices and is pure overhead for a 2-4 row block.
1689 BlockType diag = m_matrix.blockRef(id).template selfadjointView<DiagUpLo>();
1690 result.template middleRows<BlockRows>(bi * BlockRows).noalias() +=
1691 diag * rhs.template middleRows<BlockCols>(bj * BlockCols);
1692 }
1693 } else {
1694 result.template middleRows<BlockRows>(bi * BlockRows).noalias() +=
1695 m_matrix.blockRef(id) * rhs.template middleRows<BlockCols>(bj * BlockCols);
1696 result.template middleRows<BlockRows>(bj * BlockRows).noalias() +=
1697 m_matrix.blockRef(id).adjoint() * rhs.template middleRows<BlockRows>(bi * BlockRows);
1698 }
1699 }
1700 return result;
1701 }
1702
1704 template <typename OtherDerived>
1706 const BlockSparseSelfAdjointView& view) {
1707 EIGEN_STATIC_ASSERT(
1708 (std::is_same<Scalar, typename OtherDerived::Scalar>::value),
1709 YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY)
1710 return (view * lhs.adjoint()).adjoint();
1711 }
1712
1713 private:
1714 const BSM& m_matrix;
1715};
1716
1717// =============================================================================
1718// BlockSparseMatrix::transposeImpl / transpose / adjoint (out-of-line)
1719// =============================================================================
1720
1721template <typename Scalar_, int Options_, int BlockRows_, int BlockCols_, typename StorageIndex_>
1722template <bool Conjugate>
1723BlockSparseMatrix<Scalar_, Options_, BlockCols_, BlockRows_, StorageIndex_>
1724BlockSparseMatrix<Scalar_, Options_, BlockRows_, BlockCols_, StorageIndex_>::transposeImpl() const {
1725 using ResultType = BlockSparseMatrix<Scalar_, Options_, BlockCols_, BlockRows_, StorageIndex_>;
1726 ResultType result(blockCols(), blockRows());
1727
1728 // Count entries per new outer (= old inner).
1729 for (Index id = 0; id < nonZeroBlocks(); ++id) result.m_outerIndex(m_innerIndex(id) + 1)++;
1730
1731 // Prefix sum.
1732 for (Index j = 0; j < result.m_blockOuterSize; ++j) result.m_outerIndex(j + 1) += result.m_outerIndex(j);
1733
1734 Index nnz = nonZeroBlocks();
1735 result.resizeBlockStorage_(nnz);
1736
1737 // One insertion cursor per new outer; start at the prefix-sum boundary.
1738 // Because we iterate oldOuter in increasing order, for each newOuter = oldInner
1739 // the emitted newInner = oldOuter values are automatically sorted.
1740 Array<StorageIndex_, Dynamic, 1> pos = result.m_outerIndex.head(result.m_blockOuterSize);
1741
1742 for (Index oldOuter = 0; oldOuter < m_blockOuterSize; ++oldOuter) {
1743 for (Index id = m_outerIndex(oldOuter); id < m_outerIndex(oldOuter + 1); ++id) {
1744 Index newOuter = m_innerIndex(id);
1745 Index insertAt = pos(newOuter)++;
1746 result.m_innerIndex(insertAt) = StorageIndex_(oldOuter);
1747 result.blockRef(insertAt) = internal::adjoint_if<Conjugate>(blockRef(id));
1748 }
1749 }
1750 return result;
1751}
1752
1753template <typename Scalar_, int Options_, int BlockRows_, int BlockCols_, typename StorageIndex_>
1758
1759template <typename Scalar_, int Options_, int BlockRows_, int BlockCols_, typename StorageIndex_>
1764
1765namespace internal {
1766
1767// ---------------------------------------------------------------------------
1768// generic_product_impl: BlockSparse × Dense → Dense
1769// Provides evalTo / addTo / subTo / scaleAndAddTo via generic_product_impl_base.
1770// ---------------------------------------------------------------------------
1771template <typename Lhs, typename Rhs, int ProductType>
1772struct generic_product_impl<Lhs, Rhs, BlockSparseShape, DenseShape, ProductType>
1773 : generic_product_impl_base<Lhs, Rhs, generic_product_impl<Lhs, Rhs, BlockSparseShape, DenseShape, ProductType>> {
1774 using Scalar = typename Product<Lhs, Rhs>::Scalar;
1775
1776 template <typename Dst>
1777 static void scaleAndAddTo(Dst& dst, const Lhs& lhs, const Rhs& rhs, const Scalar& alpha) {
1778 constexpr bool IsRM = (Lhs::Options & RowMajorBit) != 0;
1779 constexpr int BR = Lhs::BlockRows;
1780 constexpr int BC = Lhs::BlockCols;
1781 const typename Lhs::StorageIndex* outerPtr = lhs.outerIndexPtr();
1782 const typename Lhs::StorageIndex* innerPtr = lhs.innerIndexPtr();
1783 // Branch on alpha before the loop: alpha==1 and alpha==-1 avoid creating a
1784 // CwiseUnaryOp<scalar_multiple, B×B_block>, which defeats SIMD for complex scalars.
1785 bool a1 = (alpha == Scalar(1));
1786 bool am1 = (alpha == Scalar(-1));
1787 for (Eigen::Index out = 0; out < lhs.blockOuterSize(); ++out) {
1788 for (Eigen::Index id = outerPtr[out]; id < outerPtr[out + 1]; ++id) {
1789 Eigen::Index inner = innerPtr[id];
1790 Eigen::Index bi = IsRM ? out : inner;
1791 Eigen::Index bj = IsRM ? inner : out;
1792 auto dst_seg = dst.template middleRows<BR>(bi * BR);
1793 auto rhs_seg = rhs.template middleRows<BC>(bj * BC);
1794 if (EIGEN_PREDICT_TRUE(a1))
1795 dst_seg.noalias() += lhs.blockRef(id) * rhs_seg;
1796 else if (am1)
1797 dst_seg.noalias() -= lhs.blockRef(id) * rhs_seg;
1798 else {
1799 // Materialize block×rhs_seg into a small fixed-size stack buffer, then
1800 // scale by alpha. Keeps the B×B block as a plain Map for vectorization.
1802 TmpType tmp(BR, rhs.cols());
1803 tmp.noalias() = lhs.blockRef(id) * rhs_seg;
1804 dst_seg += alpha * tmp;
1805 }
1806 }
1807 }
1808 }
1809};
1810
1811// ---------------------------------------------------------------------------
1812// generic_product_impl: Dense × BlockSparse → Dense
1813// ---------------------------------------------------------------------------
1814template <typename Lhs, typename Rhs, int ProductType>
1815struct generic_product_impl<Lhs, Rhs, DenseShape, BlockSparseShape, ProductType>
1816 : generic_product_impl_base<Lhs, Rhs, generic_product_impl<Lhs, Rhs, DenseShape, BlockSparseShape, ProductType>> {
1817 using Scalar = typename Product<Lhs, Rhs>::Scalar;
1818
1819 template <typename Dst>
1820 static void scaleAndAddTo(Dst& dst, const Lhs& lhs, const Rhs& rhs, const Scalar& alpha) {
1821 constexpr bool IsRM = (Rhs::Options & RowMajorBit) != 0;
1822 constexpr int BR = Rhs::BlockRows;
1823 constexpr int BC = Rhs::BlockCols;
1824 const typename Rhs::StorageIndex* outerPtr = rhs.outerIndexPtr();
1825 const typename Rhs::StorageIndex* innerPtr = rhs.innerIndexPtr();
1826 bool a1 = (alpha == Scalar(1));
1827 bool am1 = (alpha == Scalar(-1));
1828 for (Eigen::Index out = 0; out < rhs.blockOuterSize(); ++out) {
1829 for (Eigen::Index id = outerPtr[out]; id < outerPtr[out + 1]; ++id) {
1830 Eigen::Index inner = innerPtr[id];
1831 Eigen::Index bi = IsRM ? out : inner;
1832 Eigen::Index bj = IsRM ? inner : out;
1833 auto dst_seg = dst.template middleCols<BC>(bj * BC);
1834 auto lhs_seg = lhs.template middleCols<BR>(bi * BR);
1835 if (EIGEN_PREDICT_TRUE(a1))
1836 dst_seg.noalias() += lhs_seg * rhs.blockRef(id);
1837 else if (am1)
1838 dst_seg.noalias() -= lhs_seg * rhs.blockRef(id);
1839 else {
1840 using TmpType = Matrix<Scalar, Lhs::RowsAtCompileTime, BC>;
1841 TmpType tmp(lhs.rows(), BC);
1842 tmp.noalias() = lhs_seg * rhs.blockRef(id);
1843 dst_seg += alpha * tmp;
1844 }
1845 }
1846 }
1847 }
1848};
1849
1850} // namespace internal
1851
1852} // end namespace Eigen
1853
1854#endif // EIGEN_BLOCKSPARSEMATRIX_H
General-purpose arrays with easy API for coefficient-wise operations.
Definition Array.h:55
constexpr const Scalar & coeff(Index rowId, Index colId) const
Definition PlainObjectBase.h:187
constexpr Scalar & coeffRef(Index rowId, Index colId)
Definition PlainObjectBase.h:205
Index blockCol() const
Definition BlockSparseMatrix.h:287
Index index() const
Definition BlockSparseMatrix.h:283
BlockMap valueRef()
Definition BlockSparseMatrix.h:292
ConstBlockMap value() const
Definition BlockSparseMatrix.h:290
Index outer() const
Definition BlockSparseMatrix.h:281
Index blockRow() const
Definition BlockSparseMatrix.h:285
A sparse matrix whose stored nonzeros are fixed-size dense blocks.
Definition BlockSparseMatrix.h:160
Index nonZeros() const
Definition BlockSparseMatrix.h:237
Index cols() const noexcept
Definition BlockSparseMatrix.h:217
BlockSparseMatrix operator+(const BlockSparseMatrix &other) const
Definition BlockSparseMatrix.h:476
Matrix< Scalar, Dynamic, 1 > diagonal() const
Definition BlockSparseMatrix.h:435
void reserve(Index n)
Definition BlockSparseMatrix.h:324
void setZero()
Definition BlockSparseMatrix.h:317
Index nonZeroBlocks() const
Definition BlockSparseMatrix.h:235
Scalar coeff(Index row, Index col) const
Definition BlockSparseMatrix.h:415
BlockSparseMatrix(Index blockRows, Index blockCols)
Definition BlockSparseMatrix.h:207
Index blockRows() const
Definition BlockSparseMatrix.h:220
Index rows() const noexcept
Definition BlockSparseMatrix.h:215
BlockSparseMatrix cwiseProduct(const BlockSparseMatrix &other) const
Definition BlockSparseMatrix.h:482
void resize(Index blockRows, Index blockCols)
Definition BlockSparseMatrix.h:309
friend BlockSparseMatrix operator*(const Scalar &s, const BlockSparseMatrix &m)
Definition BlockSparseMatrix.h:535
Index allocatedBlocks() const
Definition BlockSparseMatrix.h:239
void setFromOuterInner(Index blockRows, Index blockCols, Index nnzBlocks, const StorageIndex_ *outerPtr, const StorageIndex_ *innerPtr)
Definition BlockSparseMatrix.h:357
Index innerSize() const
Definition BlockSparseMatrix.h:232
Index blockCols() const
Definition BlockSparseMatrix.h:222
BlockSparseMatrix disjunctionExpr(const BlockSparseMatrix &other, ScalarFunc func) const
Definition BlockSparseMatrix.h:502
BlockSparseMatrix< Scalar_, Options_, BlockCols_, BlockRows_, StorageIndex_ > transpose() const
Definition BlockSparseMatrix.h:1755
BlockSparseMatrix conjunctionExpr(const BlockSparseMatrix &other, ScalarFunc func) const
Definition BlockSparseMatrix.h:509
void setFromTriplets(InputIterator begin, InputIterator end)
Definition BlockSparseMatrix.h:923
static BlockSparseMatrix fromSparse(const SparseMatrix< Scalar_, Options_, StorageIndex_ > &sp)
Definition BlockSparseMatrix.h:1054
BlockMap blockRef(Index k)
Definition BlockSparseMatrix.h:258
friend Product< OtherDerived, BlockSparseMatrix, AliasFreeProduct > operator*(const MatrixBase< OtherDerived > &lhs, const BlockSparseMatrix &bsm)
Definition BlockSparseMatrix.h:572
Index blockOuterSize() const
Definition BlockSparseMatrix.h:225
void squeeze()
Definition BlockSparseMatrix.h:329
BlockSparseMatrix operator-(const BlockSparseMatrix &other) const
Definition BlockSparseMatrix.h:479
BlockSparseTriangularView< BlockSparseMatrix, Mode, DiagIsTriangular > triangularView() const
Definition BlockSparseMatrix.h:640
void setIdentity()
Definition BlockSparseMatrix.h:339
BlockSparseSelfAdjointView< BlockSparseMatrix, UpLo, DiagIsSelfAdjoint > selfadjointView() const
Definition BlockSparseMatrix.h:661
Index outerSize() const
Definition BlockSparseMatrix.h:230
BlockSparseMatrix operator-() const
Definition BlockSparseMatrix.h:514
BlockSparseMatrix operator*(const Scalar &s) const
Definition BlockSparseMatrix.h:522
Index blockInnerSize() const
Definition BlockSparseMatrix.h:227
BlockSparseMatrix< Scalar_, Options_, BlockCols_, BlockRows_, StorageIndex_ > adjoint() const
Definition BlockSparseMatrix.h:1761
BlockSparseMatrix unaryExpr(ScalarFunc func) const
Definition BlockSparseMatrix.h:488
Product< BlockSparseMatrix, OtherDerived, AliasFreeProduct > operator*(const MatrixBase< OtherDerived > &rhs) const
Definition BlockSparseMatrix.h:554
ConstBlockMap blockRef(Index k) const
Definition BlockSparseMatrix.h:256
SparseMatrix< Scalar, Options_, StorageIndex_ > toSparse() const
Definition BlockSparseMatrix.h:1006
Lazy block-level self-adjoint (Hermitian) view of a BlockSparseMatrix.
Definition BlockSparseMatrix.h:1539
BSM eval() const
Definition BlockSparseMatrix.h:1565
friend Matrix< Scalar, OtherDerived::RowsAtCompileTime, Dynamic > operator*(const MatrixBase< OtherDerived > &lhs, const BlockSparseSelfAdjointView &view)
Definition BlockSparseMatrix.h:1705
SparseMatrix< Scalar, BSM::Options, StorageIndex > toSparse() const
Definition BlockSparseMatrix.h:1637
Matrix< Scalar, Dynamic, OtherDerived::ColsAtCompileTime > operator*(const MatrixBase< OtherDerived > &rhs) const
Definition BlockSparseMatrix.h:1665
BlockSparseMatrix< Scalar, BSM::Options, BlockRows, RhsBlockCols, StorageIndex > operator*(const BlockSparseMatrix< Scalar, BSM::Options, BlockCols, RhsBlockCols, StorageIndex > &rhs) const
Definition BlockSparseMatrix.h:1646
Lazy block-level triangular view of a BlockSparseMatrix.
Definition BlockSparseMatrix.h:1251
BlockSparseMatrix< Scalar, BSM::Options, BlockRows, RhsBlockCols, StorageIndex > operator*(const BlockSparseMatrix< Scalar, BSM::Options, BlockCols, RhsBlockCols, StorageIndex > &rhs) const
Definition BlockSparseMatrix.h:1313
SparseMatrix< Scalar, BSM::Options, StorageIndex > toSparse() const
Definition BlockSparseMatrix.h:1304
void solveInPlace(MatrixBase< Derived > &x) const
Definition BlockSparseMatrix.h:1390
BSM eval() const
Definition BlockSparseMatrix.h:1274
A (blockRow, blockCol, blockValue) triplet for assembling a BlockSparseMatrix.
Definition BlockSparseMatrix.h:89
A matrix or vector expression mapping an existing array of data.
Definition Map.h:97
Base class for all dense matrices, vectors, and expressions.
Definition MatrixBase.h:53
The matrix class, also used for vectors and row-vectors.
Definition Matrix.h:188
constexpr const Scalar * data() const
Definition PlainObjectBase.h:261
Derived & setZero(Index size)
Definition CwiseNullaryOp.h:536
Expression of the product of two arbitrary matrices or vectors.
Definition Product.h:203
A versatile sparse matrix representation.
Definition SparseMatrix.h:122
bool isCompressed() const
Definition SparseCompressedBase.h:115
Index cols() const
Definition SparseMatrix.h:162
Index rows() const
Definition SparseMatrix.h:160
void reserve(Index reserveSize)
Definition SparseMatrix.h:317
@ StrictlyLower
Definition Constants.h:224
@ StrictlyUpper
Definition Constants.h:226
@ Lower
Definition Constants.h:212
@ Upper
Definition Constants.h:214
constexpr unsigned int LvalueBit
Definition Constants.h:149
constexpr unsigned int RowMajorBit
Definition Constants.h:71
Definition BlockSparseMatrix.h:34
Definition BlockSparseMatrix.h:1404
Definition BlockSparseMatrix.h:31
Definition EigenBase.h:34