dune-istl 2.12-git
Loading...
Searching...
No Matches
experimental/matrix.hh
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file LICENSE.md in module root
2// SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception
3// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
4// vi: set et ts=4 sw=2 sts=2:
5
6#ifndef DUNE_ISTL_EXPERIMENTAL_MATRIX_HH
7#define DUNE_ISTL_EXPERIMENTAL_MATRIX_HH
8
10#include "matrixview.hh"
11
18
19#include <cassert>
20#include <memory>
21#include <span>
22#include <tuple>
23#include <type_traits>
24#include <utility>
25
26namespace Dune::Experimental
27{
42 template <class B, class P, class A = std::allocator<B>>
43 class Matrix : public MatrixView<B*, P>
44 {
45 using block_alloc_type = typename std::allocator_traits<A>::template rebind_alloc<B>;
47
48 public:
50 using typename MatrixView<B*, P>::block_type;
52 using typename MatrixView<B*, P>::size_type;
56 using allocator_type = A;
57
62 Matrix(const allocator_type &allocator = allocator_type());
63
70
77
82 Matrix(const Matrix &other);
83
88 Matrix(const Matrix &other, const allocator_type &allocator);
89
94 Matrix &operator=(const Matrix &other);
95
100 Matrix(Matrix &&other);
101
108
114 Matrix(Matrix &&other, const allocator_type &allocator);
115
116 using MatrixView<B*, P>::operator=;
117
120
123
125 void swap(Matrix &other);
126
128 friend void swap(Matrix &lhs, Matrix &rhs)
129 {
130 lhs.swap(rhs);
131 }
132
135
136 private:
137
138 // matrix view getters/setters
139 using MatrixView<B*, P>::blockIter;
140 using MatrixView<B*, P>::patternPtr;
141 using MatrixView<B*, P>::resetView;
142
143 // matrix view types
146
147 // Release owned block storage if it exists; pattern ownership is handled separately.
148 void resetData();
149
151 template<class BlockAlloc, class Alloc, class... Args>
152 static void constructBlock(BlockAlloc& block_alloc, block_iter_type ptr, const Alloc& alloc, Args&&... args);
153
155 template<class Alloc, class ConstructFn>
156 block_iter_type allocateData(const Alloc& alloc, size_type count, ConstructFn&& construct_fn);
157
160 template<class Alloc, class ConstructFn, class RestoreFn>
161 block_iter_type allocateData(const Alloc& alloc, size_type count, ConstructFn&& construct_fn, RestoreFn&& restore_fn);
162
164 void allocateDataDefault();
165
167 void allocateDataCopy(block_const_iter_type other_data);
168
170 void allocateDataMove(block_iter_type other_data);
171
173 void deallocateData();
174
175 // storage of index sets, shared ownership with this class
177
178 // The allocator used for memory management
180 };
181
182 namespace Impl
183 {
184 // Lightweight replacement of std::uses_allocator_construction_args without overloads to std::pair
185 // This is needed to support libc++ versions prior to 16.
186 template<class T, class Alloc, class... Args>
187 constexpr auto usesAllocatorConstructionArgs(const Alloc& alloc, Args&&... args)
188 {
189#if defined(_LIBCPP_VERSION) && _LIBCPP_VERSION <= 16000
190 if constexpr (!std::uses_allocator_v<std::remove_cv_t<T>, Alloc> && std::is_constructible_v<T, Args...>) {
191 return std::forward_as_tuple(std::forward<Args>(args)...);
192 } else if constexpr (std::uses_allocator_v<std::remove_cv_t<T>, Alloc>
193 && std::is_constructible_v<T, std::allocator_arg_t, const Alloc&, Args...>) {
194 return std::tuple<std::allocator_arg_t, const Alloc&, Args&&...>(
195 std::allocator_arg, alloc, std::forward<Args>(args)...);
196 } else if constexpr (std::uses_allocator_v<std::remove_cv_t<T>, Alloc>
197 && std::is_constructible_v<T, Args..., const Alloc&>) {
198 return std::forward_as_tuple(std::forward<Args>(args)..., alloc);
199 } else {
200 static_assert(Dune::AlwaysFalse<T>::value,
201 "Type is not allocator-constructible for the provided allocator and arguments");
202 }
203# else
204 return std::uses_allocator_construction_args<T>(alloc, std::forward<Args>(args)...);
205#endif
206 }
207 } // namespace Impl
208
209 // Matrix implementation
210
211 template <class B, class P, class A>
212 void Matrix<B, P, A>::resetData()
213 {
214 if (blockIter())
215 deallocateData();
216 }
217
218 template <class B, class P, class A>
219 template<class BlockAlloc, class Alloc, class... Args>
220 void Matrix<B, P, A>::constructBlock(BlockAlloc& block_alloc, block_iter_type ptr, const Alloc& alloc, Args&&... args)
221 {
222 std::apply([&]<class... Xs>(Xs&&... xs) {
223 block_alloc_traits::construct(block_alloc, ptr, std::forward<Xs>(xs)...);
224 }, Impl::usesAllocatorConstructionArgs<block_type>(alloc, std::forward<Args>(args)...));
225 }
226
227 template <class B, class P, class A>
228 template<class Alloc, class ConstructFn>
229 auto Matrix<B, P, A>::allocateData(const Alloc& alloc, size_type count, ConstructFn&& construct_fn) -> block_iter_type
230 {
231 return allocateData(alloc, count, std::forward<ConstructFn>(construct_fn), [](size_type, block_type&) {});
232 }
233
234 template <class B, class P, class A>
235 template<class Alloc, class ConstructFn, class RestoreFn>
236 auto Matrix<B, P, A>::allocateData(const Alloc& alloc, size_type count, ConstructFn&& construct_fn, RestoreFn&& restore_fn) -> block_iter_type
237 {
238 // Strong exception guarantee: either fully constructed storage is returned
239 // or all partially constructed elements are rolled back and deallocated.
240 block_alloc_type block_alloc(alloc);
241 block_iter_type data = block_alloc_traits::allocate(block_alloc, count);
242 size_type constructed = 0;
243 try {
244 for (; constructed < count; ++constructed)
245 construct_fn(block_alloc, data + constructed, constructed);
246 } catch (...) {
247 if constexpr (not std::is_trivially_destructible_v<block_type>)
248 for (size_type i = 0; i < constructed; ++i) {
249 restore_fn(i, data[i]);
250 block_alloc_traits::destroy(block_alloc, std::addressof(data[i]));
251 }
252 block_alloc_traits::deallocate(block_alloc, data, count);
253 throw;
254 }
255 return data;
256 }
257
258 template <class B, class P, class A>
260 {
261 assert(not blockIter());
262 assert(patternPtr());
263 assert(patternPtr() == pattern_.get());
264 const size_type count = pattern_->count();
265 block_iter_type data_ptr = allocateData(alloc_, count, [this](auto& block_alloc, block_iter_type ptr, size_type) {
266 constructBlock(block_alloc, ptr, alloc_);
267 });
268 resetView(data_ptr, pattern_.get());
269 }
270
271 template <class B, class P, class A>
272 void Matrix<B, P, A>::allocateDataCopy(block_const_iter_type other_data)
273 {
274 assert(not blockIter());
275 assert(patternPtr());
276 assert(patternPtr() == pattern_.get());
277 const size_type count = pattern_->count();
278 block_iter_type data_ptr = allocateData(alloc_, count, [this, other_data](auto& block_alloc, block_iter_type ptr, size_type i) {
279 constructBlock(block_alloc, ptr, alloc_, other_data[i]);
280 });
281 resetView(data_ptr, pattern_.get());
282 }
283
284 template <class B, class P, class A>
285 void Matrix<B, P, A>::allocateDataMove(block_iter_type other_data)
286 {
287 assert(not blockIter());
288 assert(patternPtr());
289 assert(patternPtr() == pattern_.get());
290 const size_type count = pattern_->count();
291 block_iter_type data_ptr = allocateData(
292 alloc_,
293 count,
294 [this, other_data](auto& block_alloc, block_iter_type ptr, size_type i) {
295 constructBlock(block_alloc, ptr, alloc_, std::move(other_data[i]));
296 },
297 [other_data](size_type i, block_type& block) {
298 other_data[i] = std::move(block);
299 });
300 resetView(data_ptr, pattern_.get());
301 }
302
303 template <class B, class P, class A>
304 void Matrix<B, P, A>::deallocateData()
305 {
306 assert(blockIter());
307 assert(patternPtr());
308 assert(patternPtr() == pattern_.get());
309 block_alloc_type block_alloc(alloc_);
310 if constexpr (not std::is_trivially_destructible_v<block_type>)
311 for (auto& block : std::span(blockIter(), pattern_->count()))
312 block_alloc_traits::destroy(block_alloc, std::addressof(block));
313 block_alloc_traits::deallocate(block_alloc, blockIter(), pattern_->count());
314 resetView(nullptr, pattern_.get());
315 }
316
317 template <class B, class P, class A>
319 : Matrix(nullptr, allocator)
320 {
321 }
322
323 template <class B, class P, class A>
325 : MatrixView<B*, P>::MatrixView{nullptr, pattern.get()}
326 , pattern_(std::move(pattern))
327 , alloc_(allocator)
328 {
329 if (patternPtr())
330 allocateDataDefault();
331 }
332
333 template <class B, class P, class A>
334 Matrix<B, P, A>::Matrix(pattern_type &&pattern, const allocator_type &allocator)
335 : Matrix(nullptr, allocator)
336 {
337 using pattern_alloc_type = typename std::allocator_traits<allocator_type>::template rebind_alloc<P>;
338 pattern_alloc_type pattern_alloc(alloc_);
339 pattern_ = std::allocate_shared<const pattern_type>(pattern_alloc, std::move(pattern));
340 resetView(nullptr, pattern_.get());
341 if (patternPtr())
342 allocateDataDefault();
343 }
344
345 template <class B, class P, class A>
347 {
348 resetData();
349 resetView(nullptr, nullptr);
350 }
351
352 template <class B, class P, class A>
354 : Matrix(other, std::allocator_traits<allocator_type>::select_on_container_copy_construction(other.alloc_))
355 {}
356
357 template <class B, class P, class A>
358 Matrix<B, P, A>::Matrix(const Matrix &other, const allocator_type &allocator)
359 : Matrix(other.pattern_, allocator)
360 {
361 resetData();
362 if (patternPtr())
363 allocateDataCopy(other.blockIter());
364 }
365
366 template <class B, class P, class A>
368 {
369 return alloc_;
370 }
371
372 template <class B, class P, class A>
374 {
377 if (alloc_ != other.alloc_)
378 DUNE_THROW(InvalidStateException, "Cannot swap Matrix instances with unequal allocators when propagate_on_container_swap is false");
379
380 using std::swap;
381 swap(pattern_, other.pattern_);
382 pattern_type const * tmp_pattern_ptr = patternPtr();
383 block_iter_type tmp_data_iter = blockIter();
384 resetView(other.blockIter(), other.patternPtr());
385 other.resetView(tmp_data_iter, tmp_pattern_ptr);
387 swap(alloc_, other.alloc_);
388 }
389
390 template <class B, class P, class A>
392 {
393 if (this != &other) {
394 allocator_type target_alloc = alloc_;
396 target_alloc = other.alloc_;
397
398 auto new_pattern = other.pattern_;
399 auto* new_pattern_view = new_pattern.get();
400 block_iter_type new_block_ptr = nullptr;
401
402 if (new_pattern_view)
403 new_block_ptr = allocateData(target_alloc, new_pattern_view->count(), [&](auto& block_alloc, block_iter_type ptr, size_type i) {
404 constructBlock(block_alloc, ptr, target_alloc, other.blockIter()[i]);
405 });
406
407 resetData();
408
410 alloc_ = other.alloc_;
411
412 pattern_ = std::move(new_pattern);
413 resetView(new_block_ptr, new_pattern_view);
414 }
415 return *this;
416 }
417
418 template <class B, class P, class A>
420 : MatrixView<B*, P>(nullptr, nullptr)
421 {
422 alloc_ = std::move(other.alloc_);
423 pattern_ = std::move(other.pattern_);
424 resetView(other.blockIter(), other.patternPtr());
425 other.resetView(nullptr, nullptr);
426 }
427
428 template <class B, class P, class A>
430 : MatrixView<B*, P>(nullptr, nullptr)
431 , alloc_(allocator)
432 {
433 // If allocators compare equal we can steal storage directly, otherwise we
434 // preserve source validity by moving elements into newly allocated storage.
435 if (alloc_ == other.alloc_) {
436 pattern_ = std::move(other.pattern_);
437 resetView(other.blockIter(), other.patternPtr());
438 other.resetView(nullptr, nullptr);
439 } else {
440 pattern_ = other.pattern_;
441 resetView(nullptr, pattern_.get());
442 if (other.blockIter())
443 allocateDataMove(other.blockIter());
444 }
445 }
446
447 template <class B, class P, class A>
449 {
450 if (this != &other)
451 {
452 // Fast path: allocator propagation allows full ownership transfer.
454 resetData();
455 alloc_ = std::move(other.alloc_);
456 pattern_ = std::move(other.pattern_);
457 resetView(other.blockIter(), other.patternPtr());
458 other.resetView(nullptr, nullptr);
459 // Also fast when allocators match even without propagation.
460 } else if (alloc_ == other.alloc_) {
461 resetData();
462 pattern_ = std::move(other.pattern_);
463 resetView(other.blockIter(), other.patternPtr());
464 other.resetView(nullptr, nullptr);
465 } else {
466 // Conservative path: rebuild data with this allocator, moving entries
467 // one by one and restoring the source on construction failure.
468 auto new_pattern = other.pattern_;
469 auto* new_pattern_view = new_pattern.get();
470 block_iter_type new_block_ptr = nullptr;
471
472 if (new_pattern_view && other.blockIter())
473 new_block_ptr = allocateData(
474 alloc_,
475 new_pattern_view->count(),
476 [&](auto& block_alloc, block_iter_type ptr, size_type i) {
477 constructBlock(block_alloc, ptr, alloc_, std::move(other.blockIter()[i]));
478 },
479 [&](size_type i, block_type& block) {
480 other.blockIter()[i] = std::move(block);
481 });
482
483 resetData();
484
485 pattern_ = std::move(new_pattern);
486 resetView(new_block_ptr, new_pattern_view);
487 }
488 }
489 return *this;
490 }
491} // namespace Dune::Experimental
492
493namespace Dune {
494
495template <class B, class P, class A>
496struct FieldTraits<Experimental::Matrix<B, P, A>>
497 : public FieldTraits<typename Experimental::Matrix<B, P, A>::field_type>
498{
499};
500
501} // namespace Dune
502
503#endif // DUNE_ISTL_EXPERIMENTAL_MATRIX_HH
#define DUNE_NO_UNIQUE_ADDRESS
STL namespace.
Definition experimental/bcrsmatrix.hh:21
void destroy(pointer p)
#define DUNE_THROW(E,...)
void swap(T &v1, T &v2, bool mask)
constexpr auto get(std::integer_sequence< T, II... >, std::integral_constant< std::size_t, pos >={})
span(T(&)[N]) -> span< T, N >
Owning matrix container built on top of MatrixView.
Definition experimental/matrix.hh:44
~Matrix()
Destructor releases owned block storage.
Definition experimental/matrix.hh:346
Matrix(pattern_type &&pattern, const allocator_type &allocator=allocator_type())
Construct from an rvalue pattern.
Definition experimental/matrix.hh:334
A allocator_type
Allocator type used by this container.
Definition experimental/matrix.hh:56
Matrix(const allocator_type &allocator=allocator_type())
Construct an empty matrix with allocator allocator.
Definition experimental/matrix.hh:318
std::shared_ptr< const pattern_type > patternStorage() const
Get shared storage of the pattern.
Definition experimental/matrix.hh:134
friend void swap(Matrix &lhs, Matrix &rhs)
Swap contents of lhs and rhs, honoring allocator swap propagation traits.
Definition experimental/matrix.hh:128
Matrix(const Matrix &other, const allocator_type &allocator)
Copy constructor.
Definition experimental/matrix.hh:358
Matrix & operator=(const Matrix &other)
Copy assignment.
Definition experimental/matrix.hh:391
Matrix(const Matrix &other)
Copy constructor.
Definition experimental/matrix.hh:353
void swap(Matrix &other)
Swap contents of this matrix with other, honoring allocator swap propagation traits.
Definition experimental/matrix.hh:373
Matrix(std::shared_ptr< const pattern_type > pattern, const allocator_type &allocator=allocator_type())
Construct from a shared pattern.
Definition experimental/matrix.hh:324
Matrix(Matrix &&other, const allocator_type &allocator)
Move construct with explicit allocator.
Definition experimental/matrix.hh:429
Matrix(Matrix &&other)
Move constructor.
Definition experimental/matrix.hh:419
allocator_type get_allocator() const
Return the allocator currently associated with this matrix.
Definition experimental/matrix.hh:367
Matrix & operator=(Matrix &&other)
Move assignment.
Definition experimental/matrix.hh:448
Non-owning matrix view over external block storage and pattern.
Definition matrixview.hh:54
P pattern_type
The type used for the sparsity pattern.
Definition matrixview.hh:66
B block_iter_type
The iterator type used to access the blocks of the matrix.
Definition matrixview.hh:57
const pattern_type & pattern() const
Definition matrixview.hh:248
void resetView(block_iter_type block_iter, pattern_type const *pattern_ptr)
Reset the view to point to block_iter and pattern_ptr.
Definition matrixview.hh:263
pattern_type const * patternPtr() const
Return the underlying pattern pointer.
Definition matrixview.hh:280
block_iter_type blockIter()
Return the underlying block iterator.
Definition matrixview.hh:270
std::ranges::range_size_t< pattern_type > size_type
The type used to count elements in the row and column index sets.
Definition matrixview.hh:74
Const block iterator adapter.
Definition matrixview.hh:324
A generic dynamic dense matrix.
Definition matrix.hh:561
Matrix()
Create empty matrix.
Definition matrix.hh:592
T addressof(T... args)
T apply(T... args)
T forward_as_tuple(T... args)
T get(T... args)
T is_constructible_v
T swap(T... args)