Eigen  5.0.1
 
Loading...
Searching...
No Matches
SparseMatrix.h
1// This file is part of Eigen, a lightweight C++ template library
2// for linear algebra.
3//
4// Copyright (C) 2008-2014 Gael Guennebaud <gael.guennebaud@inria.fr>
5//
6// This Source Code Form is subject to the terms of the Mozilla
7// Public License v. 2.0. If a copy of the MPL was not distributed
8// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
9// SPDX-License-Identifier: MPL-2.0
10
11#ifndef EIGEN_SPARSEMATRIX_H
12#define EIGEN_SPARSEMATRIX_H
13
14// IWYU pragma: private
15#include "./InternalHeaderCheck.h"
16
17namespace Eigen {
18
49
50namespace internal {
51template <typename Scalar_, int Options_, typename StorageIndex_>
52struct traits<SparseMatrix<Scalar_, Options_, StorageIndex_>> {
53 using Scalar = Scalar_;
54 using StorageIndex = StorageIndex_;
55 using StorageKind = Sparse;
56 using XprKind = MatrixXpr;
57 enum {
58 RowsAtCompileTime = Dynamic,
59 ColsAtCompileTime = Dynamic,
60 MaxRowsAtCompileTime = Dynamic,
61 MaxColsAtCompileTime = Dynamic,
62 Options = Options_,
63 Flags = Options_ | NestByRefBit | LvalueBit | CompressedAccessBit,
64 SupportedAccessPatterns = InnerRandomAccessPattern
65 };
66};
67
68template <typename Scalar_, int Options_, typename StorageIndex_, int DiagIndex>
69struct traits<Diagonal<SparseMatrix<Scalar_, Options_, StorageIndex_>, DiagIndex>> {
70 using MatrixType = SparseMatrix<Scalar_, Options_, StorageIndex_>;
71 using MatrixTypeNested = typename ref_selector<MatrixType>::type;
72 using MatrixTypeNested_ = std::remove_reference_t<MatrixTypeNested>;
73
74 using Scalar = Scalar_;
75 using StorageKind = Dense;
76 using StorageIndex = StorageIndex_;
77 using XprKind = MatrixXpr;
78
79 enum {
80 RowsAtCompileTime = Dynamic,
81 ColsAtCompileTime = 1,
82 MaxRowsAtCompileTime = Dynamic,
83 MaxColsAtCompileTime = 1,
84 Flags = LvalueBit
85 };
86};
87
88template <typename Scalar_, int Options_, typename StorageIndex_, int DiagIndex>
89struct traits<Diagonal<const SparseMatrix<Scalar_, Options_, StorageIndex_>, DiagIndex>>
90 : public traits<Diagonal<SparseMatrix<Scalar_, Options_, StorageIndex_>, DiagIndex>> {
91 enum { Flags = 0 };
92};
93
94template <typename StorageIndex>
95struct sparse_reserve_op {
96 EIGEN_DEVICE_FUNC sparse_reserve_op(Index begin, Index end, Index size) {
97 Index range = numext::mini(end - begin, size);
98 m_begin = begin;
99 m_end = begin + range;
100 m_val = StorageIndex(size / range);
101 m_remainder = StorageIndex(size % range);
102 }
103 template <typename IndexType>
104 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE StorageIndex operator()(IndexType i) const {
105 if ((i >= m_begin) && (i < m_end))
106 return m_val + ((i - m_begin) < m_remainder ? 1 : 0);
107 else
108 return 0;
109 }
110 StorageIndex m_val, m_remainder;
111 Index m_begin, m_end;
112};
113
114template <typename Scalar>
115struct functor_traits<sparse_reserve_op<Scalar>> {
116 enum { Cost = 1, PacketAccess = false, IsRepeatable = true };
117};
118
119} // end namespace internal
120
121template <typename Scalar_, int Options_, typename StorageIndex_>
122class SparseMatrix : public SparseCompressedBase<SparseMatrix<Scalar_, Options_, StorageIndex_>> {
124 using Base::convert_index;
125 friend class SparseVector<Scalar_, 0, StorageIndex_>;
126 template <typename, typename, typename, typename, typename>
127 friend struct internal::Assignment;
128
129 public:
130 using Base::isCompressed;
131 using Base::nonZeros;
132 EIGEN_SPARSE_PUBLIC_INTERFACE(SparseMatrix)
133 using Base::operator+=;
134 using Base::operator-=;
135
137 using DiagonalReturnType = Diagonal<SparseMatrix>;
138 using ConstDiagonalReturnType = Diagonal<const SparseMatrix>;
139 using InnerIterator = typename Base::InnerIterator;
140 using ReverseInnerIterator = typename Base::ReverseInnerIterator;
141
142 using Base::IsRowMajor;
143 using Storage = internal::CompressedStorage<Scalar, StorageIndex>;
144 enum { Options = Options_ };
145
146 using IndexVector = typename Base::IndexVector;
147 using ScalarVector = typename Base::ScalarVector;
148
149 protected:
151
152 Index m_outerSize;
153 Index m_innerSize;
154 StorageIndex* m_outerIndex;
155 StorageIndex* m_innerNonZeros; // optional, if null then the data is compressed
156 Storage m_data;
157
158 public:
160 inline Index rows() const { return IsRowMajor ? m_outerSize : m_innerSize; }
162 inline Index cols() const { return IsRowMajor ? m_innerSize : m_outerSize; }
163
165 inline Index innerSize() const { return m_innerSize; }
167 inline Index outerSize() const { return m_outerSize; }
168
172 inline const Scalar* valuePtr() const { return m_data.valuePtr(); }
176 inline Scalar* valuePtr() { return m_data.valuePtr(); }
177
181 inline const StorageIndex* innerIndexPtr() const { return m_data.indexPtr(); }
185 inline StorageIndex* innerIndexPtr() { return m_data.indexPtr(); }
186
190 inline const StorageIndex* outerIndexPtr() const { return m_outerIndex; }
194 inline StorageIndex* outerIndexPtr() { return m_outerIndex; }
195
199 inline const StorageIndex* innerNonZeroPtr() const { return m_innerNonZeros; }
203 inline StorageIndex* innerNonZeroPtr() { return m_innerNonZeros; }
204
206 constexpr Storage& data() { return m_data; }
208 constexpr const Storage& data() const { return m_data; }
209
212 inline Scalar coeff(Index row, Index col) const {
213 eigen_assert(row >= 0 && row < rows() && col >= 0 && col < cols());
214
215 const Index outer = IsRowMajor ? row : col;
216 const Index inner = IsRowMajor ? col : row;
217 Index end = m_innerNonZeros ? m_outerIndex[outer] + m_innerNonZeros[outer] : m_outerIndex[outer + 1];
218 return m_data.atInRange(m_outerIndex[outer], end, inner);
219 }
220
232 inline Scalar& findOrInsertCoeff(Index row, Index col, bool* inserted) {
233 eigen_assert(row >= 0 && row < rows() && col >= 0 && col < cols());
234 const Index outer = IsRowMajor ? row : col;
235 const Index inner = IsRowMajor ? col : row;
236 Index start = m_outerIndex[outer];
237 Index end = isCompressed() ? m_outerIndex[outer + 1] : m_outerIndex[outer] + m_innerNonZeros[outer];
238 eigen_assert(end >= start && "you probably called coeffRef on a non finalized matrix");
239 Index dst = start == end ? end : m_data.searchLowerIndex(start, end, inner);
240 if (dst == end) {
241 Index capacity = m_outerIndex[outer + 1] - end;
242 if (capacity > 0) {
243 // implies uncompressed: push to back of vector
244 m_innerNonZeros[outer]++;
245 m_data.index(end) = StorageIndex(inner);
246 m_data.value(end) = Scalar(0);
247 if (inserted != nullptr) {
248 *inserted = true;
249 }
250 return m_data.value(end);
251 }
252 }
253 if ((dst < end) && (m_data.index(dst) == inner)) {
254 // this coefficient exists, return a reference to it
255 if (inserted != nullptr) {
256 *inserted = false;
257 }
258 return m_data.value(dst);
259 } else {
260 if (inserted != nullptr) {
261 *inserted = true;
262 }
263 // insertion will require reconfiguring the buffer
264 return insertAtByOuterInner(outer, inner, dst);
265 }
266 }
267
276 inline Scalar& coeffRef(Index row, Index col) { return findOrInsertCoeff(row, col, nullptr); }
277
295 inline Scalar& insert(Index row, Index col);
296
297 public:
305 inline void setZero() {
306 m_data.clear();
307 using std::fill_n;
308 fill_n(m_outerIndex, m_outerSize + 1, StorageIndex(0));
309 if (m_innerNonZeros) {
310 fill_n(m_innerNonZeros, m_outerSize, StorageIndex(0));
311 }
312 }
313
317 inline void reserve(Index reserveSize) {
318 eigen_assert(isCompressed() && "This function does not make sense in non compressed mode.");
319 m_data.reserve(reserveSize);
320 }
321
322#ifdef EIGEN_PARSED_BY_DOXYGEN
335 template <class SizesType>
336 inline void reserve(const SizesType& reserveSizes);
337#else
338 template <class SizesType>
339 inline void reserve(const SizesType& reserveSizes,
340 const typename SizesType::value_type& enableif = typename SizesType::value_type()) {
341 EIGEN_UNUSED_VARIABLE(enableif);
342 reserveInnerVectors(reserveSizes);
343 }
344#endif // EIGEN_PARSED_BY_DOXYGEN
345 protected:
346 template <class SizesType>
347 inline void reserveInnerVectors(const SizesType& reserveSizes) {
348 if (isCompressed()) {
349 Index totalReserveSize = 0;
350 for (Index j = 0; j < m_outerSize; ++j) totalReserveSize += internal::convert_index<Index>(reserveSizes[j]);
351
352 // if reserveSizes is empty, don't do anything!
353 if (totalReserveSize == 0) return;
354
355 // turn the matrix into non-compressed mode
356 m_innerNonZeros = internal::conditional_aligned_new_auto<StorageIndex, true>(m_outerSize);
357
358 // temporarily use m_innerNonZeros to hold the new starting points.
359 StorageIndex* newOuterIndex = m_innerNonZeros;
360
361 Index count = 0;
362 for (Index j = 0; j < m_outerSize; ++j) {
363 newOuterIndex[j] = internal::convert_index<StorageIndex>(count);
364 Index reserveSize = internal::convert_index<Index>(reserveSizes[j]);
365 count += reserveSize + internal::convert_index<Index>(m_outerIndex[j + 1] - m_outerIndex[j]);
366 }
367
368 m_data.reserve(totalReserveSize);
369 StorageIndex previousOuterIndex = m_outerIndex[m_outerSize];
370 for (Index j = m_outerSize - 1; j >= 0; --j) {
371 StorageIndex innerNNZ = previousOuterIndex - m_outerIndex[j];
372 StorageIndex begin = m_outerIndex[j];
373 StorageIndex end = begin + innerNNZ;
374 StorageIndex target = newOuterIndex[j];
375 internal::smart_memmove(innerIndexPtr() + begin, innerIndexPtr() + end, innerIndexPtr() + target);
376 internal::smart_memmove(valuePtr() + begin, valuePtr() + end, valuePtr() + target);
377 previousOuterIndex = m_outerIndex[j];
378 m_outerIndex[j] = newOuterIndex[j];
379 m_innerNonZeros[j] = innerNNZ;
380 }
381 if (m_outerSize > 0)
382 m_outerIndex[m_outerSize] = m_outerIndex[m_outerSize - 1] + m_innerNonZeros[m_outerSize - 1] +
383 internal::convert_index<StorageIndex>(reserveSizes[m_outerSize - 1]);
384
385 m_data.resize(m_outerIndex[m_outerSize]);
386 } else {
387 StorageIndex* newOuterIndex = internal::conditional_aligned_new_auto<StorageIndex, true>(m_outerSize + 1);
388
389 Index count = 0;
390 for (Index j = 0; j < m_outerSize; ++j) {
391 newOuterIndex[j] = internal::convert_index<StorageIndex>(count);
392 Index alreadyReserved =
393 internal::convert_index<Index>(m_outerIndex[j + 1] - m_outerIndex[j] - m_innerNonZeros[j]);
394 Index reserveSize = internal::convert_index<Index>(reserveSizes[j]);
395 Index toReserve = numext::maxi(reserveSize, alreadyReserved);
396 count += toReserve + internal::convert_index<Index>(m_innerNonZeros[j]);
397 }
398 newOuterIndex[m_outerSize] = internal::convert_index<StorageIndex>(count);
399
400 m_data.resize(count);
401 for (Index j = m_outerSize - 1; j >= 0; --j) {
402 StorageIndex innerNNZ = m_innerNonZeros[j];
403 StorageIndex begin = m_outerIndex[j];
404 StorageIndex target = newOuterIndex[j];
405 m_data.moveChunk(begin, target, innerNNZ);
406 }
407
408 std::swap(m_outerIndex, newOuterIndex);
409 internal::conditional_aligned_delete_auto<StorageIndex, true>(newOuterIndex, m_outerSize + 1);
410 }
411 }
412
413 public:
414 //--- low level purely coherent filling ---
415
426 inline Scalar& insertBack(Index row, Index col) {
427 return insertBackByOuterInner(IsRowMajor ? row : col, IsRowMajor ? col : row);
428 }
429
432 inline Scalar& insertBackByOuterInner(Index outer, Index inner) {
433 eigen_assert(Index(m_outerIndex[outer + 1]) == m_data.size() && "Invalid ordered insertion (invalid outer index)");
434 eigen_assert((m_outerIndex[outer + 1] - m_outerIndex[outer] == 0 || m_data.index(m_data.size() - 1) < inner) &&
435 "Invalid ordered insertion (invalid inner index)");
436 StorageIndex p = m_outerIndex[outer + 1];
437 ++m_outerIndex[outer + 1];
438 m_data.append(Scalar(0), inner);
439 return m_data.value(p);
440 }
441
444 inline Scalar& insertBackByOuterInnerUnordered(Index outer, Index inner) {
445 StorageIndex p = m_outerIndex[outer + 1];
446 ++m_outerIndex[outer + 1];
447 m_data.append(Scalar(0), inner);
448 return m_data.value(p);
449 }
450
453 inline void startVec(Index outer) {
454 eigen_assert(m_outerIndex[outer] == Index(m_data.size()) &&
455 "You must call startVec for each inner vector sequentially");
456 eigen_assert(m_outerIndex[outer + 1] == 0 && "You must call startVec for each inner vector sequentially");
457 m_outerIndex[outer + 1] = m_outerIndex[outer];
458 }
459
463 inline void finalize() {
464 if (isCompressed()) {
465 StorageIndex size = internal::convert_index<StorageIndex>(m_data.size());
466 Index i = m_outerSize;
467 // find the last filled column
468 while (i >= 0 && m_outerIndex[i] == 0) --i;
469 ++i;
470 while (i <= m_outerSize) {
471 m_outerIndex[i] = size;
472 ++i;
473 }
474 }
475 }
476
477 // remove outer vectors j, j+1 ... j+num-1 and resize the matrix
478 void removeOuterVectors(Index j, Index num = 1) {
479 eigen_assert(num >= 0 && j >= 0 && j + num <= m_outerSize && "Invalid parameters");
480
481 const Index newRows = IsRowMajor ? m_outerSize - num : rows();
482 const Index newCols = IsRowMajor ? cols() : m_outerSize - num;
483
484 const Index begin = j + num;
485 const Index end = m_outerSize;
486 const Index target = j;
487
488 // if the removed vectors are not empty, uncompress the matrix
489 if (m_outerIndex[j + num] > m_outerIndex[j]) uncompress();
490
491 // shift m_outerIndex and m_innerNonZeros [num] to the left
492 internal::smart_memmove(m_outerIndex + begin, m_outerIndex + end + 1, m_outerIndex + target);
493 if (!isCompressed())
494 internal::smart_memmove(m_innerNonZeros + begin, m_innerNonZeros + end, m_innerNonZeros + target);
495
496 // if m_outerIndex[0] > 0, shift the data within the first vector while it is easy to do so
497 if (m_outerIndex[0] > StorageIndex(0)) {
498 uncompress();
499 const Index from = internal::convert_index<Index>(m_outerIndex[0]);
500 const Index to = Index(0);
501 const Index chunkSize = internal::convert_index<Index>(m_innerNonZeros[0]);
502 m_data.moveChunk(from, to, chunkSize);
503 m_outerIndex[0] = StorageIndex(0);
504 }
505
506 // truncate the matrix to the smaller size
507 conservativeResize(newRows, newCols);
508 }
509
510 // insert empty outer vectors at indices j, j+1 ... j+num-1 and resize the matrix
511 void insertEmptyOuterVectors(Index j, Index num = 1) {
512 using std::fill_n;
513 eigen_assert(num >= 0 && j >= 0 && j < m_outerSize && "Invalid parameters");
514
515 const Index newRows = IsRowMajor ? m_outerSize + num : rows();
516 const Index newCols = IsRowMajor ? cols() : m_outerSize + num;
517
518 const Index begin = j;
519 const Index end = m_outerSize;
520 const Index target = j + num;
521
522 // expand the matrix to the larger size
523 conservativeResize(newRows, newCols);
524
525 // shift m_outerIndex and m_innerNonZeros [num] to the right
526 internal::smart_memmove(m_outerIndex + begin, m_outerIndex + end + 1, m_outerIndex + target);
527 // m_outerIndex[begin] == m_outerIndex[target], set all indices in this range to same value
528 fill_n(m_outerIndex + begin, num, m_outerIndex[begin]);
529
530 if (!isCompressed()) {
531 internal::smart_memmove(m_innerNonZeros + begin, m_innerNonZeros + end, m_innerNonZeros + target);
532 // set the nonzeros of the newly inserted vectors to 0
533 fill_n(m_innerNonZeros + begin, num, StorageIndex(0));
534 }
535 }
536
537 template <typename InputIterators>
538 void setFromTriplets(const InputIterators& begin, const InputIterators& end);
539
540 template <typename InputIterators, typename DupFunctor>
541 void setFromTriplets(const InputIterators& begin, const InputIterators& end, DupFunctor dup_func);
542
543 template <typename Derived, typename DupFunctor>
544 void collapseDuplicates(DenseBase<Derived>& wi, DupFunctor dup_func = DupFunctor());
545
546 template <typename InputIterators>
547 void setFromSortedTriplets(const InputIterators& begin, const InputIterators& end);
548
549 template <typename InputIterators, typename DupFunctor>
550 void setFromSortedTriplets(const InputIterators& begin, const InputIterators& end, DupFunctor dup_func);
551
552 template <typename InputIterators>
553 void insertFromTriplets(const InputIterators& begin, const InputIterators& end);
554
555 template <typename InputIterators, typename DupFunctor>
556 void insertFromTriplets(const InputIterators& begin, const InputIterators& end, DupFunctor dup_func);
557
558 template <typename InputIterators>
559 void insertFromSortedTriplets(const InputIterators& begin, const InputIterators& end);
560
561 template <typename InputIterators, typename DupFunctor>
562 void insertFromSortedTriplets(const InputIterators& begin, const InputIterators& end, DupFunctor dup_func);
563
564 //---
565
568 Scalar& insertByOuterInner(Index j, Index i) {
569 eigen_assert(j >= 0 && j < m_outerSize && "invalid outer index");
570 eigen_assert(i >= 0 && i < m_innerSize && "invalid inner index");
571 Index start = m_outerIndex[j];
572 Index end = isCompressed() ? m_outerIndex[j + 1] : start + m_innerNonZeros[j];
573 Index dst = start == end ? end : m_data.searchLowerIndex(start, end, i);
574 if (dst == end) {
575 Index capacity = m_outerIndex[j + 1] - end;
576 if (capacity > 0) {
577 // implies uncompressed: push to back of vector
578 m_innerNonZeros[j]++;
579 m_data.index(end) = StorageIndex(i);
580 m_data.value(end) = Scalar(0);
581 return m_data.value(end);
582 }
583 }
584 eigen_assert((dst == end || m_data.index(dst) != i) &&
585 "you cannot insert an element that already exists, you must call coeffRef to this end");
586 return insertAtByOuterInner(j, i, dst);
587 }
588
592 if (isCompressed()) return;
593
594 eigen_internal_assert(m_outerIndex != 0 && m_outerSize > 0);
595
596 StorageIndex start = m_outerIndex[1];
597 m_outerIndex[1] = m_innerNonZeros[0];
598 // try to move fewer, larger contiguous chunks
599 Index copyStart = start;
600 Index copyTarget = m_innerNonZeros[0];
601 for (Index j = 1; j < m_outerSize; j++) {
602 StorageIndex end = start + m_innerNonZeros[j];
603 StorageIndex nextStart = m_outerIndex[j + 1];
604 // dont forget to move the last chunk!
605 bool breakUpCopy = (end != nextStart) || (j == m_outerSize - 1);
606 if (breakUpCopy) {
607 Index chunkSize = end - copyStart;
608 if (chunkSize > 0) m_data.moveChunk(copyStart, copyTarget, chunkSize);
609 copyStart = nextStart;
610 copyTarget += chunkSize;
611 }
612 start = nextStart;
613 m_outerIndex[j + 1] = m_outerIndex[j] + m_innerNonZeros[j];
614 }
615 m_data.resize(m_outerIndex[m_outerSize]);
616
617 // release as much memory as possible
618 internal::conditional_aligned_delete_auto<StorageIndex, true>(m_innerNonZeros, m_outerSize);
619 m_innerNonZeros = 0;
620 m_data.squeeze();
621 }
622
624 void uncompress() {
625 if (!isCompressed()) return;
626 m_innerNonZeros = internal::conditional_aligned_new_auto<StorageIndex, true>(m_outerSize);
627 if (m_outerIndex[m_outerSize] == 0) {
628 using std::fill_n;
629 fill_n(m_innerNonZeros, m_outerSize, StorageIndex(0));
630 } else {
631 for (Index j = 0; j < m_outerSize; j++) m_innerNonZeros[j] = m_outerIndex[j + 1] - m_outerIndex[j];
632 }
633 }
634
636 void prune(const Scalar& reference, const RealScalar& epsilon = NumTraits<RealScalar>::dummy_precision()) {
637 prune(default_prunning_func(reference, epsilon));
638 }
639
648 template <typename KeepFunc>
649 void prune(const KeepFunc& keep = KeepFunc()) {
650 StorageIndex k = 0;
651 for (Index j = 0; j < m_outerSize; ++j) {
652 StorageIndex previousStart = m_outerIndex[j];
653 if (isCompressed())
654 m_outerIndex[j] = k;
655 else
656 k = m_outerIndex[j];
657 StorageIndex end = isCompressed() ? m_outerIndex[j + 1] : previousStart + m_innerNonZeros[j];
658 for (StorageIndex i = previousStart; i < end; ++i) {
659 StorageIndex row = IsRowMajor ? StorageIndex(j) : m_data.index(i);
660 StorageIndex col = IsRowMajor ? m_data.index(i) : StorageIndex(j);
661 bool keepEntry = keep(row, col, m_data.value(i));
662 if (keepEntry) {
663 m_data.value(k) = m_data.value(i);
664 m_data.index(k) = m_data.index(i);
665 ++k;
666 } else if (!isCompressed())
667 m_innerNonZeros[j]--;
668 }
669 }
670 if (isCompressed()) {
671 m_outerIndex[m_outerSize] = k;
672 m_data.resize(k, 0);
673 }
674 }
675
684 void conservativeResize(Index rows, Index cols) {
685 // If one dimension is null, then there is nothing to be preserved
686 if (rows == 0 || cols == 0) return resize(rows, cols);
687
688 Index newOuterSize = IsRowMajor ? rows : cols;
689 Index newInnerSize = IsRowMajor ? cols : rows;
690
691 Index innerChange = newInnerSize - m_innerSize;
692 Index outerChange = newOuterSize - m_outerSize;
693
694 if (outerChange != 0) {
695 m_outerIndex = internal::conditional_aligned_realloc_new_auto<StorageIndex, true>(m_outerIndex, newOuterSize + 1,
696 m_outerSize + 1);
697
698 if (!isCompressed())
699 m_innerNonZeros = internal::conditional_aligned_realloc_new_auto<StorageIndex, true>(m_innerNonZeros,
700 newOuterSize, m_outerSize);
701
702 if (outerChange > 0) {
703 StorageIndex lastIdx = m_outerSize == 0 ? StorageIndex(0) : m_outerIndex[m_outerSize];
704 using std::fill_n;
705 fill_n(m_outerIndex + m_outerSize, outerChange + 1, lastIdx);
706
707 if (!isCompressed()) fill_n(m_innerNonZeros + m_outerSize, outerChange, StorageIndex(0));
708 }
709 }
710 m_outerSize = newOuterSize;
711
712 if (innerChange < 0) {
713 for (Index j = 0; j < m_outerSize; j++) {
714 Index start = m_outerIndex[j];
715 Index end = isCompressed() ? m_outerIndex[j + 1] : start + m_innerNonZeros[j];
716 Index lb = m_data.searchLowerIndex(start, end, newInnerSize);
717 if (lb != end) {
718 uncompress();
719 m_innerNonZeros[j] = StorageIndex(lb - start);
720 }
721 }
722 }
723 m_innerSize = newInnerSize;
724
725 Index newSize = m_outerIndex[m_outerSize];
726 eigen_assert(newSize <= m_data.size());
727 m_data.resize(newSize);
728 }
729
731 void conservativeResize(NoChange_t, Index cols) { conservativeResize(rows(), cols); }
732
734 void conservativeResize(Index rows, NoChange_t) { conservativeResize(rows, cols()); }
735
743 void resize(Index rows, Index cols) {
744 const Index outerSize = IsRowMajor ? rows : cols;
745 m_innerSize = IsRowMajor ? cols : rows;
746 m_data.clear();
747
748 if ((m_outerIndex == 0) || (m_outerSize != outerSize)) {
749 m_outerIndex = internal::conditional_aligned_realloc_new_auto<StorageIndex, true>(m_outerIndex, outerSize + 1,
750 m_outerSize + 1);
751 m_outerSize = outerSize;
752 }
753
754 internal::conditional_aligned_delete_auto<StorageIndex, true>(m_innerNonZeros, m_outerSize);
755 m_innerNonZeros = 0;
756
757 using std::fill_n;
758 fill_n(m_outerIndex, m_outerSize + 1, StorageIndex(0));
759 }
760
762 void resize(NoChange_t, Index cols) { resize(rows(), cols); }
763
765 void resize(Index rows, NoChange_t) { resize(rows, cols()); }
766
769 void resizeNonZeros(Index size) { m_data.resize(size); }
770
772 const ConstDiagonalReturnType diagonal() const { return ConstDiagonalReturnType(*this); }
773
778 DiagonalReturnType diagonal() { return DiagonalReturnType(*this); }
779
781 inline SparseMatrix() : m_outerSize(0), m_innerSize(0), m_outerIndex(0), m_innerNonZeros(0) { resize(0, 0); }
782
784 inline SparseMatrix(Index rows, Index cols) : m_outerSize(0), m_innerSize(0), m_outerIndex(0), m_innerNonZeros(0) {
785 resize(rows, cols);
786 }
787
789 template <typename OtherDerived>
791 : m_outerSize(0), m_innerSize(0), m_outerIndex(0), m_innerNonZeros(0) {
792 EIGEN_STATIC_ASSERT(
793 (std::is_same<Scalar, typename OtherDerived::Scalar>::value),
794 YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY)
795 const bool needToTranspose = (Flags & RowMajorBit) != (internal::evaluator<OtherDerived>::Flags & RowMajorBit);
796 if (needToTranspose)
797 *this = other.derived();
798 else {
799#ifdef EIGEN_SPARSE_CREATE_TEMPORARY_PLUGIN
800 EIGEN_SPARSE_CREATE_TEMPORARY_PLUGIN
801#endif
802 internal::call_assignment_no_alias(*this, other.derived());
803 }
804 }
805
807 template <typename OtherDerived, unsigned int UpLo>
809 : m_outerSize(0), m_innerSize(0), m_outerIndex(0), m_innerNonZeros(0) {
810 Base::operator=(other);
811 }
812
814 inline SparseMatrix(SparseMatrix&& other) : SparseMatrix() { this->swap(other); }
815
816 template <typename OtherDerived>
818 *this = other.derived().markAsRValue();
819 }
820
822 inline SparseMatrix(const SparseMatrix& other)
823 : Base(), m_outerSize(0), m_innerSize(0), m_outerIndex(0), m_innerNonZeros(0) {
824 *this = other.derived();
825 }
826
828 template <typename OtherDerived>
829 SparseMatrix(const ReturnByValue<OtherDerived>& other)
830 : Base(), m_outerSize(0), m_innerSize(0), m_outerIndex(0), m_innerNonZeros(0) {
831 initAssignment(other);
832 other.evalTo(*this);
833 }
834
836 template <typename OtherDerived>
838 : Base(), m_outerSize(0), m_innerSize(0), m_outerIndex(0), m_innerNonZeros(0) {
839 *this = other.derived();
840 }
841
844 inline void swap(SparseMatrix& other) {
845 std::swap(m_outerIndex, other.m_outerIndex);
846 std::swap(m_innerSize, other.m_innerSize);
847 std::swap(m_outerSize, other.m_outerSize);
848 std::swap(m_innerNonZeros, other.m_innerNonZeros);
849 m_data.swap(other.m_data);
850 }
851
852 friend EIGEN_DEVICE_FUNC void swap(SparseMatrix& a, SparseMatrix& b) { a.swap(b); }
853
856 inline void setIdentity() {
857 eigen_assert(m_outerSize == m_innerSize && "ONLY FOR SQUARED MATRICES");
858 internal::conditional_aligned_delete_auto<StorageIndex, true>(m_innerNonZeros, m_outerSize);
859 m_innerNonZeros = 0;
860 m_data.resize(m_outerSize);
861 // is it necessary to squeeze?
862 m_data.squeeze();
863 std::iota(m_outerIndex, m_outerIndex + m_outerSize + 1, StorageIndex(0));
864 std::iota(innerIndexPtr(), innerIndexPtr() + m_outerSize, StorageIndex(0));
865 using std::fill_n;
866 fill_n(valuePtr(), m_outerSize, Scalar(1));
867 }
868
869 inline SparseMatrix& operator=(const SparseMatrix& other) {
870 if (other.isRValue()) {
871 swap(other.const_cast_derived());
872 } else if (this != &other) {
873#ifdef EIGEN_SPARSE_CREATE_TEMPORARY_PLUGIN
874 EIGEN_SPARSE_CREATE_TEMPORARY_PLUGIN
875#endif
876 initAssignment(other);
877 if (other.isCompressed()) {
878 internal::smart_copy(other.m_outerIndex, other.m_outerIndex + m_outerSize + 1, m_outerIndex);
879 m_data = other.m_data;
880 } else {
881 Base::operator=(other);
882 }
883 }
884 return *this;
885 }
886
887 inline SparseMatrix& operator=(SparseMatrix&& other) {
888 this->swap(other);
889 return *this;
890 }
891
892 template <typename OtherDerived>
893 inline SparseMatrix& operator=(const EigenBase<OtherDerived>& other) {
894 return Base::operator=(other.derived());
895 }
896
897 template <typename Lhs, typename Rhs>
898 inline SparseMatrix& operator=(const Product<Lhs, Rhs, AliasFreeProduct>& other);
899
900 template <typename OtherDerived>
901 EIGEN_DONT_INLINE SparseMatrix& operator=(const SparseMatrixBase<OtherDerived>& other);
902
903 template <typename OtherDerived>
904 inline SparseMatrix& operator=(SparseCompressedBase<OtherDerived>&& other) {
905 *this = other.derived().markAsRValue();
906 return *this;
907 }
908
909#ifndef EIGEN_NO_IO
910 friend std::ostream& operator<<(std::ostream& s, const SparseMatrix& m) {
911 EIGEN_DBG_SPARSE(
912 s << "Nonzero entries:\n"; if (m.isCompressed()) {
913 for (Index i = 0; i < m.nonZeros(); ++i) s << "(" << m.m_data.value(i) << "," << m.m_data.index(i) << ") ";
914 } else {
915 for (Index i = 0; i < m.outerSize(); ++i) {
916 Index p = m.m_outerIndex[i];
917 Index pe = m.m_outerIndex[i] + m.m_innerNonZeros[i];
918 Index k = p;
919 for (; k < pe; ++k) {
920 s << "(" << m.m_data.value(k) << "," << m.m_data.index(k) << ") ";
921 }
922 for (; k < m.m_outerIndex[i + 1]; ++k) {
923 s << "(_,_) ";
924 }
925 }
926 } s << std::endl;
927 s << std::endl; s << "Outer pointers:\n";
928 for (Index i = 0; i < m.outerSize(); ++i) { s << m.m_outerIndex[i] << " "; } s << " $" << std::endl;
929 if (!m.isCompressed()) {
930 s << "Inner non zeros:\n";
931 for (Index i = 0; i < m.outerSize(); ++i) {
932 s << m.m_innerNonZeros[i] << " ";
933 }
934 s << " $" << std::endl;
935 } s
936 << std::endl;);
937 s << static_cast<const SparseMatrixBase<SparseMatrix>&>(m);
938 return s;
939 }
940#endif
941
943 inline ~SparseMatrix() {
944 internal::conditional_aligned_delete_auto<StorageIndex, true>(m_outerIndex, m_outerSize + 1);
945 internal::conditional_aligned_delete_auto<StorageIndex, true>(m_innerNonZeros, m_outerSize);
946 }
947
949 Scalar sum() const;
950
951#ifdef EIGEN_SPARSEMATRIX_PLUGIN
952#include EIGEN_SPARSEMATRIX_PLUGIN
953#endif
954
955 protected:
956 template <typename Other>
957 void initAssignment(const Other& other) {
958 resize(other.rows(), other.cols());
959 internal::conditional_aligned_delete_auto<StorageIndex, true>(m_innerNonZeros, m_outerSize);
960 m_innerNonZeros = 0;
961 }
962
965 EIGEN_DEPRECATED EIGEN_DONT_INLINE Scalar& insertCompressed(Index row, Index col);
966
969 EIGEN_DEPRECATED EIGEN_DONT_INLINE Scalar& insertUncompressed(Index row, Index col);
970
971 public:
974 EIGEN_STRONG_INLINE Scalar& insertBackUncompressed(Index row, Index col) {
975 const Index outer = IsRowMajor ? row : col;
976 const Index inner = IsRowMajor ? col : row;
977
978 eigen_assert(!isCompressed());
979 eigen_assert(m_innerNonZeros[outer] <= (m_outerIndex[outer + 1] - m_outerIndex[outer]));
980
981 Index p = m_outerIndex[outer] + m_innerNonZeros[outer]++;
982 m_data.index(p) = StorageIndex(inner);
983 m_data.value(p) = Scalar(0);
984 return m_data.value(p);
985 }
986
987 protected:
988 struct IndexPosPair {
989 IndexPosPair(Index a_i, Index a_p) : i(a_i), p(a_p) {}
990 Index i;
991 Index p;
992 };
993
1003 template <typename DiagXpr, typename Func>
1004 void assignDiagonal(const DiagXpr diagXpr, const Func& assignFunc) {
1005 constexpr StorageIndex kEmptyIndexVal(-1);
1006 typedef typename ScalarVector::AlignedMapType ValueMap;
1007
1008 Index n = diagXpr.size();
1009
1010 const bool overwrite = std::is_same<Func, internal::assign_op<Scalar, Scalar>>::value;
1011 if (overwrite) {
1012 if ((m_outerSize != n) || (m_innerSize != n) || (n == 0)) resize(n, n);
1013 }
1014
1015 if (m_data.size() == 0 || overwrite) {
1016 internal::conditional_aligned_delete_auto<StorageIndex, true>(m_innerNonZeros, m_outerSize);
1017 m_innerNonZeros = 0;
1018 resizeNonZeros(n);
1019 ValueMap valueMap(valuePtr(), n);
1020 std::iota(m_outerIndex, m_outerIndex + n + 1, StorageIndex(0));
1021 std::iota(innerIndexPtr(), innerIndexPtr() + n, StorageIndex(0));
1022 valueMap.setZero();
1023 internal::call_assignment_no_alias(valueMap, diagXpr, assignFunc);
1024 } else {
1025 internal::evaluator<DiagXpr> diaEval(diagXpr);
1026
1027 ei_declare_aligned_stack_constructed_variable(StorageIndex, tmp, n, 0);
1028 typename IndexVector::AlignedMapType insertionLocations(tmp, n);
1029 insertionLocations.setConstant(kEmptyIndexVal);
1030
1031 Index deferredInsertions = 0;
1032 Index shift = 0;
1033
1034 for (Index j = 0; j < n; j++) {
1035 Index begin = m_outerIndex[j];
1036 Index end = isCompressed() ? m_outerIndex[j + 1] : begin + m_innerNonZeros[j];
1037 Index capacity = m_outerIndex[j + 1] - end;
1038 Index dst = m_data.searchLowerIndex(begin, end, j);
1039 // the entry exists: update it now
1040 if (dst != end && m_data.index(dst) == StorageIndex(j))
1041 assignFunc.assignCoeff(m_data.value(dst), diaEval.coeff(j));
1042 // the entry belongs at the back of the vector: push to back
1043 else if (dst == end && capacity > 0)
1044 assignFunc.assignCoeff(insertBackUncompressed(j, j), diaEval.coeff(j));
1045 // the insertion requires a data move, record insertion location and handle in second pass
1046 else {
1047 insertionLocations.coeffRef(j) = StorageIndex(dst);
1048 deferredInsertions++;
1049 // if there is no capacity, all vectors to the right of this are shifted
1050 if (capacity == 0) shift++;
1051 }
1052 }
1053
1054 if (deferredInsertions > 0) {
1055 m_data.resize(m_data.size() + shift);
1056 Index copyEnd = isCompressed() ? m_outerIndex[m_outerSize]
1057 : m_outerIndex[m_outerSize - 1] + m_innerNonZeros[m_outerSize - 1];
1058 for (Index j = m_outerSize - 1; deferredInsertions > 0; j--) {
1059 Index begin = m_outerIndex[j];
1060 Index end = isCompressed() ? m_outerIndex[j + 1] : begin + m_innerNonZeros[j];
1061 Index capacity = m_outerIndex[j + 1] - end;
1062
1063 bool doInsertion = insertionLocations(j) >= 0;
1064 bool breakUpCopy = doInsertion && (capacity > 0);
1065 // break up copy for sorted insertion into inactive nonzeros
1066 // optionally, add another criterion, i.e. 'breakUpCopy || (capacity > threshold)'
1067 // where `threshold >= 0` to skip inactive nonzeros in each vector
1068 // this reduces the total number of copied elements, but requires more moveChunk calls
1069 if (breakUpCopy) {
1070 Index copyBegin = m_outerIndex[j + 1];
1071 Index to = copyBegin + shift;
1072 Index chunkSize = copyEnd - copyBegin;
1073 m_data.moveChunk(copyBegin, to, chunkSize);
1074 copyEnd = end;
1075 }
1076
1077 m_outerIndex[j + 1] += shift;
1078
1079 if (doInsertion) {
1080 // if there is capacity, shift into the inactive nonzeros
1081 if (capacity > 0) shift++;
1082 Index copyBegin = insertionLocations(j);
1083 Index to = copyBegin + shift;
1084 Index chunkSize = copyEnd - copyBegin;
1085 m_data.moveChunk(copyBegin, to, chunkSize);
1086 Index dst = to - 1;
1087 m_data.index(dst) = StorageIndex(j);
1088 m_data.value(dst) = Scalar(0);
1089 assignFunc.assignCoeff(m_data.value(dst), diaEval.coeff(j));
1090 if (!isCompressed()) m_innerNonZeros[j]++;
1091 shift--;
1092 deferredInsertions--;
1093 copyEnd = copyBegin;
1094 }
1095 }
1096 }
1097 eigen_assert((shift == 0) && (deferredInsertions == 0));
1098 }
1099 }
1100
1101 /* These functions are used to avoid a redundant binary search operation in functions such as coeffRef() and assume
1102 * `dst` is the appropriate sorted insertion point */
1103 EIGEN_STRONG_INLINE Scalar& insertAtByOuterInner(Index outer, Index inner, Index dst);
1104 Scalar& insertCompressedAtByOuterInner(Index outer, Index inner, Index dst);
1105 Scalar& insertUncompressedAtByOuterInner(Index outer, Index inner, Index dst);
1106
1107 private:
1108 EIGEN_STATIC_ASSERT(NumTraits<StorageIndex>::IsSigned, THE_INDEX_TYPE_MUST_BE_A_SIGNED_TYPE)
1109 EIGEN_STATIC_ASSERT((Options & (ColMajor | RowMajor)) == Options, INVALID_MATRIX_TEMPLATE_PARAMETERS)
1110
1111 struct default_prunning_func {
1112 default_prunning_func(const Scalar& ref, const RealScalar& eps) : reference(ref), epsilon(eps) {}
1113 inline bool operator()(const Index&, const Index&, const Scalar& value) const {
1114 return !internal::isMuchSmallerThan(value, reference, epsilon);
1115 }
1116 Scalar reference;
1117 RealScalar epsilon;
1118 };
1119};
1120
1121namespace internal {
1122
1123// Creates a compressed sparse matrix from a range of unsorted triplets
1124// Requires temporary storage to handle duplicate entries
1125template <typename InputIterator, typename SparseMatrixType, typename DupFunctor>
1126void set_from_triplets(const InputIterator& begin, const InputIterator& end, SparseMatrixType& mat,
1127 DupFunctor dup_func) {
1128 constexpr bool IsRowMajor = SparseMatrixType::IsRowMajor;
1129 using StorageIndex = typename SparseMatrixType::StorageIndex;
1130 using IndexMap = typename VectorX<StorageIndex>::AlignedMapType;
1131 using TransposedSparseMatrix =
1132 SparseMatrix<typename SparseMatrixType::Scalar, IsRowMajor ? ColMajor : RowMajor, StorageIndex>;
1133
1134 if (begin == end) {
1135 // Clear out existing data (if any).
1136 mat.setZero();
1137 return;
1138 }
1139
1140 // There are two strategies to consider for constructing a matrix from unordered triplets:
1141 // A) construct the 'mat' in its native storage order and sort in-place (less memory); or,
1142 // B) construct the transposed matrix and use an implicit sort upon assignment to `mat` (less time).
1143 // This routine uses B) for faster execution time.
1144 TransposedSparseMatrix trmat(mat.rows(), mat.cols());
1145
1146 // scan triplets to determine allocation size before constructing matrix
1147 Index nonZeros = 0;
1148 for (InputIterator it(begin); it != end; ++it) {
1149 eigen_assert(it->row() >= 0 && it->row() < mat.rows() && it->col() >= 0 && it->col() < mat.cols());
1150 StorageIndex j = convert_index<StorageIndex>(IsRowMajor ? it->col() : it->row());
1151 eigen_assert(nonZeros < NumTraits<StorageIndex>::highest() &&
1152 "non-zero count exceeds StorageIndex range, use a wider StorageIndex (e.g. int64_t)");
1153 if (nonZeros >= NumTraits<StorageIndex>::highest()) internal::throw_std_bad_alloc();
1154 trmat.outerIndexPtr()[j + 1]++;
1155 nonZeros++;
1156 }
1157
1158 std::partial_sum(trmat.outerIndexPtr(), trmat.outerIndexPtr() + trmat.outerSize() + 1, trmat.outerIndexPtr());
1159 eigen_assert(nonZeros == trmat.outerIndexPtr()[trmat.outerSize()]);
1160 trmat.resizeNonZeros(nonZeros);
1161
1162 // construct temporary array to track insertions (outersize) and collapse duplicates (innersize)
1163 ei_declare_aligned_stack_constructed_variable(StorageIndex, tmp, numext::maxi(mat.innerSize(), mat.outerSize()), 0);
1164 smart_copy(trmat.outerIndexPtr(), trmat.outerIndexPtr() + trmat.outerSize(), tmp);
1165
1166 // push triplets to back of each vector
1167 for (InputIterator it(begin); it != end; ++it) {
1168 StorageIndex j = convert_index<StorageIndex>(IsRowMajor ? it->col() : it->row());
1169 StorageIndex i = convert_index<StorageIndex>(IsRowMajor ? it->row() : it->col());
1170 StorageIndex k = tmp[j];
1171 trmat.data().index(k) = i;
1172 trmat.data().value(k) = it->value();
1173 tmp[j]++;
1174 }
1175
1176 IndexMap wi(tmp, trmat.innerSize());
1177 trmat.collapseDuplicates(wi, dup_func);
1178 // implicit sorting
1179 mat = trmat;
1180}
1181
1182// Creates a compressed sparse matrix from a sorted range of triplets
1183template <typename InputIterator, typename SparseMatrixType, typename DupFunctor>
1184void set_from_triplets_sorted(const InputIterator& begin, const InputIterator& end, SparseMatrixType& mat,
1185 DupFunctor dup_func) {
1186 constexpr bool IsRowMajor = SparseMatrixType::IsRowMajor;
1187 using StorageIndex = typename SparseMatrixType::StorageIndex;
1188
1189 if (begin == end) return;
1190
1191 constexpr StorageIndex kEmptyIndexValue(-1);
1192 // deallocate inner nonzeros if present and zero outerIndexPtr
1193 mat.resize(mat.rows(), mat.cols());
1194 // use outer indices to count non zero entries (excluding duplicate entries)
1195 StorageIndex previous_j = kEmptyIndexValue;
1196 StorageIndex previous_i = kEmptyIndexValue;
1197 // scan triplets to determine allocation size before constructing matrix
1198 Index nonZeros = 0;
1199 for (InputIterator it(begin); it != end; ++it) {
1200 eigen_assert(it->row() >= 0 && it->row() < mat.rows() && it->col() >= 0 && it->col() < mat.cols());
1201 StorageIndex j = convert_index<StorageIndex>(IsRowMajor ? it->row() : it->col());
1202 StorageIndex i = convert_index<StorageIndex>(IsRowMajor ? it->col() : it->row());
1203 eigen_assert(j > previous_j || (j == previous_j && i >= previous_i));
1204 // identify duplicates by examining previous location
1205 bool duplicate = (previous_j == j) && (previous_i == i);
1206 if (!duplicate) {
1207 eigen_assert(nonZeros < NumTraits<StorageIndex>::highest() &&
1208 "non-zero count exceeds StorageIndex range, use a wider StorageIndex (e.g. int64_t)");
1209 if (nonZeros >= NumTraits<StorageIndex>::highest()) internal::throw_std_bad_alloc();
1210 nonZeros++;
1211 mat.outerIndexPtr()[j + 1]++;
1212 previous_j = j;
1213 previous_i = i;
1214 }
1215 }
1216
1217 // finalize outer indices and allocate memory
1218 std::partial_sum(mat.outerIndexPtr(), mat.outerIndexPtr() + mat.outerSize() + 1, mat.outerIndexPtr());
1219 eigen_assert(nonZeros == mat.outerIndexPtr()[mat.outerSize()]);
1220 mat.resizeNonZeros(nonZeros);
1221
1222 previous_i = kEmptyIndexValue;
1223 previous_j = kEmptyIndexValue;
1224 Index back = 0;
1225 for (InputIterator it(begin); it != end; ++it) {
1226 StorageIndex j = convert_index<StorageIndex>(IsRowMajor ? it->row() : it->col());
1227 StorageIndex i = convert_index<StorageIndex>(IsRowMajor ? it->col() : it->row());
1228 bool duplicate = (previous_j == j) && (previous_i == i);
1229 if (duplicate) {
1230 mat.data().value(back - 1) = dup_func(mat.data().value(back - 1), it->value());
1231 } else {
1232 // push triplets to back
1233 mat.data().index(back) = i;
1234 mat.data().value(back) = it->value();
1235 previous_j = j;
1236 previous_i = i;
1237 back++;
1238 }
1239 }
1240 eigen_assert(back == nonZeros);
1241 // matrix is finalized
1242}
1243
1244// thin wrapper around a generic binary functor to use the sparse disjunction evaluator instead of the default
1245// "arithmetic" evaluator
1246template <typename DupFunctor, typename LhsScalar, typename RhsScalar = LhsScalar>
1247struct scalar_disjunction_op {
1248 using result_type = typename result_of<DupFunctor(LhsScalar, RhsScalar)>::type;
1249 scalar_disjunction_op(const DupFunctor& op) : m_functor(op) {}
1250 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE result_type operator()(const LhsScalar& a, const RhsScalar& b) const {
1251 return m_functor(a, b);
1252 }
1253 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const DupFunctor& functor() const { return m_functor; }
1254 const DupFunctor& m_functor;
1255};
1256
1257template <typename DupFunctor, typename LhsScalar, typename RhsScalar>
1258struct functor_traits<scalar_disjunction_op<DupFunctor, LhsScalar, RhsScalar>> : public functor_traits<DupFunctor> {};
1259
1260// Creates a compressed sparse matrix from its existing entries and those from an unsorted range of triplets
1261template <typename InputIterator, typename SparseMatrixType, typename DupFunctor>
1262void insert_from_triplets(const InputIterator& begin, const InputIterator& end, SparseMatrixType& mat,
1263 DupFunctor dup_func) {
1264 using Scalar = typename SparseMatrixType::Scalar;
1265 using SrcXprType =
1266 CwiseBinaryOp<scalar_disjunction_op<DupFunctor, Scalar>, const SparseMatrixType, const SparseMatrixType>;
1267
1268 // set_from_triplets is necessary to sort the inner indices and remove the duplicate entries
1269 SparseMatrixType trips(mat.rows(), mat.cols());
1270 set_from_triplets(begin, end, trips, dup_func);
1271
1272 SrcXprType src = mat.binaryExpr(trips, scalar_disjunction_op<DupFunctor, Scalar>(dup_func));
1273 // the sparse assignment procedure creates a temporary matrix and swaps the final result
1274 assign_sparse_to_sparse<SparseMatrixType, SrcXprType>(mat, src);
1275}
1276
1277// Creates a compressed sparse matrix from its existing entries and those from an sorted range of triplets
1278template <typename InputIterator, typename SparseMatrixType, typename DupFunctor>
1279void insert_from_triplets_sorted(const InputIterator& begin, const InputIterator& end, SparseMatrixType& mat,
1280 DupFunctor dup_func) {
1281 using Scalar = typename SparseMatrixType::Scalar;
1282 using SrcXprType =
1283 CwiseBinaryOp<scalar_disjunction_op<DupFunctor, Scalar>, const SparseMatrixType, const SparseMatrixType>;
1284
1285 // Saving the trips temporary would need a direct mat+triplets merge with
1286 // on-the-fly duplicate collapsing (non-trivial).
1287 SparseMatrixType trips(mat.rows(), mat.cols());
1288 set_from_triplets_sorted(begin, end, trips, dup_func);
1289
1290 SrcXprType src = mat.binaryExpr(trips, scalar_disjunction_op<DupFunctor, Scalar>(dup_func));
1291 // the sparse assignment procedure creates a temporary matrix and swaps the final result
1292 assign_sparse_to_sparse<SparseMatrixType, SrcXprType>(mat, src);
1293}
1294
1295} // namespace internal
1296
1334template <typename Scalar, int Options_, typename StorageIndex_>
1335template <typename InputIterators>
1337 const InputIterators& end) {
1338 internal::set_from_triplets<InputIterators, SparseMatrix<Scalar, Options_, StorageIndex_>>(
1339 begin, end, *this, internal::scalar_sum_op<Scalar, Scalar>());
1340}
1341
1351template <typename Scalar, int Options_, typename StorageIndex_>
1352template <typename InputIterators, typename DupFunctor>
1354 const InputIterators& end, DupFunctor dup_func) {
1355 internal::set_from_triplets<InputIterators, SparseMatrix<Scalar, Options_, StorageIndex_>, DupFunctor>(
1356 begin, end, *this, dup_func);
1357}
1358
1364template <typename Scalar, int Options_, typename StorageIndex_>
1365template <typename InputIterators>
1367 const InputIterators& end) {
1368 internal::set_from_triplets_sorted<InputIterators, SparseMatrix<Scalar, Options_, StorageIndex_>>(
1369 begin, end, *this, internal::scalar_sum_op<Scalar, Scalar>());
1370}
1371
1381template <typename Scalar, int Options_, typename StorageIndex_>
1382template <typename InputIterators, typename DupFunctor>
1384 const InputIterators& end,
1385 DupFunctor dup_func) {
1386 internal::set_from_triplets_sorted<InputIterators, SparseMatrix<Scalar, Options_, StorageIndex_>, DupFunctor>(
1387 begin, end, *this, dup_func);
1388}
1389
1428template <typename Scalar, int Options_, typename StorageIndex_>
1429template <typename InputIterators>
1431 const InputIterators& end) {
1432 internal::insert_from_triplets<InputIterators, SparseMatrix<Scalar, Options_, StorageIndex_>>(
1433 begin, end, *this, internal::scalar_sum_op<Scalar, Scalar>());
1434}
1435
1445template <typename Scalar, int Options_, typename StorageIndex_>
1446template <typename InputIterators, typename DupFunctor>
1448 const InputIterators& end, DupFunctor dup_func) {
1449 internal::insert_from_triplets<InputIterators, SparseMatrix<Scalar, Options_, StorageIndex_>, DupFunctor>(
1450 begin, end, *this, dup_func);
1451}
1452
1457template <typename Scalar, int Options_, typename StorageIndex_>
1458template <typename InputIterators>
1460 const InputIterators& end) {
1461 internal::insert_from_triplets_sorted<InputIterators, SparseMatrix<Scalar, Options_, StorageIndex_>>(
1462 begin, end, *this, internal::scalar_sum_op<Scalar, Scalar>());
1463}
1464
1474template <typename Scalar, int Options_, typename StorageIndex_>
1475template <typename InputIterators, typename DupFunctor>
1477 const InputIterators& end,
1478 DupFunctor dup_func) {
1479 internal::insert_from_triplets_sorted<InputIterators, SparseMatrix<Scalar, Options_, StorageIndex_>, DupFunctor>(
1480 begin, end, *this, dup_func);
1481}
1482
1484template <typename Scalar_, int Options_, typename StorageIndex_>
1485template <typename Derived, typename DupFunctor>
1486void SparseMatrix<Scalar_, Options_, StorageIndex_>::collapseDuplicates(DenseBase<Derived>& wi, DupFunctor dup_func) {
1487 // removes duplicate entries and compresses the matrix
1488 // the excess allocated memory is not released
1489 // the inner indices do not need to be sorted, nor is the matrix returned in a sorted state
1490 eigen_assert(wi.size() == m_innerSize);
1491 constexpr StorageIndex kEmptyIndexValue(-1);
1492 wi.setConstant(kEmptyIndexValue);
1493 StorageIndex count = 0;
1494 const bool is_compressed = isCompressed();
1495 // for each inner-vector, wi[inner_index] will hold the position of first element into the index/value buffers
1496 for (Index j = 0; j < m_outerSize; ++j) {
1497 const StorageIndex newBegin = count;
1498 const StorageIndex end = is_compressed ? m_outerIndex[j + 1] : m_outerIndex[j] + m_innerNonZeros[j];
1499 for (StorageIndex k = m_outerIndex[j]; k < end; ++k) {
1500 StorageIndex i = m_data.index(k);
1501 if (wi(i) >= newBegin) {
1502 // entry at k is a duplicate
1503 // accumulate it into the primary entry located at wi(i)
1504 m_data.value(wi(i)) = dup_func(m_data.value(wi(i)), m_data.value(k));
1505 } else {
1506 // k is the primary entry in j with inner index i
1507 // shift it to the left and record its location at wi(i)
1508 m_data.index(count) = i;
1509 m_data.value(count) = m_data.value(k);
1510 wi(i) = count;
1511 ++count;
1512 }
1513 }
1514 m_outerIndex[j] = newBegin;
1515 }
1516 m_outerIndex[m_outerSize] = count;
1517 m_data.resize(count);
1518
1519 // turn the matrix into compressed form (if it is not already)
1520 internal::conditional_aligned_delete_auto<StorageIndex, true>(m_innerNonZeros, m_outerSize);
1521 m_innerNonZeros = 0;
1522}
1523
1525template <typename Scalar, int Options_, typename StorageIndex_>
1526template <typename OtherDerived>
1527EIGEN_DONT_INLINE SparseMatrix<Scalar, Options_, StorageIndex_>&
1528SparseMatrix<Scalar, Options_, StorageIndex_>::operator=(const SparseMatrixBase<OtherDerived>& other) {
1529 EIGEN_STATIC_ASSERT(
1530 (std::is_same<Scalar, typename OtherDerived::Scalar>::value),
1531 YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY)
1532
1533#ifdef EIGEN_SPARSE_CREATE_TEMPORARY_PLUGIN
1534 EIGEN_SPARSE_CREATE_TEMPORARY_PLUGIN
1535#endif
1536
1537 const bool needToTranspose = (Flags & RowMajorBit) != (internal::evaluator<OtherDerived>::Flags & RowMajorBit);
1538 if (needToTranspose) {
1539#ifdef EIGEN_SPARSE_TRANSPOSED_COPY_PLUGIN
1540 EIGEN_SPARSE_TRANSPOSED_COPY_PLUGIN
1541#endif
1542 // two passes algorithm:
1543 // 1 - compute the number of coeffs per dest inner vector
1544 // 2 - do the actual copy/eval
1545 // Since each coeff of the rhs has to be evaluated twice, let's evaluate it if needed
1546 typedef
1547 typename internal::nested_eval<OtherDerived, 2, typename internal::plain_matrix_type<OtherDerived>::type>::type
1548 OtherCopy;
1549 typedef internal::remove_all_t<OtherCopy> OtherCopy_;
1550 typedef internal::evaluator<OtherCopy_> OtherCopyEval;
1551 OtherCopy otherCopy(other.derived());
1552 OtherCopyEval otherCopyEval(otherCopy);
1553
1554 SparseMatrix dest(other.rows(), other.cols());
1555 Eigen::Map<IndexVector>(dest.m_outerIndex, dest.outerSize()).setZero();
1556
1557 // pass 1
1558 // FIXME: merge the above copy into this pass to avoid iterating twice.
1559 for (Index j = 0; j < otherCopy.outerSize(); ++j)
1560 for (typename OtherCopyEval::InnerIterator it(otherCopyEval, j); it; ++it) ++dest.m_outerIndex[it.index()];
1561
1562 // prefix sum
1563 StorageIndex count = 0;
1564 IndexVector positions(dest.outerSize());
1565 for (Index j = 0; j < dest.outerSize(); ++j) {
1566 StorageIndex tmp = dest.m_outerIndex[j];
1567 dest.m_outerIndex[j] = count;
1568 positions[j] = count;
1569 count += tmp;
1570 }
1571 dest.m_outerIndex[dest.outerSize()] = count;
1572 // alloc
1573 dest.m_data.resize(count);
1574 // pass 2
1575 for (StorageIndex j = 0; j < otherCopy.outerSize(); ++j) {
1576 for (typename OtherCopyEval::InnerIterator it(otherCopyEval, j); it; ++it) {
1577 Index pos = internal::convert_index<Index>(positions.coeff(it.index()));
1578 positions.coeffRef(it.index()) = internal::convert_index<StorageIndex>(pos + 1);
1579 dest.m_data.index(pos) = j;
1580 dest.m_data.value(pos) = it.value();
1581 }
1582 }
1583 this->swap(dest);
1584 return *this;
1585 } else {
1586 if (other.isRValue()) {
1587 initAssignment(other.derived());
1588 }
1589 // there is no special optimization
1590 return Base::operator=(other.derived());
1591 }
1592}
1593
1594template <typename Scalar_, int Options_, typename StorageIndex_>
1595inline typename SparseMatrix<Scalar_, Options_, StorageIndex_>::Scalar&
1597 return insertByOuterInner(IsRowMajor ? row : col, IsRowMajor ? col : row);
1598}
1599
1600template <typename Scalar_, int Options_, typename StorageIndex_>
1601EIGEN_STRONG_INLINE typename SparseMatrix<Scalar_, Options_, StorageIndex_>::Scalar&
1602SparseMatrix<Scalar_, Options_, StorageIndex_>::insertAtByOuterInner(Index outer, Index inner, Index dst) {
1603 // random insertion into compressed matrix is very slow
1604 uncompress();
1605 return insertUncompressedAtByOuterInner(outer, inner, dst);
1606}
1607
1608template <typename Scalar_, int Options_, typename StorageIndex_>
1609EIGEN_DEPRECATED EIGEN_DONT_INLINE typename SparseMatrix<Scalar_, Options_, StorageIndex_>::Scalar&
1610SparseMatrix<Scalar_, Options_, StorageIndex_>::insertUncompressed(Index row, Index col) {
1611 eigen_assert(!isCompressed());
1612 Index outer = IsRowMajor ? row : col;
1613 Index inner = IsRowMajor ? col : row;
1614 Index start = m_outerIndex[outer];
1615 Index end = start + m_innerNonZeros[outer];
1616 Index dst = start == end ? end : m_data.searchLowerIndex(start, end, inner);
1617 if (dst == end) {
1618 Index capacity = m_outerIndex[outer + 1] - end;
1619 if (capacity > 0) {
1620 // implies uncompressed: push to back of vector
1621 m_innerNonZeros[outer]++;
1622 m_data.index(end) = StorageIndex(inner);
1623 m_data.value(end) = Scalar(0);
1624 return m_data.value(end);
1625 }
1626 }
1627 eigen_assert((dst == end || m_data.index(dst) != inner) &&
1628 "you cannot insert an element that already exists, you must call coeffRef to this end");
1629 return insertUncompressedAtByOuterInner(outer, inner, dst);
1630}
1631
1632template <typename Scalar_, int Options_, typename StorageIndex_>
1633EIGEN_DEPRECATED EIGEN_DONT_INLINE typename SparseMatrix<Scalar_, Options_, StorageIndex_>::Scalar&
1634SparseMatrix<Scalar_, Options_, StorageIndex_>::insertCompressed(Index row, Index col) {
1635 eigen_assert(isCompressed());
1636 Index outer = IsRowMajor ? row : col;
1637 Index inner = IsRowMajor ? col : row;
1638 Index start = m_outerIndex[outer];
1639 Index end = m_outerIndex[outer + 1];
1640 Index dst = start == end ? end : m_data.searchLowerIndex(start, end, inner);
1641 eigen_assert((dst == end || m_data.index(dst) != inner) &&
1642 "you cannot insert an element that already exists, you must call coeffRef to this end");
1643 return insertCompressedAtByOuterInner(outer, inner, dst);
1644}
1645
1646template <typename Scalar_, int Options_, typename StorageIndex_>
1647typename SparseMatrix<Scalar_, Options_, StorageIndex_>::Scalar&
1648SparseMatrix<Scalar_, Options_, StorageIndex_>::insertCompressedAtByOuterInner(Index outer, Index inner, Index dst) {
1649 eigen_assert(isCompressed());
1650 // compressed insertion always requires expanding the buffer
1651 // first, check if there is adequate allocated memory
1652 if (m_data.allocatedSize() <= m_data.size()) {
1653 // if there is no capacity for a single insertion, double the capacity
1654 // increase capacity by a minimum of 32
1655 Index minReserve = 32;
1656 Index reserveSize = numext::maxi(minReserve, m_data.allocatedSize());
1657 m_data.reserve(reserveSize);
1658 }
1659 m_data.resize(m_data.size() + 1);
1660 Index chunkSize = m_outerIndex[m_outerSize] - dst;
1661 // shift the existing data to the right if necessary
1662 m_data.moveChunk(dst, dst + 1, chunkSize);
1663 // update nonzero counts
1664 // potentially O(outerSize) bottleneck!
1665 for (Index j = outer; j < m_outerSize; j++) m_outerIndex[j + 1]++;
1666 // initialize the coefficient
1667 m_data.index(dst) = StorageIndex(inner);
1668 m_data.value(dst) = Scalar(0);
1669 // return a reference to the coefficient
1670 return m_data.value(dst);
1671}
1672
1673template <typename Scalar_, int Options_, typename StorageIndex_>
1674typename SparseMatrix<Scalar_, Options_, StorageIndex_>::Scalar&
1675SparseMatrix<Scalar_, Options_, StorageIndex_>::insertUncompressedAtByOuterInner(Index outer, Index inner, Index dst) {
1676 eigen_assert(!isCompressed());
1677 // find a vector with capacity, starting at `outer` and searching to the left and right
1678 for (Index leftTarget = outer - 1, rightTarget = outer; (leftTarget >= 0) || (rightTarget < m_outerSize);) {
1679 if (rightTarget < m_outerSize) {
1680 Index start = m_outerIndex[rightTarget];
1681 Index end = start + m_innerNonZeros[rightTarget];
1682 Index nextStart = m_outerIndex[rightTarget + 1];
1683 Index capacity = nextStart - end;
1684 if (capacity > 0) {
1685 // move [dst, end) to dst+1 and insert at dst
1686 Index chunkSize = end - dst;
1687 if (chunkSize > 0) m_data.moveChunk(dst, dst + 1, chunkSize);
1688 m_innerNonZeros[outer]++;
1689 for (Index j = outer; j < rightTarget; j++) m_outerIndex[j + 1]++;
1690 m_data.index(dst) = StorageIndex(inner);
1691 m_data.value(dst) = Scalar(0);
1692 return m_data.value(dst);
1693 }
1694 rightTarget++;
1695 }
1696 if (leftTarget >= 0) {
1697 Index start = m_outerIndex[leftTarget];
1698 Index end = start + m_innerNonZeros[leftTarget];
1699 Index nextStart = m_outerIndex[leftTarget + 1];
1700 Index capacity = nextStart - end;
1701 if (capacity > 0) {
1702 // tricky: dst is a lower bound, so we must insert at dst-1 when shifting left
1703 // move [nextStart, dst) to nextStart-1 and insert at dst-1
1704 Index chunkSize = dst - nextStart;
1705 if (chunkSize > 0) m_data.moveChunk(nextStart, nextStart - 1, chunkSize);
1706 m_innerNonZeros[outer]++;
1707 for (Index j = leftTarget; j < outer; j++) m_outerIndex[j + 1]--;
1708 m_data.index(dst - 1) = StorageIndex(inner);
1709 m_data.value(dst - 1) = Scalar(0);
1710 return m_data.value(dst - 1);
1711 }
1712 leftTarget--;
1713 }
1714 }
1715
1716 // no room for interior insertion
1717 // nonZeros() == m_data.size()
1718 // record offset as outerIndexPtr will change
1719 Index dst_offset = dst - m_outerIndex[outer];
1720 // allocate space for random insertion
1721 if (m_data.allocatedSize() == 0) {
1722 // fast method to allocate space for one element per vector in empty matrix
1723 m_data.resize(m_outerSize);
1724 std::iota(m_outerIndex, m_outerIndex + m_outerSize + 1, StorageIndex(0));
1725 } else {
1726 // check for integer overflow: if maxReserveSize == 0, insertion is not possible
1727 Index maxReserveSize = static_cast<Index>(NumTraits<StorageIndex>::highest()) - m_data.allocatedSize();
1728 eigen_assert(maxReserveSize > 0);
1729 if (m_outerSize <= maxReserveSize) {
1730 // allocate space for one additional element per vector
1731 reserveInnerVectors(IndexVector::Constant(m_outerSize, 1));
1732 } else {
1733 // handle the edge case where StorageIndex is insufficient to reserve outerSize additional elements
1734 // allocate space for one additional element in the interval [outer,maxReserveSize)
1735 typedef internal::sparse_reserve_op<StorageIndex> ReserveSizesOp;
1736 typedef CwiseNullaryOp<ReserveSizesOp, IndexVector> ReserveSizesXpr;
1737 ReserveSizesXpr reserveSizesXpr(m_outerSize, 1, ReserveSizesOp(outer, m_outerSize, maxReserveSize));
1738 reserveInnerVectors(reserveSizesXpr);
1739 }
1740 }
1741 // insert element at `dst` with new outer indices
1742 Index start = m_outerIndex[outer];
1743 Index end = start + m_innerNonZeros[outer];
1744 Index new_dst = start + dst_offset;
1745 Index chunkSize = end - new_dst;
1746 if (chunkSize > 0) m_data.moveChunk(new_dst, new_dst + 1, chunkSize);
1747 m_innerNonZeros[outer]++;
1748 m_data.index(new_dst) = StorageIndex(inner);
1749 m_data.value(new_dst) = Scalar(0);
1750 return m_data.value(new_dst);
1751}
1752
1753namespace internal {
1754
1755template <typename Scalar_, int Options_, typename StorageIndex_>
1756struct evaluator<SparseMatrix<Scalar_, Options_, StorageIndex_>>
1757 : evaluator<SparseCompressedBase<SparseMatrix<Scalar_, Options_, StorageIndex_>>> {
1758 using Base = evaluator<SparseCompressedBase<SparseMatrix<Scalar_, Options_, StorageIndex_>>>;
1759 using SparseMatrixType = SparseMatrix<Scalar_, Options_, StorageIndex_>;
1760 evaluator() = default;
1761 explicit evaluator(const SparseMatrixType& mat) : Base(mat) {}
1762};
1763
1764} // namespace internal
1765
1766// Specialization for SparseMatrix.
1767// Serializes [rows, cols, isCompressed, outerSize, innerBufferSize,
1768// innerNonZeros, outerIndices, innerIndices, values].
1769template <typename Scalar, int Options, typename StorageIndex>
1770class Serializer<SparseMatrix<Scalar, Options, StorageIndex>, void> {
1771 public:
1772 using SparseMat = SparseMatrix<Scalar, Options, StorageIndex>;
1773
1774 struct Header {
1775 typename SparseMat::Index rows;
1776 typename SparseMat::Index cols;
1777 bool compressed;
1778 Index outer_size;
1779 Index inner_buffer_size;
1780 };
1781
1782 EIGEN_DEVICE_FUNC size_t size(const SparseMat& value) const {
1783 // innerNonZeros.
1784 std::size_t num_storage_indices = value.isCompressed() ? 0 : value.outerSize();
1785 // Outer indices.
1786 num_storage_indices += value.outerSize() + 1;
1787 // Inner indices.
1788 const StorageIndex inner_buffer_size = value.outerIndexPtr()[value.outerSize()];
1789 num_storage_indices += inner_buffer_size;
1790 // Values.
1791 std::size_t num_values = inner_buffer_size;
1792 return sizeof(Header) + sizeof(Scalar) * num_values + sizeof(StorageIndex) * num_storage_indices;
1793 }
1794
1795 EIGEN_DEVICE_FUNC uint8_t* serialize(uint8_t* dest, uint8_t* end, const SparseMat& value) {
1796 if (EIGEN_PREDICT_FALSE(dest == nullptr)) return nullptr;
1797 if (EIGEN_PREDICT_FALSE(dest + size(value) > end)) return nullptr;
1798
1799 const size_t header_bytes = sizeof(Header);
1800 Header header = {value.rows(), value.cols(), value.isCompressed(), value.outerSize(),
1801 value.outerIndexPtr()[value.outerSize()]};
1802 EIGEN_USING_STD(memcpy)
1803 memcpy(dest, &header, header_bytes);
1804 dest += header_bytes;
1805
1806 // innerNonZeros.
1807 if (!header.compressed) {
1808 std::size_t data_bytes = sizeof(StorageIndex) * header.outer_size;
1809 memcpy(dest, value.innerNonZeroPtr(), data_bytes);
1810 dest += data_bytes;
1811 }
1812
1813 // Outer indices.
1814 std::size_t data_bytes = sizeof(StorageIndex) * (header.outer_size + 1);
1815 memcpy(dest, value.outerIndexPtr(), data_bytes);
1816 dest += data_bytes;
1817
1818 // Inner indices.
1819 data_bytes = sizeof(StorageIndex) * header.inner_buffer_size;
1820 memcpy(dest, value.innerIndexPtr(), data_bytes);
1821 dest += data_bytes;
1822
1823 // Values.
1824 data_bytes = sizeof(Scalar) * header.inner_buffer_size;
1825 memcpy(dest, value.valuePtr(), data_bytes);
1826 dest += data_bytes;
1827
1828 return dest;
1829 }
1830
1831 EIGEN_DEVICE_FUNC const uint8_t* deserialize(const uint8_t* src, const uint8_t* end, SparseMat& value) const {
1832 if (EIGEN_PREDICT_FALSE(src == nullptr)) return nullptr;
1833 if (EIGEN_PREDICT_FALSE(src + sizeof(Header) > end)) return nullptr;
1834
1835 const size_t header_bytes = sizeof(Header);
1836 Header header;
1837 EIGEN_USING_STD(memcpy)
1838 memcpy(&header, src, header_bytes);
1839 src += header_bytes;
1840
1841 value.setZero();
1842 value.resize(header.rows, header.cols);
1843 if (header.compressed) {
1844 value.makeCompressed();
1845 } else {
1846 value.uncompress();
1847 }
1848
1849 // Adjust value ptr size.
1850 value.data().resize(header.inner_buffer_size);
1851
1852 // Initialize compressed state and inner non-zeros.
1853 if (!header.compressed) {
1854 // Inner non-zero counts.
1855 std::size_t data_bytes = sizeof(StorageIndex) * header.outer_size;
1856 if (EIGEN_PREDICT_FALSE(src + data_bytes > end)) return nullptr;
1857 if (data_bytes != 0) {
1858 memcpy(value.innerNonZeroPtr(), src, data_bytes);
1859 }
1860 src += data_bytes;
1861 }
1862
1863 // Outer indices.
1864 std::size_t data_bytes = sizeof(StorageIndex) * (header.outer_size + 1);
1865 if (EIGEN_PREDICT_FALSE(src + data_bytes > end)) return nullptr;
1866 if (data_bytes != 0) {
1867 memcpy(value.outerIndexPtr(), src, data_bytes);
1868 }
1869 src += data_bytes;
1870
1871 // Inner indices.
1872 data_bytes = sizeof(StorageIndex) * header.inner_buffer_size;
1873 if (EIGEN_PREDICT_FALSE(src + data_bytes > end)) return nullptr;
1874 if (data_bytes != 0) {
1875 memcpy(value.innerIndexPtr(), src, data_bytes);
1876 }
1877 src += data_bytes;
1878
1879 // Values.
1880 data_bytes = sizeof(Scalar) * header.inner_buffer_size;
1881 if (EIGEN_PREDICT_FALSE(src + data_bytes > end)) return nullptr;
1882 if (data_bytes != 0) {
1883 memcpy(value.valuePtr(), src, data_bytes);
1884 }
1885 src += data_bytes;
1886 return src;
1887 }
1888};
1889
1890} // end namespace Eigen
1891
1892#endif // EIGEN_SPARSEMATRIX_H
Base class for all dense matrices, vectors, and arrays.
Definition DenseBase.h:45
Derived & setConstant(const Scalar &value)
Definition CwiseNullaryOp.h:333
Base class for diagonal matrices and expressions.
Definition DiagonalMatrix.h:34
const Derived & derived() const
Definition DiagonalMatrix.h:60
Expression of a diagonal/subdiagonal/superdiagonal in a matrix.
Definition Diagonal.h:78
A matrix or vector expression mapping an existing array of data.
Definition Map.h:97
Common base class for sparse [compressed]-{row|column}-storage format.
Definition SparseCompressedBase.h:44
Index nonZeros() const
Definition SparseCompressedBase.h:65
bool isCompressed() const
Definition SparseCompressedBase.h:115
Base class of any sparse matrices or sparse expressions.
Definition SparseMatrixBase.h:31
typename NumTraits< Scalar >::Real RealScalar
Definition SparseMatrixBase.h:128
constexpr RowXpr row(Index i)
Definition SparseMatrixBase.h:1094
typename internal::traits< SparseMatrix< Scalar_, Options_, StorageIndex_ > >::StorageIndex StorageIndex
Definition SparseMatrixBase.h:45
constexpr ColXpr col(Index i)
Definition SparseMatrixBase.h:1081
@ Flags
Definition SparseMatrixBase.h:95
A versatile sparse matrix representation.
Definition SparseMatrix.h:122
Scalar coeff(Index row, Index col) const
Definition SparseMatrix.h:212
void resize(Index rows, NoChange_t)
Definition SparseMatrix.h:765
const ConstDiagonalReturnType diagonal() const
Definition SparseMatrix.h:772
void swap(SparseMatrix &other)
Definition SparseMatrix.h:844
void insertFromSortedTriplets(const InputIterators &begin, const InputIterators &end, DupFunctor dup_func)
Definition SparseMatrix.h:1476
const StorageIndex * innerIndexPtr() const
Definition SparseMatrix.h:181
void setZero()
Definition SparseMatrix.h:305
bool isCompressed() const
Definition SparseCompressedBase.h:115
Index cols() const
Definition SparseMatrix.h:162
StorageIndex * innerIndexPtr()
Definition SparseMatrix.h:185
SparseMatrix(const ReturnByValue< OtherDerived > &other)
Copy constructor with in-place evaluation.
Definition SparseMatrix.h:829
void setFromSortedTriplets(const InputIterators &begin, const InputIterators &end)
Definition SparseMatrix.h:1366
void setFromTriplets(const InputIterators &begin, const InputIterators &end, DupFunctor dup_func)
Definition SparseMatrix.h:1353
Index outerSize() const
Definition SparseMatrix.h:167
SparseMatrix()
Definition SparseMatrix.h:781
void uncompress()
Definition SparseMatrix.h:624
void insertFromSortedTriplets(const InputIterators &begin, const InputIterators &end)
Definition SparseMatrix.h:1459
const Scalar * valuePtr() const
Definition SparseMatrix.h:172
void makeCompressed()
Definition SparseMatrix.h:591
void setFromSortedTriplets(const InputIterators &begin, const InputIterators &end, DupFunctor dup_func)
Definition SparseMatrix.h:1383
SparseMatrix(const SparseSelfAdjointView< OtherDerived, UpLo > &other)
Definition SparseMatrix.h:808
void resize(Index rows, Index cols)
Definition SparseMatrix.h:743
void conservativeResize(Index rows, NoChange_t)
Definition SparseMatrix.h:734
Index rows() const
Definition SparseMatrix.h:160
SparseMatrix(const SparseMatrix &other)
Definition SparseMatrix.h:822
StorageIndex * innerNonZeroPtr()
Definition SparseMatrix.h:203
void setFromTriplets(const InputIterators &begin, const InputIterators &end)
Definition SparseMatrix.h:1336
SparseMatrix(const DiagonalBase< OtherDerived > &other)
Copy constructor with in-place evaluation.
Definition SparseMatrix.h:837
void setIdentity()
Definition SparseMatrix.h:856
Index innerSize() const
Definition SparseMatrix.h:165
SparseMatrix(Index rows, Index cols)
Definition SparseMatrix.h:784
StorageIndex * outerIndexPtr()
Definition SparseMatrix.h:194
const StorageIndex * innerNonZeroPtr() const
Definition SparseMatrix.h:199
void conservativeResize(Index rows, Index cols)
Definition SparseMatrix.h:684
void insertFromTriplets(const InputIterators &begin, const InputIterators &end, DupFunctor dup_func)
Definition SparseMatrix.h:1447
void prune(const Scalar &reference, const RealScalar &epsilon=NumTraits< RealScalar >::dummy_precision())
Definition SparseMatrix.h:636
const StorageIndex * outerIndexPtr() const
Definition SparseMatrix.h:190
SparseMatrix(const SparseMatrixBase< OtherDerived > &other)
Definition SparseMatrix.h:790
friend void swap(SparseMatrix &a, SparseMatrix &b)
Definition SparseMatrix.h:852
~SparseMatrix()
Definition SparseMatrix.h:943
Scalar * valuePtr()
Definition SparseMatrix.h:176
void prune(const KeepFunc &keep=KeepFunc())
Definition SparseMatrix.h:649
void conservativeResize(NoChange_t, Index cols)
Definition SparseMatrix.h:731
Scalar sum() const
Definition SparseRedux.h:31
void reserve(Index reserveSize)
Definition SparseMatrix.h:317
Scalar & coeffRef(Index row, Index col)
Definition SparseMatrix.h:276
Scalar & insert(Index row, Index col)
Definition SparseMatrix.h:1596
void reserve(const SizesType &reserveSizes)
DiagonalReturnType diagonal()
Definition SparseMatrix.h:778
void resize(NoChange_t, Index cols)
Definition SparseMatrix.h:762
Scalar & findOrInsertCoeff(Index row, Index col, bool *inserted)
Definition SparseMatrix.h:232
void insertFromTriplets(const InputIterators &begin, const InputIterators &end)
Definition SparseMatrix.h:1430
SparseMatrix(SparseMatrix &&other)
Definition SparseMatrix.h:814
Pseudo expression to manipulate a triangular sparse matrix as a selfadjoint matrix.
Definition SparseSelfAdjointView.h:53
a sparse vector class
Definition SparseVector.h:63
constexpr unsigned int LvalueBit
Definition Constants.h:149
constexpr unsigned int RowMajorBit
Definition Constants.h:71
constexpr unsigned int CompressedAccessBit
Definition Constants.h:196
constexpr Derived & derived()
Definition EigenBase.h:50
constexpr Index size() const noexcept
Definition EigenBase.h:65