SimiLie
Loading...
Searching...
No Matches
tensor_prod.hpp
1// SPDX-FileCopyrightText: 2024 Baptiste Legouix
2// SPDX-License-Identifier: MIT
3
4#pragma once
5
6#include <ddc/ddc.hpp>
7
8#include <similie/misc/specialization.hpp>
9
10#include "character.hpp"
11#if defined BUILD_YOUNG_TABLEAU
12#include "young_tableau_tensor.hpp"
13#endif
14
15namespace sil {
16
17namespace tensor {
18
19namespace detail {
20
21template <class Index>
22struct SubindicesDomain;
23
24template <template <class...> class T, class... SubIndex>
25struct SubindicesDomain<T<SubIndex...>>
26{
27 using type = ddc::DiscreteDomain<SubIndex...>;
28
29 static constexpr type run()
30 {
31 return ddc::DiscreteDomain<SubIndex...>(
32 ddc::DiscreteElement<SubIndex...>(ddc::DiscreteElement<SubIndex>(0)...),
33 ddc::DiscreteVector<SubIndex...>(
34 ddc::DiscreteVector<SubIndex>(SubIndex::size())...));
35 }
36};
37
38} // namespace detail
39
40template <class T>
41using subindices_domain_t = detail::SubindicesDomain<T>::type;
42
43template <class T>
44static constexpr subindices_domain_t<T> subindices_domain()
45{
46 return detail::SubindicesDomain<T>::run();
47};
48
49// Check tensor compatibility
50namespace detail {
51
52template <class ProdDDims, class Indices1, class Indices2>
53struct CheckTensorsCompatibility;
54
55template <class... ProdDDim, class... Index1, class... Index2>
56struct CheckTensorsCompatibility<
57 ddc::detail::TypeSeq<ProdDDim...>,
58 ddc::detail::TypeSeq<Index1...>,
59 ddc::detail::TypeSeq<Index2...>>
60{
61 KOKKOS_FUNCTION static constexpr void run()
62 {
63 static_assert(std::is_same_v<
64 ddc::type_seq_remove_t<
65 uncharacterize_t<ddc::to_type_seq_t<
66 ddc::cartesian_prod_t<natural_domain_t<Index1>...>>>,
67 uncharacterize_t<ddc::to_type_seq_t<
68 ddc::cartesian_prod_t<natural_domain_t<ProdDDim>...>>>>,
69 ddc::type_seq_remove_t<
70 uncharacterize_t<ddc::to_type_seq_t<
71 ddc::cartesian_prod_t<natural_domain_t<Index2>...>>>,
72 uncharacterize_t<ddc::to_type_seq_t<
73 ddc::cartesian_prod_t<natural_domain_t<ProdDDim>...>>>>>);
74 static_assert(are_different_characters_v<
75 ddc::type_seq_remove_t<
76 ddc::to_type_seq_t<
77 ddc::cartesian_prod_t<natural_domain_t<Index1>...>>,
78 ddc::to_type_seq_t<
79 ddc::cartesian_prod_t<natural_domain_t<ProdDDim>...>>>,
80 ddc::type_seq_remove_t<
81 ddc::to_type_seq_t<
82 ddc::cartesian_prod_t<natural_domain_t<Index2>...>>,
83 ddc::to_type_seq_t<
84 ddc::cartesian_prod_t<natural_domain_t<ProdDDim>...>>>>);
85 }
86};
87
88} // namespace detail
89
90template <class ProdDDims, class Indices1, class Indices2>
92{
93 return detail::CheckTensorsCompatibility<ProdDDims, Indices1, Indices2>::run();
94}
95
96// Any-any product into Any (general case not optimized)
97namespace detail {
98
99template <
100 class ProdDDims,
101 class Indices1,
102 class Indices2,
103 class HeadDDim1TypeSeq,
104 class ContractDDimTypeSeq,
105 class TailDDim2TypeSeq>
106struct TensorProdAnyAnyAny;
107
108template <
109 class... ProdDDim,
110 class... Index1,
111 class... Index2,
112 class... HeadDDim1,
113 class... ContractDDim,
114 class... TailDDim2>
115struct TensorProdAnyAnyAny<
116 ddc::detail::TypeSeq<ProdDDim...>,
117 ddc::detail::TypeSeq<Index1...>,
118 ddc::detail::TypeSeq<Index2...>,
119 ddc::detail::TypeSeq<HeadDDim1...>,
120 ddc::detail::TypeSeq<ContractDDim...>,
121 ddc::detail::TypeSeq<TailDDim2...>>
122{
123 template <class ElementType, class LayoutStridedPolicy, class MemorySpace>
124 KOKKOS_FUNCTION static Tensor<
125 ElementType,
126 ddc::DiscreteDomain<ProdDDim...>,
127 LayoutStridedPolicy,
128 MemorySpace>
129 run(Tensor<ElementType, ddc::DiscreteDomain<ProdDDim...>, LayoutStridedPolicy, MemorySpace>
130 prod_tensor,
131 Tensor<ElementType, ddc::DiscreteDomain<Index1...>, LayoutStridedPolicy, MemorySpace>
132 tensor1,
133 Tensor<ElementType, ddc::DiscreteDomain<Index2...>, LayoutStridedPolicy, MemorySpace>
134 tensor2)
135 {
136 tensor::TensorAccessor<ContractDDim...> contract_accessor;
137 ddc::DiscreteDomain<ContractDDim...> contract_dom = contract_accessor.natural_domain();
138
139 ddc::device_for_each(prod_tensor.domain(), [&](ddc::DiscreteElement<ProdDDim...> mem_elem) {
140 auto elem = prod_tensor.canonical_natural_element(mem_elem);
141 prod_tensor.mem(mem_elem) = ddc::device_transform_reduce(
142 contract_dom,
143 0.,
144 ddc::reducer::sum<ElementType>(),
145 [&](ddc::DiscreteElement<ContractDDim...> contract_elem) {
146 return tensor1.get(tensor1.access_element(
147 ddc::DiscreteElement<HeadDDim1..., ContractDDim...>(
148 ddc::select<HeadDDim1...>(elem),
149 contract_accessor.access_element(contract_elem))))
150 * tensor2.get(tensor2.access_element(
151 ddc::DiscreteElement<ContractDDim..., TailDDim2...>(
152 contract_elem,
153 ddc::select<TailDDim2...>(elem))));
154 });
155 });
156 return prod_tensor;
157 }
158};
159
160} // namespace detail
161
162template <
163 class... ProdDDim,
164 class... Index1,
165 class... Index2,
166 class ElementType,
167 class LayoutStridedPolicy,
168 class MemorySpace>
169 requires(
170 ((!TensorNatIndex<ProdDDim> || ...) || (!TensorNatIndex<Index2> || ...)
171 || (!TensorNatIndex<Index1> || ...))
172#if defined BUILD_YOUNG_TABLEAU
173 && (!misc::Specialization<ProdDDim, TensorYoungTableauIndex> && ...)
174 && (!misc::Specialization<Index1, TensorYoungTableauIndex> && ...)
175 && (!misc::Specialization<Index2, TensorYoungTableauIndex> && ...)
176#endif
177 )
178Tensor<ElementType, ddc::DiscreteDomain<ProdDDim...>, LayoutStridedPolicy, MemorySpace>
179 KOKKOS_FUNCTION tensor_prod(
180 Tensor<ElementType,
181 ddc::DiscreteDomain<ProdDDim...>,
182 LayoutStridedPolicy,
183 MemorySpace> prod_tensor,
184 Tensor<ElementType,
185 ddc::DiscreteDomain<Index1...>,
186 LayoutStridedPolicy,
187 MemorySpace> tensor1,
188 Tensor<ElementType,
189 ddc::DiscreteDomain<Index2...>,
190 LayoutStridedPolicy,
191 MemorySpace> tensor2)
192{
194 ddc::detail::TypeSeq<ProdDDim...>,
195 ddc::detail::TypeSeq<Index1...>,
196 ddc::detail::TypeSeq<Index2...>>();
197
198 detail::TensorProdAnyAnyAny<
199 uncharacterize_t<ddc::detail::TypeSeq<ProdDDim...>>,
200 uncharacterize_t<ddc::detail::TypeSeq<Index1...>>,
201 uncharacterize_t<ddc::detail::TypeSeq<Index2...>>,
202 ddc::type_seq_remove_t<
203 uncharacterize_t<ddc::to_type_seq_t<
204 ddc::cartesian_prod_t<natural_domain_t<ProdDDim>...>>>,
205 uncharacterize_t<ddc::to_type_seq_t<
206 ddc::cartesian_prod_t<natural_domain_t<Index2>...>>>>,
207 ddc::type_seq_remove_t<
209 ddc::to_type_seq_t<ddc::cartesian_prod_t<natural_domain_t<Index1>...>>>,
210 uncharacterize_t<ddc::to_type_seq_t<
211 ddc::cartesian_prod_t<natural_domain_t<ProdDDim>...>>>>,
212 ddc::type_seq_remove_t<
213 uncharacterize_t<ddc::to_type_seq_t<
214 ddc::cartesian_prod_t<natural_domain_t<ProdDDim>...>>>,
215 uncharacterize_t<ddc::to_type_seq_t<
216 ddc::cartesian_prod_t<natural_domain_t<Index1>...>>>>>::
217 run(uncharacterize_tensor(prod_tensor),
218 uncharacterize_tensor(tensor1),
219 uncharacterize_tensor(tensor2));
220 return prod_tensor;
221}
222
223#if defined BUILD_YOUNG_TABLEAU
224// Young-dense product
225namespace detail {
226
227template <class Index1, class HeadDDim1TypeSeq, class ContractDDimTypeSeq, class TailDDim2TypeSeq>
228struct TensorProdNatYoungNat;
229
230template <class Index1, class... HeadDDim1, class... ContractDDim, class... TailDDim2>
231struct TensorProdNatYoungNat<
232 Index1,
233 ddc::detail::TypeSeq<HeadDDim1...>,
234 ddc::detail::TypeSeq<ContractDDim...>,
235 ddc::detail::TypeSeq<TailDDim2...>>
236{
237 template <class ElementType, class LayoutStridedPolicy, class MemorySpace>
238 static Tensor<
239 ElementType,
240 ddc::DiscreteDomain<HeadDDim1..., TailDDim2...>,
241 LayoutStridedPolicy,
242 MemorySpace>
243 run(Tensor<ElementType,
244 ddc::DiscreteDomain<HeadDDim1..., TailDDim2...>,
245 Kokkos::layout_right,
246 Kokkos::DefaultHostExecutionSpace::memory_space> prod_tensor,
247 Tensor<ElementType, ddc::DiscreteDomain<Index1>, LayoutStridedPolicy, MemorySpace> tensor1,
248 Tensor<ElementType,
249 ddc::DiscreteDomain<ContractDDim..., TailDDim2...>,
250 LayoutStridedPolicy,
251 MemorySpace> tensor2)
252 {
253 /*
254 typename TensorYoungTableauIndex<DDim1...>::young_tableau young_tableau;
255 csr::Csr u = young_tableau.template u<YoungTableauIndex, DDim2...>(tensor2.domain());
256*/
257 ddc::Chunk uncompressed_tensor1_alloc(
258 Index1::subindices_domain(),
259 ddc::HostAllocator<double>());
260 tensor::Tensor uncompressed_tensor1(uncompressed_tensor1_alloc);
261
262 tensor::uncompress(uncompressed_tensor1, tensor1);
263
264 return tensor_prod(prod_tensor, uncompressed_tensor1, tensor2);
265 }
266};
267
268} // namespace detail
269
270template <
271 TensorNatIndex... ProdDDim,
272 misc::Specialization<TensorYoungTableauIndex> Index1,
273 TensorNatIndex... DDim2,
274 class ElementType,
275 class LayoutStridedPolicy,
276 class MemorySpace>
277Tensor<ElementType,
278 ddc::DiscreteDomain<ProdDDim...>,
279 Kokkos::layout_right,
280 Kokkos::DefaultHostExecutionSpace::memory_space>
282 Tensor<ElementType,
283 ddc::DiscreteDomain<ProdDDim...>,
284 Kokkos::layout_right,
285 Kokkos::DefaultHostExecutionSpace::memory_space> prod_tensor,
286 Tensor<ElementType, ddc::DiscreteDomain<Index1>, LayoutStridedPolicy, MemorySpace> tensor1,
287 Tensor<ElementType, ddc::DiscreteDomain<DDim2...>, LayoutStridedPolicy, MemorySpace>
288 tensor2)
289{
290 /*
291 check_tensors_compatibility<
292 ddc::detail::TypeSeq<ProdDDim...>,
293 ddc::detail::TypeSeq<Index1>,
294 ddc::detail::TypeSeq<DDim2...>>();
295 */
296 // TODO DDim2 -> Index2, characterize
297 return detail::TensorProdNatYoungNat<
298 Index1,
299 ddc::type_seq_remove_t<
300 ddc::detail::TypeSeq<ProdDDim...>,
301 ddc::detail::TypeSeq<DDim2...>>,
302 ddc::type_seq_remove_t<
303 ddc::to_type_seq_t<typename Index1::subindices_domain_t>,
304 ddc::detail::TypeSeq<ProdDDim...>>,
305 ddc::type_seq_remove_t<
306 ddc::detail::TypeSeq<ProdDDim...>,
307 ddc::to_type_seq_t<typename Index1::subindices_domain_t>>>::
308 run(prod_tensor, tensor1, tensor2);
309}
310
311// Young-young product
312namespace detail {
313
314template <
315 class Index1,
316 class Index2,
317 class HeadDDim1TypeSeq,
318 class ContractDDimTypeSeq,
319 class TailDDim2TypeSeq>
320struct TensorProdNatYoungYoung;
321
322template <class Index1, class Index2, class... HeadDDim1, class... ContractDDim, class... TailDDim2>
323struct TensorProdNatYoungYoung<
324 Index1,
325 Index2,
326 ddc::detail::TypeSeq<HeadDDim1...>,
327 ddc::detail::TypeSeq<ContractDDim...>,
328 ddc::detail::TypeSeq<TailDDim2...>>
329{
330 template <class ElementType, class LayoutStridedPolicy, class MemorySpace>
331 static Tensor<
332 ElementType,
333 ddc::DiscreteDomain<HeadDDim1..., TailDDim2...>,
334 LayoutStridedPolicy,
335 MemorySpace>
336 run(Tensor<ElementType,
337 ddc::DiscreteDomain<HeadDDim1..., TailDDim2...>,
338 Kokkos::layout_right,
339 Kokkos::DefaultHostExecutionSpace::memory_space> prod_tensor,
340 Tensor<ElementType, ddc::DiscreteDomain<Index1>, LayoutStridedPolicy, MemorySpace> tensor1,
341 Tensor<ElementType, ddc::DiscreteDomain<Index2>, LayoutStridedPolicy, MemorySpace> tensor2)
342 {
343 /*
344 typename TensorYoungTableauIndex<DDim1...>::young_tableau young_tableau;
345 csr::Csr u = young_tableau.template u<YoungTableauIndex, DDim2...>(tensor2.domain());
346*/
347 ddc::Chunk uncompressed_tensor1_alloc(
348 Index1::subindices_domain(),
349 ddc::HostAllocator<double>());
350 tensor::Tensor uncompressed_tensor1(uncompressed_tensor1_alloc);
351
352 ddc::Chunk uncompressed_tensor2_alloc(
353 Index2::subindices_domain(),
354 ddc::HostAllocator<double>());
355 tensor::Tensor uncompressed_tensor2(uncompressed_tensor2_alloc);
356
357 tensor::uncompress(uncompressed_tensor1, tensor1);
358 tensor::uncompress(uncompressed_tensor2, tensor2);
359
360 return tensor_prod(prod_tensor, uncompressed_tensor1, uncompressed_tensor2);
361 }
362};
363
364} // namespace detail
365
366template <
367 TensorNatIndex... ProdDDim,
368 misc::Specialization<TensorYoungTableauIndex> Index1,
369 misc::Specialization<TensorYoungTableauIndex> Index2,
370 class ElementType,
371 class LayoutStridedPolicy,
372 class MemorySpace>
373Tensor<ElementType,
374 ddc::DiscreteDomain<ProdDDim...>,
375 Kokkos::layout_right,
376 Kokkos::DefaultHostExecutionSpace::memory_space>
378 Tensor<ElementType,
379 ddc::DiscreteDomain<ProdDDim...>,
380 Kokkos::layout_right,
381 Kokkos::DefaultHostExecutionSpace::memory_space> prod_tensor,
382 Tensor<ElementType, ddc::DiscreteDomain<Index1>, LayoutStridedPolicy, MemorySpace> tensor1,
383 Tensor<ElementType, ddc::DiscreteDomain<Index2>, LayoutStridedPolicy, MemorySpace> tensor2)
384{
386 ddc::detail::TypeSeq<ProdDDim...>,
387 ddc::detail::TypeSeq<Index1>,
388 ddc::detail::TypeSeq<Index2>>();
389
390 detail::TensorProdNatYoungYoung<
391 uncharacterize_t<Index1>,
392 uncharacterize_t<Index2>,
393 ddc::type_seq_remove_t<
394 uncharacterize_t<ddc::to_type_seq_t<
395 ddc::cartesian_prod_t<natural_domain_t<ProdDDim>...>>>,
397 ddc::to_type_seq_t<ddc::cartesian_prod_t<natural_domain_t<Index2>>>>>,
398 ddc::type_seq_remove_t<
400 ddc::to_type_seq_t<ddc::cartesian_prod_t<natural_domain_t<Index1>>>>,
401 uncharacterize_t<ddc::to_type_seq_t<
402 ddc::cartesian_prod_t<natural_domain_t<ProdDDim>...>>>>,
403 ddc::type_seq_remove_t<
404 uncharacterize_t<ddc::to_type_seq_t<
405 ddc::cartesian_prod_t<natural_domain_t<ProdDDim>...>>>,
407 ddc::to_type_seq_t<ddc::cartesian_prod_t<natural_domain_t<Index1>>>>>>::
408 run(uncharacterize_tensor(prod_tensor),
409 uncharacterize_tensor(tensor1),
410 uncharacterize_tensor(tensor2));
411
412 return prod_tensor;
413}
414
415// Any-any product into Young
416namespace detail {
417
418template <
419 class ProdDDims,
420 class Indices1,
421 class Indices2,
422 class HeadDDim1TypeSeq,
423 class ContractDDimTypeSeq,
424 class TailDDim2TypeSeq>
425struct TensorProdYoungAnyAny;
426
427template <
428 class... ProdDDim,
429 class... Index1,
430 class... Index2,
431 class... HeadDDim1,
432 class... ContractDDim,
433 class... TailDDim2>
434struct TensorProdYoungAnyAny<
435 ddc::detail::TypeSeq<ProdDDim...>,
436 ddc::detail::TypeSeq<Index1...>,
437 ddc::detail::TypeSeq<Index2...>,
438 ddc::detail::TypeSeq<HeadDDim1...>,
439 ddc::detail::TypeSeq<ContractDDim...>,
440 ddc::detail::TypeSeq<TailDDim2...>>
441{
442 template <class ElementType, class LayoutStridedPolicy, class MemorySpace>
443 static Tensor<ElementType, ddc::DiscreteDomain<ProdDDim...>, LayoutStridedPolicy, MemorySpace>
444 run(Tensor<ElementType,
445 ddc::DiscreteDomain<ProdDDim...>,
446 Kokkos::layout_right,
447 Kokkos::DefaultHostExecutionSpace::memory_space> prod_tensor,
448 Tensor<ElementType, ddc::DiscreteDomain<Index1...>, LayoutStridedPolicy, MemorySpace>
449 tensor1,
450 Tensor<ElementType, ddc::DiscreteDomain<Index2...>, LayoutStridedPolicy, MemorySpace>
451 tensor2)
452 {
453 /*
454 typename TensorYoungTableauIndex<DDim1...>::young_tableau young_tableau;
455 csr::Csr u = young_tableau.template u<YoungTableauIndex, DDim2...>(tensor2.domain());
456*/
457 ddc::Chunk uncompressed_prod_alloc(
458 ddc::DiscreteDomain(ProdDDim::subindices_domain()...),
459 ddc::HostAllocator<double>());
460 tensor::Tensor uncompressed_prod(uncompressed_prod_alloc);
461
462 tensor::TensorAccessor<ContractDDim...> contract_accessor;
463 ddc::DiscreteDomain<ContractDDim...> contract_dom = contract_accessor.natural_domain();
464
465 ddc::for_each(
466 uncompressed_prod.domain(),
467 [&](ddc::cartesian_prod_t<
468 typename ProdDDim::subindices_domain_t...>::discrete_element_type elem) {
469 uncompressed_prod(elem) = ddc::transform_reduce(
470 contract_dom,
471 0.,
472 ddc::reducer::sum<ElementType>(),
473 [&](ddc::DiscreteElement<ContractDDim...> contract_elem) {
474 return tensor1.get(tensor1.access_element(
475 ddc::DiscreteElement<HeadDDim1..., ContractDDim...>(
476 ddc::select<HeadDDim1...>(elem),
477 contract_accessor.access_element(
478 contract_elem))))
479 * tensor2.get(tensor2.access_element(
480 ddc::DiscreteElement<ContractDDim..., TailDDim2...>(
481 contract_elem,
482 ddc::select<TailDDim2...>(elem))));
483 });
484 });
485 tensor::compress(prod_tensor, uncompressed_prod);
486 return prod_tensor;
487 }
488};
489
490} // namespace detail
491
492template <
493 misc::Specialization<TensorYoungTableauIndex> ProdDDim,
494 class... Index1,
495 class... Index2,
496 class ElementType,
497 class LayoutStridedPolicy,
498 class MemorySpace>
499Tensor<ElementType,
500 ddc::DiscreteDomain<ProdDDim>,
501 Kokkos::layout_right,
502 Kokkos::DefaultHostExecutionSpace::memory_space>
504 Tensor<ElementType,
505 ddc::DiscreteDomain<ProdDDim>,
506 Kokkos::layout_right,
507 Kokkos::DefaultHostExecutionSpace::memory_space> prod_tensor,
508 Tensor<ElementType, ddc::DiscreteDomain<Index1...>, LayoutStridedPolicy, MemorySpace>
509 tensor1,
510 Tensor<ElementType, ddc::DiscreteDomain<Index2...>, LayoutStridedPolicy, MemorySpace>
511 tensor2)
512{
514 ddc::detail::TypeSeq<ProdDDim>,
515 ddc::detail::TypeSeq<Index1...>,
516 ddc::detail::TypeSeq<Index2...>>();
517
518 detail::TensorProdYoungAnyAny<
519 uncharacterize_t<ddc::detail::TypeSeq<ProdDDim>>,
520 uncharacterize_t<ddc::detail::TypeSeq<Index1...>>,
521 uncharacterize_t<ddc::detail::TypeSeq<Index2...>>,
522 ddc::type_seq_remove_t<
524 ddc::to_type_seq_t<ddc::cartesian_prod_t<natural_domain_t<ProdDDim>>>>,
525 uncharacterize_t<ddc::to_type_seq_t<
526 ddc::cartesian_prod_t<natural_domain_t<Index2>...>>>>,
527 ddc::type_seq_remove_t<
529 ddc::to_type_seq_t<ddc::cartesian_prod_t<natural_domain_t<Index1>...>>>,
531 ddc::to_type_seq_t<ddc::cartesian_prod_t<natural_domain_t<ProdDDim>>>>>,
532 ddc::type_seq_remove_t<
534 ddc::to_type_seq_t<ddc::cartesian_prod_t<natural_domain_t<ProdDDim>>>>,
535 uncharacterize_t<ddc::to_type_seq_t<
536 ddc::cartesian_prod_t<natural_domain_t<Index1>...>>>>>::
537 run(uncharacterize_tensor(prod_tensor),
538 uncharacterize_tensor(tensor1),
539 uncharacterize_tensor(tensor2));
540 return prod_tensor;
541}
542#endif
543
544} // namespace tensor
545
546} // namespace sil
static constexpr natural_domain_t natural_domain()
sil::tensor::Tensor< double, ddc::DiscreteDomain< TailTensorIndex... >, Kokkos::layout_right, Kokkos::DefaultHostExecutionSpace::memory_space > tensor_prod(sil::tensor::Tensor< double, ddc::DiscreteDomain< TailTensorIndex... >, Kokkos::layout_right, Kokkos::DefaultHostExecutionSpace::memory_space > prod, sil::tensor::Tensor< double, ddc::DiscreteDomain< HeadTensorIndex >, Kokkos::layout_right, Kokkos::DefaultHostExecutionSpace::memory_space > dense, Csr< N, HeadTensorIndex, TailTensorIndex... > csr)
Definition csr.hpp:107
constexpr void check_tensors_compatibility()
Tensor(ddc::Chunk< ElementType, SupportType, Allocator >) -> Tensor< ElementType, SupportType, Kokkos::layout_right, typename Allocator::memory_space >
constexpr uncharacterize_tensor_t< TensorType > uncharacterize_tensor(TensorType tensor)
constexpr bool are_different_characters_v
detail::SubindicesDomain< T >::type subindices_domain_t
detail::Uncharacterize< Index >::type uncharacterize_t
The top-level namespace of SimiLie.
Definition csr.hpp:14