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