SimiLie
Loading...
Searching...
No Matches
young_tableau.hpp
1// SPDX-FileCopyrightText: 2024 Baptiste Legouix
2// SPDX-License-Identifier: MIT
3
4#pragma once
5
6#if defined BUILD_YOUNG_TABLEAU
7
8#include <ddc/ddc.hpp>
9
10#include <similie/csr/csr.hpp>
11#include <similie/csr/csr_dynamic.hpp>
12#include <similie/misc/permutation_parity.hpp>
13#include <similie/misc/specialization.hpp>
14#include <similie/misc/stride.hpp>
15#include <similie/tensor/dummy_index.hpp>
16#include <similie/tensor/prime.hpp>
17#include <similie/tensor/tensor_impl.hpp>
18
19namespace sil {
20
21namespace young_tableau {
22
23namespace detail {
24
25template <class T, T... Args>
26constexpr T sum(std::integer_sequence<T, Args...> = {})
27{
28 return (Args + ...);
29}
30
31} // namespace detail
32
33template <class... Row>
34class YoungTableauSeq
35{
36public:
37 using shape = std::index_sequence<Row::size()...>;
38
39 static constexpr std::size_t rank = detail::sum(shape());
40};
41
42namespace detail {
43
44// Extract Row from a YoungTableauSeq at index I
45template <std::size_t I, std::size_t Id, class TableauSeq>
46struct ExtractRow;
47
48template <std::size_t I, std::size_t Id, class HeadRow, class... TailRow>
49struct ExtractRow<I, Id, YoungTableauSeq<HeadRow, TailRow...>>
50{
51 using type = std::conditional_t<
52 I == Id,
53 HeadRow,
54 typename ExtractRow<I, Id + 1, YoungTableauSeq<TailRow...>>::type>;
55};
56
57template <std::size_t I, std::size_t Id>
58struct ExtractRow<I, Id, YoungTableauSeq<>>
59{
60 using type = std::index_sequence<>;
61};
62
63template <std::size_t I, class TableauSeq>
64using extract_row_t = ExtractRow<I, 0, TableauSeq>::type;
65
66// Override the row of YoungTableauSeq at index I with RowToSet
67template <
68 std::size_t I,
69 std::size_t Id,
70 class RowToSet,
71 class HeadTableauSeq,
72 class InterestTableauSeq,
73 class TailTableauSeq>
74struct OverrideRow;
75
76template <
77 std::size_t I,
78 std::size_t Id,
79 class RowToSet,
80 class... HeadRow,
81 class InterestRow,
82 class HeadOfTailRow,
83 class... TailOfTailRow>
84struct OverrideRow<
85 I,
86 Id,
87 RowToSet,
88 YoungTableauSeq<HeadRow...>,
89 YoungTableauSeq<InterestRow>,
90 YoungTableauSeq<HeadOfTailRow, TailOfTailRow...>>
91{
92 using type = std::conditional_t<
93 I == Id,
94 YoungTableauSeq<HeadRow..., RowToSet, HeadOfTailRow, TailOfTailRow...>,
95 typename OverrideRow<
96 I,
97 Id + 1,
98 RowToSet,
99 YoungTableauSeq<HeadRow..., InterestRow>,
100 YoungTableauSeq<HeadOfTailRow>,
101 YoungTableauSeq<TailOfTailRow...>>::type>;
102};
103
104template <std::size_t I, std::size_t Id, class RowToSet, class... HeadRow, class InterestRow>
105struct OverrideRow<
106 I,
107 Id,
108 RowToSet,
109 YoungTableauSeq<HeadRow...>,
110 YoungTableauSeq<InterestRow>,
111 YoungTableauSeq<>>
112{
113 using type = YoungTableauSeq<HeadRow..., RowToSet>;
114};
115
116template <std::size_t I, class RowToSet, class TableauSeq>
117struct OverrideRowHelper;
118
119template <std::size_t I, class RowToSet, class InterestRow, class... TailRow>
120struct OverrideRowHelper<I, RowToSet, YoungTableauSeq<InterestRow, TailRow...>>
121{
122 using type = OverrideRow<
123 I,
124 0,
125 RowToSet,
126 YoungTableauSeq<>,
127 YoungTableauSeq<InterestRow>,
128 YoungTableauSeq<TailRow...>>::type;
129};
130
131template <std::size_t I, class RowToSet, class TableauSeq>
132using override_row_t = OverrideRowHelper<I, RowToSet, TableauSeq>::type;
133
134// Add cell with value Value at the end of a row
135template <class Row, std::size_t Value>
136struct AddCellToRow;
137
138template <std::size_t... Elem, std::size_t Value>
139struct AddCellToRow<std::index_sequence<Elem...>, Value>
140{
141 using type = std::index_sequence<Elem..., Value>;
142};
143
144template <class Row, std::size_t Value>
145using add_cell_to_row_t = AddCellToRow<Row, Value>::type;
146
147// Add cell with value Value at the end of the row at index I
148template <class Tableau, std::size_t I, std::size_t Value>
149struct AddCellToTableau;
150
151template <class... Row, std::size_t I, std::size_t Value>
152struct AddCellToTableau<YoungTableauSeq<Row...>, I, Value>
153{
154 using type = detail::override_row_t<
155 I,
156 add_cell_to_row_t<extract_row_t<I, YoungTableauSeq<Row...>>, Value>,
157 YoungTableauSeq<Row...>>;
158};
159
160template <class Tableau, std::size_t I, std::size_t Value>
161using add_cell_to_tableau_t = AddCellToTableau<Tableau, I, Value>::type;
162
163// Compute dual of YoungTableauSeq
164template <class TableauDual, class Tableau, std::size_t I, std::size_t J>
165struct Dual;
166
167template <
168 class TableauDual,
169 std::size_t HeadElemOfHeadRow,
170 std::size_t... TailElemOfHeadRow,
171 class... TailRow,
172 std::size_t I,
173 std::size_t J>
174struct Dual<
175 TableauDual,
176 YoungTableauSeq<std::index_sequence<HeadElemOfHeadRow, TailElemOfHeadRow...>, TailRow...>,
177 I,
178 J>
179{
180 using type = std::conditional_t<
181 sizeof...(TailRow) == 0 && sizeof...(TailElemOfHeadRow) == 0,
182 add_cell_to_tableau_t<TableauDual, J, HeadElemOfHeadRow>,
183 std::conditional_t<
184 sizeof...(TailElemOfHeadRow) == 0,
185 typename Dual<
186 add_cell_to_tableau_t<TableauDual, J, HeadElemOfHeadRow>,
187 YoungTableauSeq<TailRow...>,
188 I + 1,
189 0>::type,
190 typename Dual<
191 add_cell_to_tableau_t<TableauDual, J, HeadElemOfHeadRow>,
192 YoungTableauSeq<std::index_sequence<TailElemOfHeadRow...>, TailRow...>,
193 I,
194 J + 1>::type>>;
195};
196
197template <class TableauDual, class... TailRow, std::size_t I, std::size_t J>
198struct Dual<TableauDual, YoungTableauSeq<std::index_sequence<>, TailRow...>, I, J>
199{
200 using type = YoungTableauSeq<>;
201};
202
203template <class TableauDual, std::size_t I, std::size_t J>
204struct Dual<TableauDual, YoungTableauSeq<>, I, J>
205{
206 using type = TableauDual;
207};
208
209template <class Tableau>
210struct DualHelper;
211
212template <std::size_t... ElemOfHeadRow, class... TailRow>
213struct DualHelper<YoungTableauSeq<std::index_sequence<ElemOfHeadRow...>, TailRow...>>
214{
215 using type
216 = Dual<YoungTableauSeq<std::conditional_t<
217 ElemOfHeadRow == -1,
218 std::index_sequence<>,
219 std::index_sequence<>>...>,
220 YoungTableauSeq<std::index_sequence<ElemOfHeadRow...>, TailRow...>,
221 0,
222 0>::type;
223};
224
225template <>
226struct DualHelper<YoungTableauSeq<>>
227{
228 using type = YoungTableauSeq<>;
229};
230
231template <class Tableau>
232using dual_t = DualHelper<Tableau>::type;
233
234// Compute hooks
235template <class TableauHooks, class Tableau, class Shape, std::size_t I, std::size_t J>
236struct Hooks;
237
238template <
239 class TableauHooks,
240 std::size_t HeadElemOfHeadRow,
241 std::size_t... TailElemOfHeadRow,
242 class... TailRow,
243 std::size_t HeadRowSize,
244 std::size_t... TailRowSize,
245 std::size_t I,
246 std::size_t J>
247struct Hooks<
248 TableauHooks,
249 YoungTableauSeq<std::index_sequence<HeadElemOfHeadRow, TailElemOfHeadRow...>, TailRow...>,
250 std::index_sequence<HeadRowSize, TailRowSize...>,
251 I,
252 J>
253{
254 using type = std::conditional_t<
255 sizeof...(TailRow) == 0 && sizeof...(TailElemOfHeadRow) == 0,
256 add_cell_to_tableau_t<TableauHooks, I, HeadRowSize - J>,
257 std::conditional_t<
258 sizeof...(TailElemOfHeadRow) == 0,
259 typename Hooks<
260 add_cell_to_tableau_t<TableauHooks, I, HeadRowSize - J>,
261 YoungTableauSeq<TailRow...>,
262 std::index_sequence<TailRowSize...>,
263 I + 1,
264 0>::type,
265 typename Hooks<
266 add_cell_to_tableau_t<TableauHooks, I, HeadRowSize - J>,
267 YoungTableauSeq<std::index_sequence<TailElemOfHeadRow...>, TailRow...>,
268 std::index_sequence<HeadRowSize, TailRowSize...>,
269 I,
270 J + 1>::type>>;
271};
272
273template <class TableauHooks, class... TailRow, class Shape, std::size_t I, std::size_t J>
274struct Hooks<TableauHooks, YoungTableauSeq<std::index_sequence<>, TailRow...>, Shape, I, J>
275{
276 using type = YoungTableauSeq<>;
277};
278
279template <class TableauHooks, class Shape, std::size_t I, std::size_t J>
280struct Hooks<TableauHooks, YoungTableauSeq<>, Shape, I, J>
281{
282 using type = TableauHooks;
283};
284
285template <class Tableau>
286struct HooksHelper;
287
288template <class... Row>
289struct HooksHelper<YoungTableauSeq<Row...>>
290{
291 using type
292 = Hooks<YoungTableauSeq<std::conditional_t<true, std::index_sequence<>, Row>...>,
293 YoungTableauSeq<Row...>,
294 typename YoungTableauSeq<Row...>::shape,
295 0,
296 0>::type;
297};
298
299template <>
300struct HooksHelper<YoungTableauSeq<>>
301{
302 using type = YoungTableauSeq<>;
303};
304
305template <class Tableau>
306using hooks_t = HooksHelper<Tableau>::type;
307
308// Sum the partial contributions of hooks to get hook lengths (= hooks+hooks_of_dual^T-1)
309template <class TableauHookLengths, class Tableau1, class Tableau2, std::size_t I>
310struct HookLengths;
311
312template <
313 class TableauHookLengths,
314 std::size_t HeadElemOfHeadRow1,
315 std::size_t... TailElemOfHeadRow1,
316 class... TailRow1,
317 std::size_t HeadElemOfHeadRow2,
318 std::size_t... TailElemOfHeadRow2,
319 class... TailRow2,
320 std::size_t I>
321struct HookLengths<
322 TableauHookLengths,
323 YoungTableauSeq<
324 std::index_sequence<HeadElemOfHeadRow1, TailElemOfHeadRow1...>,
325 TailRow1...>,
326 YoungTableauSeq<
327 std::index_sequence<HeadElemOfHeadRow2, TailElemOfHeadRow2...>,
328 TailRow2...>,
329 I>
330{
331 using type = std::conditional_t<
332 sizeof...(TailRow1) == 0 && sizeof...(TailElemOfHeadRow1) == 0,
333 add_cell_to_tableau_t<
334 TableauHookLengths,
335 I,
336 HeadElemOfHeadRow1 + HeadElemOfHeadRow2 - 1>,
337 std::conditional_t<
338 sizeof...(TailElemOfHeadRow1) == 0,
339 typename HookLengths<
340 add_cell_to_tableau_t<
341 TableauHookLengths,
342 I,
343 HeadElemOfHeadRow1 + HeadElemOfHeadRow2 - 1>,
344 YoungTableauSeq<TailRow1...>,
345 YoungTableauSeq<TailRow2...>,
346 I + 1>::type,
347 typename HookLengths<
348 add_cell_to_tableau_t<
349 TableauHookLengths,
350 I,
351 HeadElemOfHeadRow1 + HeadElemOfHeadRow2 - 1>,
352 YoungTableauSeq<
353 std::index_sequence<TailElemOfHeadRow1...>,
354 TailRow1...>,
355 YoungTableauSeq<
356 std::index_sequence<TailElemOfHeadRow2...>,
357 TailRow2...>,
358 I>::type>>;
359};
360
361template <class TableauHookLengths, class... TailRow1, class... TailRow2, std::size_t I>
362struct HookLengths<
363 TableauHookLengths,
364 YoungTableauSeq<std::index_sequence<>, TailRow1...>,
365 YoungTableauSeq<std::index_sequence<>, TailRow2...>,
366 I>
367{
368 using type = YoungTableauSeq<>;
369};
370
371template <class TableauHookLengths, std::size_t I>
372struct HookLengths<TableauHookLengths, YoungTableauSeq<>, YoungTableauSeq<>, I>
373{
374 using type = TableauHookLengths;
375};
376
377template <class Tableau1, class Tableau2>
378struct HookLengthsHelper;
379
380template <class... Row1, class Tableau2>
381struct HookLengthsHelper<YoungTableauSeq<Row1...>, Tableau2>
382{
383 using type = HookLengths<
384 YoungTableauSeq<std::conditional_t<true, std::index_sequence<>, Row1>...>,
385 YoungTableauSeq<Row1...>,
386 Tableau2,
387 0>::type;
388};
389
390template <>
391struct HookLengthsHelper<YoungTableauSeq<>, YoungTableauSeq<>>
392{
393 using type = YoungTableauSeq<>;
394};
395
396template <class Tableau1, class Tableau2>
397using hook_lengths_t = HookLengthsHelper<Tableau1, Tableau2>::type;
398
399// Compute irreductible representations dimension
400template <std::size_t Dimension, class TableauHookLengths, std::size_t I, std::size_t J>
401struct IrrepDim;
402
403template <
404 std::size_t Dimension,
405 std::size_t HeadElemOfHeadRow,
406 std::size_t... TailElemOfHeadRow,
407 class... TailRow,
408 std::size_t I,
409 std::size_t J>
410struct IrrepDim<
411 Dimension,
412 YoungTableauSeq<std::index_sequence<HeadElemOfHeadRow, TailElemOfHeadRow...>, TailRow...>,
413 I,
414 J>
415{
416 static consteval std::size_t run(double prod)
417 {
418 prod *= Dimension + J - I;
419 prod /= HeadElemOfHeadRow;
420 if constexpr (sizeof...(TailRow) == 0 && sizeof...(TailElemOfHeadRow) == 0) {
421 return prod;
422 } else if constexpr (sizeof...(TailElemOfHeadRow) == 0) {
423 return IrrepDim<Dimension, YoungTableauSeq<TailRow...>, I + 1, 0>::run(prod);
424 } else {
425 return IrrepDim<
426 Dimension,
427 YoungTableauSeq<std::index_sequence<TailElemOfHeadRow...>, TailRow...>,
428 I,
429 J + 1>::run(prod);
430 }
431 }
432};
433
434} // namespace detail
435
439template <std::size_t Dimension, misc::Specialization<YoungTableauSeq> TableauSeq>
440class YoungTableau
441{
442public:
443 using tableau_seq = TableauSeq;
444 using shape = typename TableauSeq::shape;
445
446private:
447 static constexpr std::size_t s_d = Dimension;
448 static constexpr std::size_t s_r = TableauSeq::rank;
449
450public:
451 using dual = YoungTableau<s_d, detail::dual_t<tableau_seq>>;
452 using hook_lengths = detail::hook_lengths_t<
453 detail::hooks_t<tableau_seq>,
454 detail::dual_t<detail::hooks_t<detail::dual_t<tableau_seq>>>>;
455
456private:
457 static constexpr std::size_t s_irrep_dim = detail::IrrepDim<s_d, hook_lengths, 0, 0>::run(1);
458
459 static constexpr std::string generate_tag();
460
461 static constexpr std::string s_tag = generate_tag();
462
463 static consteval auto load_irrep();
464
465 static constexpr auto s_irrep = load_irrep();
466
467public:
468 YoungTableau();
469
470 static constexpr std::size_t dimension()
471 {
472 return s_d;
473 }
474
475 static constexpr std::size_t rank()
476 {
477 return s_r;
478 }
479
480 static constexpr std::size_t irrep_dim()
481 {
482 return s_irrep_dim;
483 }
484
485 static constexpr std::string tag()
486 {
487 return s_tag;
488 }
489
490private:
491 static constexpr std::size_t n_nonzeros_in_irrep()
492 {
493 return std::get<2>(std::get<0>(s_irrep)).size();
494 }
495
496public:
497 template <tensor::TensorNatIndex... Id>
498 using projector_domain = ddc::DiscreteDomain<tensor::prime<Id>..., Id...>;
499
500 template <tensor::TensorNatIndex... Id>
501 static auto projector();
502
503 template <tensor::TensorIndex BasisId, tensor::TensorNatIndex... Id>
504 static constexpr csr::Csr<n_nonzeros_in_irrep(), BasisId, Id...> u(
505 ddc::DiscreteDomain<Id...> restricted_domain);
506
507 template <tensor::TensorIndex BasisId, tensor::TensorNatIndex... Id>
508 static constexpr csr::Csr<n_nonzeros_in_irrep(), BasisId, Id...> v(
509 ddc::DiscreteDomain<Id...> restricted_domain);
510};
511
512namespace detail {
513
514template <std::size_t Dimension, class... TailRow, std::size_t I, std::size_t J>
515struct IrrepDim<Dimension, YoungTableauSeq<std::index_sequence<>, TailRow...>, I, J>
516{
517 static consteval std::size_t run(double prod)
518 {
519 return prod;
520 }
521};
522
523template <std::size_t Dimension, std::size_t I, std::size_t J>
524struct IrrepDim<Dimension, YoungTableauSeq<>, I, J>
525{
526 static consteval std::size_t run(double prod)
527 {
528 return prod;
529 }
530};
531
532// Gram-Schmidt approach to produce from vec an orthogonal vector to the vector space generated by basis
533template <
534 class ElementType,
535 class... Id,
536 class LayoutStridedPolicy,
537 class MemorySpace,
538 class BasisId>
539tensor::Tensor<ElementType, ddc::DiscreteDomain<Id...>, LayoutStridedPolicy, MemorySpace>
540orthogonalize(
541 tensor::Tensor<ElementType, ddc::DiscreteDomain<Id...>, LayoutStridedPolicy, MemorySpace>
542 tensor,
543 csr::CsrDynamic<BasisId, Id...> basis,
544 std::size_t max_basis_id)
545{
546 ddc::Chunk eigentensor_alloc(
547 ddc::DiscreteDomain<BasisId, Id...>(basis.domain()),
548 ddc::HostAllocator<double>());
549 tensor::Tensor eigentensor(eigentensor_alloc);
550 for (ddc::DiscreteElement<BasisId> elem : ddc::DiscreteDomain<BasisId>(
551 ddc::DiscreteElement<BasisId>(0),
552 ddc::DiscreteVector<BasisId>(max_basis_id))) {
553 csr::csr2dense(eigentensor, basis.get(elem));
554
555 ddc::Chunk scalar_prod_alloc(ddc::DiscreteDomain<> {}, ddc::HostAllocator<double>());
556 tensor::Tensor scalar_prod(scalar_prod_alloc);
557 tensor::tensor_prod(scalar_prod, tensor, eigentensor[ddc::DiscreteElement<BasisId>(0)]);
558 ddc::Chunk norm_squared_alloc(ddc::DiscreteDomain<> {}, ddc::HostAllocator<double>());
559 tensor::Tensor norm_squared(norm_squared_alloc);
560 tensor::tensor_prod(norm_squared, eigentensor, eigentensor);
561
562 eigentensor *= -1. * scalar_prod(ddc::DiscreteElement<>())
563 / norm_squared(ddc::DiscreteElement<>());
564 tensor += eigentensor[ddc::DiscreteElement<BasisId>(0)];
565 }
566 return tensor;
567}
568
569// Function to find the number at the given index `n` with increasing Hamming weight
570std::vector<bool> index_hamming_weight_code(std::size_t index, std::size_t length)
571{
572 std::size_t count = 0;
573 std::vector<bool> bits(length);
574 for (std::size_t hamming_weight = 0; hamming_weight < length; ++hamming_weight) {
575 std::fill(bits.begin(), bits.begin() + hamming_weight, true);
576 std::fill(bits.begin() + hamming_weight, bits.end(), false);
577 do {
578 if (count == index) {
579 return bits;
580 }
581 count++;
582 } while (std::prev_permutation(bits.begin(), bits.end()));
583 }
584 assert(false && "hamming weight code not found for index");
585 return bits;
586}
587
588// Dummy tag used by OrthonormalBasisSubspaceEigenvalueOne (coalescent dimension of the CsrDynamic storage)
589struct BasisId : tensor::TensorNaturalIndex<>
590{
591};
592
593template <class Ids>
594struct OrthonormalBasisSubspaceEigenvalueOne;
595
596template <class... Id>
597struct OrthonormalBasisSubspaceEigenvalueOne<tensor::TensorFullIndex<Id...>>
598{
599 template <class YoungTableau>
600 static std::pair<csr::CsrDynamic<BasisId, Id...>, csr::CsrDynamic<BasisId, Id...>> run(
601 YoungTableau tableau)
602 {
603 auto [proj_alloc, proj] = tableau.template projector<Id...>();
604
605 tensor::TensorAccessor<Id...> candidate_accessor;
606 ddc::DiscreteDomain<Id...> candidate_dom = candidate_accessor.domain();
607 ddc::Chunk candidate_alloc(candidate_dom, ddc::HostAllocator<double>());
608 tensor::Tensor candidate(candidate_alloc);
609 ddc::Chunk prod_alloc(
610 tensor::natural_tensor_prod_domain(proj.domain(), candidate.domain()),
611 ddc::HostAllocator<double>());
612 tensor::Tensor prod(prod_alloc);
613
614 ddc::DiscreteDomain<BasisId, Id...> basis_dom(
615 ddc::DiscreteDomain<BasisId>(
616 ddc::DiscreteElement<BasisId>(0),
617 ddc::DiscreteVector<BasisId>(tableau.irrep_dim())),
618 candidate_dom);
619 csr::CsrDynamic<BasisId, Id...> u(basis_dom);
620 csr::CsrDynamic<BasisId, Id...> v(basis_dom);
621 std::size_t n_irreps = 0;
622 std::size_t index = 0;
623 while (n_irreps < tableau.irrep_dim()) {
624 index++;
625 std::vector<bool> hamming_weight_code
626 = index_hamming_weight_code(index, (Id::size() * ...));
627
628 ddc::parallel_for_each(candidate.domain(), [&](ddc::DiscreteElement<Id...> elem) {
629 candidate(elem) = hamming_weight_code[(
630 (misc::detail::stride<Id, Id...>() * elem.template uid<Id>()) + ...)];
631 });
632
633 tensor::tensor_prod(prod, proj, candidate);
634 Kokkos::deep_copy(
635 candidate.allocation_kokkos_view(),
636 prod.allocation_kokkos_view()); // We rely on Kokkos::deep_copy in place of ddc::parallel_deepcopy to avoid type verification of the type dimensions
637
638 orthogonalize(candidate, v, n_irreps);
639
640 if (ddc ::transform_reduce(
641 candidate.domain(),
642 false,
643 ddc::reducer::lor<bool>(),
644 [&](ddc::DiscreteElement<Id...> elem) { return candidate(elem) > 1e-6; })) {
645 ddc::Chunk
646 norm_squared_alloc(ddc::DiscreteDomain<> {}, ddc::HostAllocator<double>());
647 tensor::Tensor norm_squared(norm_squared_alloc);
648 tensor::tensor_prod(norm_squared, candidate, candidate);
649 ddc::parallel_for_each(candidate.domain(), [&](ddc::DiscreteElement<Id...> elem) {
650 candidate(elem) /= Kokkos::sqrt(norm_squared(ddc::DiscreteElement<>()));
651 });
652 // Not sure if u = v is correct in any case (ie. complex tensors ?)
653 u.push_back(candidate);
654 v.push_back(candidate);
655 n_irreps++;
656 std::cout << n_irreps << "/" << tableau.irrep_dim()
657 << " eigentensors found associated to the eigenvalue 1 for the Young "
658 "projector labelized "
659 << tableau.tag() << std::endl;
660 }
661 }
662 return std::pair<csr::CsrDynamic<BasisId, Id...>, csr::CsrDynamic<BasisId, Id...>>(u, v);
663 }
664};
665
666} // namespace detail
667
668template <std::size_t Dimension, misc::Specialization<YoungTableauSeq> TableauSeq>
669YoungTableau<Dimension, TableauSeq>::YoungTableau()
670{
671 // Check if the irrep is available in the dictionnary
672 {
673 std::ifstream file(IRREPS_DICT_PATH, std::ios::out | std::ios::binary);
674 std::string line;
675 while (!file.eof()) {
676 getline(file, line);
677 if (line == s_tag) {
678 file.close();
679 if (n_nonzeros_in_irrep() == 0) {
680 std::cout << "\033[1;31mIrrep " << s_tag << " in dimension " << s_d
681 << " required and found in dictionnary " << IRREPS_DICT_PATH
682 << " but the executable has been compiled without it. Please "
683 "recompile.\033[0m"
684 << std::endl;
685 }
686 return;
687 }
688 }
689 }
690
691 // If the current irrep is not found in the dictionnary, compute and dump it
692 std::cout << "\033[1;31mIrrep " << s_tag << " corresponding to the Young Tableau:\033[0m\n"
693 << *this << "\n\033[1;31min dimension " << s_d
694 << " required but not found in dictionnary " << IRREPS_DICT_PATH
695 << ". It will be computed, and you will have to recompile once it is done.\033[0m"
696 << std::endl;
697
698 auto [u, v]
699 = detail::OrthonormalBasisSubspaceEigenvalueOne<tensor::dummy_index_t<s_d, s_r>>::run(
700 *this);
701
702 std::ofstream file(IRREPS_DICT_PATH, std::ios::app | std::ios::binary);
703 if (!file) {
704 std::cerr << "Error opening file: " << IRREPS_DICT_PATH << std::endl;
705 return;
706 }
707 file << s_tag << "\n";
708 u.write(file);
709 v.write(file);
710 file << "\n";
711 file.close();
712 if (!file.good()) {
713 std::cerr << "Error occurred while writing to file " << IRREPS_DICT_PATH
714 << " while adding irrep " << s_tag << std::endl;
715 } else {
716 std::cout << "\033[1;32mIrrep " << s_tag << " added to the dictionnary " << IRREPS_DICT_PATH
717 << ".\033[0m \033[1;31mPlease recompile.\033[0m" << std::endl;
718 }
719}
720
721namespace detail {
722
723// Build index for symmetrizer (such that sym*proj is properly defined)
724template <class OId, class... Id>
725using symmetrizer_index_t = std::conditional_t<
726 (ddc::type_seq_rank_v<OId, ddc::detail::TypeSeq<Id...>> < (sizeof...(Id) / 2)),
727 tensor::prime<OId>,
728 ddc::type_seq_element_t<
729 static_cast<std::size_t>(
730 std::
731 max(static_cast<std::ptrdiff_t>(0),
732 static_cast<std::ptrdiff_t>(
733 ddc::type_seq_rank_v<OId, ddc::detail::TypeSeq<Id...>>
734 - (sizeof...(Id) / 2)))),
735 ddc::detail::TypeSeq<Id...>>>;
736
737// Functor to fill identity or transpose projectors
738template <std::size_t Dimension, std::size_t Rank, class... NaturalId>
739class TrFunctor
740{
741private:
742 using TensorType = tensor::Tensor<
743 double,
744 ddc::DiscreteDomain<NaturalId...>,
745 Kokkos::layout_right,
746 Kokkos::DefaultHostExecutionSpace::memory_space>;
747
748 TensorType t;
749 std::array<std::size_t, Rank> idx_to_permute;
750
751public:
752 TrFunctor(TensorType t_, std::array<std::size_t, Rank> idx_to_permute_)
753 : t(t_)
754 , idx_to_permute(idx_to_permute_)
755 {
756 }
757
758 void operator()(ddc::DiscreteElement<NaturalId...> elem) const
759 {
760 std::array<std::size_t, sizeof...(NaturalId)> elem_array = ddc::detail::array(elem);
761 bool has_to_be_one = true;
762 for (std::size_t i = 0; i < Rank; ++i) {
763 has_to_be_one
764 = has_to_be_one && (elem_array[i] == elem_array[idx_to_permute[i] + Rank]);
765 }
766 if (has_to_be_one) {
767 t(elem) = 1;
768 }
769 }
770};
771
772template <std::size_t Dimension, bool AntiSym, class... Id>
773static tensor::Tensor<
774 double,
775 ddc::DiscreteDomain<Id...>,
776 Kokkos::layout_right,
777 Kokkos::DefaultHostExecutionSpace::memory_space>
778fill_symmetrizer(
779 tensor::Tensor<
780 double,
781 ddc::DiscreteDomain<Id...>,
782 Kokkos::layout_right,
783 Kokkos::DefaultHostExecutionSpace::memory_space> sym,
784 std::array<std::size_t, sizeof...(Id) / 2> idx_to_permute)
785{
786 ddc::Chunk tr_alloc(sym.domain(), ddc::HostAllocator<double>());
787 tensor::Tensor tr(tr_alloc);
788 ddc::parallel_fill(tr, 0);
789 ddc::for_each(tr.domain(), TrFunctor<Dimension, sizeof...(Id) / 2, Id...>(tr, idx_to_permute));
790
791 if constexpr (AntiSym) {
792 tr *= static_cast<double>(misc::permutation_parity(idx_to_permute));
793 }
794 sym += tr;
795
796 return sym;
797}
798
799/*
800 Compute all permutations on a subset of indices in idx_to_permute, keep the
801 rest of the indices where they are.
802 */
803template <std::size_t Nt, std::size_t Ns>
804static std::vector<std::array<std::size_t, Nt>> permutations_subset(
805 std::array<std::size_t, Nt> t,
806 std::array<std::size_t, Ns> subset_values)
807{
808 std::array<std::size_t, Ns> subset_indices;
809 std::array<std::size_t, Ns> elements_to_permute;
810 int j = 0;
811 for (std::size_t i = 0; i < t.size(); ++i) {
812 if (std::find(subset_values.begin(), subset_values.end(), t[i] + 1)
813 != std::end(subset_values)) {
814 subset_indices[j] = i;
815 elements_to_permute[j++] = t[i];
816 }
817 }
818
819 std::vector<std::array<std::size_t, Nt>> result;
820 do {
821 std::array<std::size_t, Nt> tmp = t;
822 for (std::size_t i = 0; i < Ns; ++i) {
823 tmp[subset_indices[i]] = elements_to_permute[i];
824 }
825 result.push_back(tmp);
826 } while (std::next_permutation(elements_to_permute.begin(), elements_to_permute.end()));
827
828 return result;
829}
830
831// Compute projector
832template <class PartialTableauSeq, std::size_t Dimension, bool AntiSym = false>
833struct Projector;
834
835template <std::size_t... ElemOfHeadRow, class... TailRow, std::size_t Dimension, bool AntiSym>
836struct Projector<
837 YoungTableauSeq<std::index_sequence<ElemOfHeadRow...>, TailRow...>,
838 Dimension,
839 AntiSym>
840{
841 template <class... Id>
842 static tensor::Tensor<
843 double,
844 ddc::DiscreteDomain<Id...>,
845 Kokkos::layout_right,
846 Kokkos::DefaultHostExecutionSpace::memory_space>
847 run(tensor::Tensor<
848 double,
849 ddc::DiscreteDomain<Id...>,
850 Kokkos::layout_right,
851 Kokkos::DefaultHostExecutionSpace::memory_space> proj)
852 {
853 if constexpr (sizeof...(ElemOfHeadRow) >= 2) {
854 // Allocate & build a symmetric projector for the row
855 tensor::TensorAccessor<symmetrizer_index_t<Id, Id...>...> sym_accessor;
856 ddc::DiscreteDomain<symmetrizer_index_t<Id, Id...>...> sym_dom = sym_accessor.domain();
857
858 ddc::Chunk sym_alloc(sym_dom, ddc::HostAllocator<double>());
859 tensor::Tensor sym(sym_alloc);
860 ddc::parallel_fill(sym, 0);
861 std::array<std::size_t, sizeof...(Id) / 2> idx_to_permute;
862 for (std::size_t i = 0; i < sizeof...(Id) / 2; ++i) {
863 idx_to_permute[i] = i;
864 }
865 std::array<std::size_t, sizeof...(ElemOfHeadRow)> row_values {ElemOfHeadRow...};
866 auto idx_permutations = detail::permutations_subset(
867 idx_to_permute,
868 row_values); // TODO check https://indico.cern.ch/event/814040/contributions/3452485/attachments/1860434/3057354/psr_alcock.pdf page 6, it may be incomplete
869 for (std::size_t i = 0; i < idx_permutations.size(); ++i) {
870 fill_symmetrizer<
871 Dimension,
872 AntiSym,
873 symmetrizer_index_t<Id, Id...>...>(sym, idx_permutations[i]);
874 }
875
876 // Extract the symmetric part (for the row) of the projector (requires an intermediate prod tensor)
877 ddc::Chunk prod_alloc(
878 natural_tensor_prod_domain(sym.domain(), proj.domain()),
879 ddc::HostAllocator<double>());
880 tensor::Tensor prod(prod_alloc);
881 tensor::tensor_prod(prod, sym, proj);
882 Kokkos::deep_copy(
883 proj.allocation_kokkos_view(),
884 prod.allocation_kokkos_view()); // We rely on Kokkos::deep_copy in place of ddc::parallel_deepcopy to avoid type verification of the type dimensions
885 }
886 if constexpr (sizeof...(TailRow) == 0) {
887 return proj;
888 } else {
889 return Projector<YoungTableauSeq<TailRow...>, Dimension, AntiSym>::run(proj);
890 }
891 }
892};
893
894} // namespace detail
895
896template <std::size_t Dimension, misc::Specialization<YoungTableauSeq> TableauSeq>
897template <tensor::TensorNatIndex... Id>
898auto YoungTableau<Dimension, TableauSeq>::projector()
899{
900 static_assert(sizeof...(Id) == s_r);
901 tensor::TensorAccessor<tensor::prime<Id>..., Id...> proj_accessor;
902 ddc::DiscreteDomain<tensor::prime<Id>..., Id...> proj_dom = proj_accessor.domain();
903
904 // Allocate a projector and fill it as an identity tensor
905 ddc::Chunk proj_alloc(proj_dom, ddc::HostAllocator<double>());
906 tensor::Tensor proj(proj_alloc);
907 ddc::parallel_fill(proj, 0);
908 std::array<std::size_t, s_r> idx_to_permute;
909 for (std::size_t i = 0; i < s_r; ++i) {
910 idx_to_permute[i] = i;
911 }
912 ddc::for_each(
913 proj.domain(),
914 detail::TrFunctor<s_d, s_r, tensor::prime<Id>..., Id...>(proj, idx_to_permute));
915
916 // Build the projector
917 detail::Projector<tableau_seq, s_d>::run(proj);
918 detail::Projector<typename dual::tableau_seq, s_d, true>::run(proj);
919 return std::make_tuple(std::move(proj_alloc), proj);
920}
921
922// Access to u and v Csr, allowing to ie. compress or uncompress a tensor with internal symmetries
923template <std::size_t Dimension, misc::Specialization<YoungTableauSeq> TableauSeq>
924template <tensor::TensorIndex BasisId, tensor::TensorNatIndex... Id>
925constexpr csr::Csr<YoungTableau<Dimension, TableauSeq>::n_nonzeros_in_irrep(), BasisId, Id...>
926YoungTableau<Dimension, TableauSeq>::u(ddc::DiscreteDomain<Id...> restricted_domain)
927{
928 if constexpr (n_nonzeros_in_irrep() != 0) {
929 ddc::DiscreteDomain<BasisId, Id...>
930 domain(ddc::DiscreteDomain<BasisId>(
931 ddc::DiscreteElement<BasisId>(0),
932 ddc::DiscreteVector<BasisId>(n_nonzeros_in_irrep())),
933 restricted_domain);
934
935 return csr::Csr<n_nonzeros_in_irrep(), BasisId, Id...>(
936 domain,
937 std::get<0>(std::get<0>(s_irrep)),
938 std::get<1>(std::get<0>(s_irrep)),
939 std::get<2>(std::get<0>(s_irrep)));
940 } else {
941 ddc::DiscreteDomain<BasisId, Id...>
942 domain(ddc::DiscreteDomain<BasisId>(
943 ddc::DiscreteElement<BasisId>(0),
944 ddc::DiscreteVector<BasisId>(0)),
945 restricted_domain);
946
947 return csr::Csr<n_nonzeros_in_irrep(), BasisId, Id...>(
948 domain,
949 std::array<std::size_t, BasisId::mem_size() + 1> {},
950 std::get<1>(std::get<0>(s_irrep)),
951 std::get<2>(std::get<0>(s_irrep)));
952 }
953}
954
955template <std::size_t Dimension, misc::Specialization<YoungTableauSeq> TableauSeq>
956template <tensor::TensorIndex BasisId, tensor::TensorNatIndex... Id>
957constexpr csr::Csr<YoungTableau<Dimension, TableauSeq>::n_nonzeros_in_irrep(), BasisId, Id...>
958YoungTableau<Dimension, TableauSeq>::v(ddc::DiscreteDomain<Id...> restricted_domain)
959{
960 if constexpr (n_nonzeros_in_irrep() != 0) {
961 ddc::DiscreteDomain<BasisId, Id...>
962 domain(ddc::DiscreteDomain<BasisId>(
963 ddc::DiscreteElement<BasisId>(0),
964 ddc::DiscreteVector<BasisId>(n_nonzeros_in_irrep())),
965 restricted_domain);
966
967 return csr::Csr<n_nonzeros_in_irrep(), BasisId, Id...>(
968 domain,
969 std::get<0>(std::get<1>(s_irrep)),
970 std::get<1>(std::get<1>(s_irrep)),
971 std::get<2>(std::get<1>(s_irrep)));
972 } else {
973 ddc::DiscreteDomain<BasisId, Id...>
974 domain(ddc::DiscreteDomain<BasisId>(
975 ddc::DiscreteElement<BasisId>(0),
976 ddc::DiscreteVector<BasisId>(0)),
977 restricted_domain);
978
979 return csr::Csr<n_nonzeros_in_irrep(), BasisId, Id...>(
980 domain,
981 std::array<std::size_t, BasisId::mem_size() + 1> {},
982 std::get<1>(std::get<1>(s_irrep)),
983 std::get<2>(std::get<1>(s_irrep)));
984 }
985}
986
987// Load binary files and build u and v static constexpr Csr at compile-time
988namespace detail {
989
990template <std::size_t Line>
991consteval std::string_view load_irrep_line_for_tag(std::string_view const tag)
992{
993 constexpr static char raw[] = {
994#embed IRREPS_DICT_PATH
995 };
996
997 constexpr static std::string_view str(raw, sizeof(raw));
998
999 size_t tagPos = str.find(tag);
1000
1001 if (tagPos != std::string::npos) {
1002 std::size_t endOfTagLine = str.find('\n', tagPos);
1003 if (endOfTagLine != std::string::npos) {
1004 std::size_t nextLineStart = endOfTagLine + 1;
1005 for (std::size_t i = 0; i < Line; ++i) {
1006 nextLineStart = str.find("break\n", nextLineStart) + 6;
1007 }
1008 std::size_t nextLineEnd = str.find("break\n", nextLineStart);
1009
1010 return std::string_view(str.data() + nextLineStart, nextLineEnd - nextLineStart);
1011 }
1012 }
1013
1014 return "";
1015}
1016
1017template <class Ids, std::size_t Offset>
1018struct LoadIrrepIdxForTag;
1019
1020template <std::size_t... I, std::size_t Offset>
1021struct LoadIrrepIdxForTag<std::index_sequence<I...>, Offset>
1022{
1023 static consteval std::array<std::string_view, sizeof...(I)> run(std::string_view const tag)
1024 {
1025 return std::array<std::string_view, sizeof...(I)> {
1026 load_irrep_line_for_tag<I + Offset>(tag)...};
1027 }
1028};
1029
1030template <class T, std::size_t N, std::size_t I = 0>
1031consteval std::array<T, N> bit_cast_array(
1032 std::array<T, N> vec,
1033 std::string_view const str,
1034 std::string_view const tag)
1035{
1036 if constexpr (I == N) {
1037 return vec;
1038 } else {
1039 std::array<char, sizeof(T) / sizeof(char)> chars = {};
1040 for (std::size_t j = 0; j < sizeof(T) / sizeof(char); ++j) {
1041 chars[j] = str[sizeof(T) / sizeof(char) * I + j];
1042 }
1043
1044 vec[I] = std::bit_cast<T>(
1045 chars); // We rely on std::bit_cast because std::reinterprest_cast is not constexpr
1046 return bit_cast_array<T, N, I + 1>(vec, str, tag);
1047 }
1048}
1049
1050template <class T, std::size_t N, std::size_t I = 0>
1051consteval std::array<T, N> bit_cast_array(std::string_view const str, std::string_view const tag)
1052{
1053 std::array<T, N> vec {};
1054 return bit_cast_array<T, N>(vec, str, tag);
1055}
1056
1057template <class T, std::size_t N, class Ids>
1058struct BitCastArrayOfArrays;
1059
1060template <class T, std::size_t N, std::size_t... I>
1061struct BitCastArrayOfArrays<T, N, std::index_sequence<I...>>
1062{
1063 static consteval std::array<std::array<T, N>, sizeof...(I)> run(
1064 std::array<std::string_view, sizeof...(I)> const str,
1065 std::string_view const tag)
1066 {
1067 return std::array<std::array<T, N>, sizeof...(I)> {bit_cast_array<T, N>(str[I], tag)...};
1068 }
1069};
1070
1071} // namespace detail
1072
1073template <std::size_t Dimension, misc::Specialization<YoungTableauSeq> TableauSeq>
1074consteval auto YoungTableau<Dimension, TableauSeq>::load_irrep()
1075{
1076 static constexpr std::string_view str_u_coalesc_idx(detail::load_irrep_line_for_tag<0>(s_tag));
1077 static constexpr std::array<std::string_view, s_r> str_u_idx(
1078 detail::LoadIrrepIdxForTag<std::make_index_sequence<s_r>, 1>::run(s_tag));
1079 static constexpr std::string_view str_u_values(detail::load_irrep_line_for_tag<s_r + 1>(s_tag));
1080 static constexpr std::string_view str_v_coalesc_idx(
1081 detail::load_irrep_line_for_tag<s_r + 2>(s_tag));
1082 static constexpr std::array<std::string_view, s_r> str_v_idx(
1083 detail::LoadIrrepIdxForTag<std::make_index_sequence<s_r>, s_r + 3>::run(s_tag));
1084 static constexpr std::string_view str_v_values(
1085 detail::load_irrep_line_for_tag<2 * s_r + 3>(s_tag));
1086
1087 if constexpr (str_u_values.size() != 0) {
1088 static constexpr std::array u_coalesc_idx = detail::bit_cast_array<
1089 std::size_t,
1090 str_u_coalesc_idx.size() / sizeof(std::size_t)>(str_u_coalesc_idx, s_tag);
1091 static constexpr std::array u_idx = detail::BitCastArrayOfArrays<
1092 std::size_t,
1093 str_u_idx[0].size() / sizeof(std::size_t),
1094 std::make_index_sequence<s_r>>::run(str_u_idx, s_tag);
1095 static constexpr std::array u_values = detail::
1096 bit_cast_array<double, str_u_values.size() / sizeof(double)>(str_u_values, s_tag);
1097 static constexpr std::array v_coalesc_idx = detail::bit_cast_array<
1098 std::size_t,
1099 str_v_coalesc_idx.size() / sizeof(std::size_t)>(str_v_coalesc_idx, s_tag);
1100 static constexpr std::array v_idx = detail::BitCastArrayOfArrays<
1101 std::size_t,
1102 str_v_idx[0].size() / sizeof(std::size_t),
1103 std::make_index_sequence<s_r>>::run(str_v_idx, s_tag);
1104 static constexpr std::array v_values = detail::
1105 bit_cast_array<double, str_v_values.size() / sizeof(double)>(str_v_values, s_tag);
1106 return std::make_pair(
1107 std::make_tuple(u_coalesc_idx, u_idx, u_values),
1108 std::make_tuple(v_coalesc_idx, v_idx, v_values));
1109 } else {
1110 return std::make_pair(
1111 std::make_tuple(
1112 std::array<std::size_t, 1> {0},
1113 std::array<std::array<std::size_t, 0>, s_r> {},
1114 std::array<double, 0> {}),
1115 std::make_tuple(
1116 std::array<std::size_t, 1> {0},
1117 std::array<std::array<std::size_t, 0>, s_r> {},
1118 std::array<double, 0> {}));
1119 }
1120}
1121
1122namespace detail {
1123
1124// Produce tag as a string (std::string features are limited at compile-time that's why we manipulate char arrays)
1125template <class Row>
1126struct YoungTableauRowToArray;
1127
1128template <std::size_t... RowElement>
1129struct YoungTableauRowToArray<std::index_sequence<RowElement...>>
1130{
1131 static constexpr auto run()
1132 {
1133 static constexpr std::array row = {RowElement...};
1134 return row;
1135 }
1136};
1137
1138template <class TableauSeq>
1139struct YoungTableauToArray;
1140
1141template <class... Row>
1142struct YoungTableauToArray<YoungTableauSeq<Row...>>
1143{
1144 static constexpr auto run()
1145 {
1146 static constexpr std::tuple tableau = {YoungTableauRowToArray<Row>::run()...};
1147 return tableau;
1148 }
1149};
1150
1151template <bool RowDelimiter>
1152struct RowToString
1153{
1154 template <std::size_t N>
1155 static constexpr std::array<char, 2 * N - !RowDelimiter> run(
1156 const std::array<std::size_t, N>& array)
1157 {
1158 std::array<char, 2 * N - !RowDelimiter> buf = {};
1159
1160 std::size_t current_pos = 0;
1161 for (std::size_t i = 0; i < N; ++i) {
1162 char temp[20];
1163 auto [ptr, ec] = std::to_chars(temp, temp + sizeof(temp), array[i]);
1164 if (ec != std::errc()) {
1165 throw std::runtime_error("Failed to convert number to string");
1166 }
1167
1168 for (char* p = temp; p < ptr; ++p) {
1169 buf[current_pos++] = *p;
1170 }
1171
1172 if (i < N - 1) {
1173 buf[current_pos++] = '_';
1174 }
1175 }
1176
1177 if constexpr (RowDelimiter) {
1178 buf[2 * N - 1] = 'l';
1179 }
1180
1181 return buf;
1182 }
1183};
1184
1185template <std::size_t... sizes>
1186constexpr auto concatenate(const std::array<char, sizes>&... arrays)
1187{
1188 std::array<char, (sizes + ...)> result;
1189 std::size_t index {};
1190
1191 ((std::copy_n(arrays.begin(), sizes, result.begin() + index), index += sizes), ...);
1192
1193 return result;
1194}
1195
1196template <class RowIdx>
1197struct ArrayToString;
1198
1199template <std::size_t... RowId>
1200struct ArrayToString<std::index_sequence<RowId...>>
1201{
1202 template <class Tuple>
1203 static constexpr auto run(Tuple const tableau)
1204 {
1205 return concatenate(
1206 RowToString<RowId != sizeof...(RowId) - 1>::run(std::get<RowId>(tableau))...);
1207 }
1208};
1209
1210template <std::size_t size>
1211constexpr auto add_dimension(const std::array<char, size>& array, std::size_t d)
1212{
1213 std::array<char, size + 2> result;
1214 char temp[1];
1215 std::to_chars(temp, temp + 1, d);
1216 std::copy_n(array.begin(), size, result.begin());
1217 result[size] = 'd';
1218 result[size + 1] = temp[0];
1219
1220 return result;
1221}
1222
1223} // namespace detail
1224
1225template <std::size_t Dimension, misc::Specialization<YoungTableauSeq> TableauSeq>
1226constexpr std::string YoungTableau<Dimension, TableauSeq>::generate_tag()
1227{
1228 static constexpr std::tuple tableau = detail::YoungTableauToArray<tableau_seq>::run();
1229 constexpr auto row_str_wo_dimension
1230 = detail::ArrayToString<std::make_index_sequence<tableau_seq::shape::size()>>::run(
1231 tableau);
1232 constexpr auto row_str = detail::add_dimension(row_str_wo_dimension, s_d);
1233 return std::string(row_str.begin(), row_str.end());
1234}
1235
1236namespace detail {
1237
1238// Print Young tableau in a string
1239template <class TableauSeq>
1240struct PrintYoungTableauSeq;
1241
1242template <std::size_t HeadRowHeadElement, std::size_t... HeadRowTailElement, class... TailRow>
1243struct PrintYoungTableauSeq<
1244 YoungTableauSeq<std::index_sequence<HeadRowHeadElement, HeadRowTailElement...>, TailRow...>>
1245{
1246 static std::string run(std::string str)
1247 {
1248 str += std::to_string(HeadRowHeadElement) + " ";
1249 if constexpr (sizeof...(TailRow) == 0 && sizeof...(HeadRowTailElement) == 0) {
1250 } else if constexpr (sizeof...(HeadRowTailElement) == 0) {
1251 str += "\n";
1252 str = PrintYoungTableauSeq<YoungTableauSeq<TailRow...>>::run(str);
1253 } else {
1254 str = PrintYoungTableauSeq<
1255 YoungTableauSeq<std::index_sequence<HeadRowTailElement...>, TailRow...>>::
1256 run(str);
1257 }
1258 return str;
1259 }
1260};
1261
1262template <>
1263struct PrintYoungTableauSeq<YoungTableauSeq<>>
1264{
1265 static std::string run(std::string str)
1266 {
1267 return str;
1268 }
1269};
1270
1271} // namespace detail
1272
1273template <std::size_t Dimension, misc::Specialization<YoungTableauSeq> TableauSeq>
1274std::ostream& operator<<(std::ostream& os, YoungTableau<Dimension, TableauSeq> const& tableau)
1275{
1276 std::string str = "";
1277 os << detail::PrintYoungTableauSeq<TableauSeq>::run(str);
1278 return os;
1279}
1280
1281} // namespace young_tableau
1282
1283} // namespace sil
1284
1285#endif
std::ostream & operator<<(std::ostream &os, Csr< N, TensorIndex... > const &csr)
Definition csr.hpp:185
natural_tensor_prod_domain_t< Dom1, Dom2 > natural_tensor_prod_domain(Dom1 dom1, Dom2 dom2)
The top-level namespace of SimiLie.
Definition csr.hpp:14