Skip to main content

spenso_hep_lib/
lib.rs

1use std::{ops::Neg, sync::LazyLock};
2
3use idenso::{IndexTooling, color::CS, dirac::AGS, representations::initialize};
4
5use spenso::{
6    algebra::complex::Complex,
7    network::{
8        Network,
9        library::{
10            LibraryTensor, TensorLibraryData,
11            function_lib::{INBUILTS, PanicMissingConcrete, SymbolLib},
12            symbolic::{ExplicitKey, TensorLibrary},
13        },
14        parsing::ShadowedStructure,
15        store::NetworkStore,
16    },
17    structure::{
18        Canonicalized, TensorDataLayout, TensorStructure, abstract_index::AbstractIndex,
19        slot::AbsInd,
20    },
21    tensors::{
22        complex::RealOrComplexTensor,
23        data::{SetTensorData, SparseTensor, StorageTensor},
24        parametric::{MixedTensor, ParamTensor},
25    },
26};
27use symbolica::{
28    atom::{Atom, Symbol},
29    parse_lit,
30};
31
32struct LogicalSparseInput<T, N> {
33    layout: TensorDataLayout,
34    storage: SparseTensor<T, N>,
35}
36
37impl<T, N: TensorStructure> LogicalSparseInput<T, N> {
38    fn set(&mut self, indices: &[usize], value: T) -> Result<(), &'static str> {
39        let storage_indices = self
40            .layout
41            .logical_expanded_to_storage_expanded(indices)
42            .map_err(|_| "hard-coded logical coordinate must be in bounds")?;
43        self.storage
44            .set(&storage_indices, value)
45            .map_err(|_| "translated storage coordinate must be in bounds")
46    }
47}
48
49fn sparse_from_logical<T, N>(
50    structure: Canonicalized<N>,
51    zero: T,
52    build: impl FnOnce(&mut LogicalSparseInput<T, N>),
53) -> Canonicalized<SparseTensor<T, N>>
54where
55    N: TensorStructure,
56{
57    let layout = TensorDataLayout::from_canonicalized(&structure)
58        .expect("hard-coded tensor data requires concrete dimensions");
59    structure.map_canonical(|structure| {
60        let mut input = LogicalSparseInput {
61            layout,
62            storage: SparseTensor::empty(structure, zero),
63        };
64        build(&mut input);
65        input.storage
66    })
67}
68
69#[allow(clippy::similar_names)]
70pub fn gamma_data_dirac<T, N>(
71    structure: Canonicalized<N>,
72    one: T,
73    zero: T,
74) -> Canonicalized<SparseTensor<Complex<T>, N>>
75where
76    T: Clone + Neg<Output = T>,
77    N: TensorStructure,
78{
79    let c1 = Complex::<T>::new(one.clone(), zero.clone());
80    let z = Complex::<T>::new(zero.clone(), zero.clone());
81    let cn1 = Complex::<T>::new(-one.clone(), zero.clone());
82    let ci = Complex::<T>::new(zero.clone(), one.clone());
83    let cni = Complex::<T>::new(zero.clone(), -one.clone());
84    sparse_from_logical(structure, z, |gamma| {
85        // Coordinates follow the public logical order: bispinor row, bispinor
86        // column, Minkowski. The layout maps them into canonical storage.
87
88        // dirac gamma matrices
89
90        gamma.set(&[0, 0, 0], c1.clone()).unwrap();
91        gamma.set(&[1, 1, 0], c1.clone()).unwrap();
92        gamma.set(&[2, 2, 0], cn1.clone()).unwrap();
93        gamma.set(&[3, 3, 0], cn1.clone()).unwrap();
94
95        gamma.set(&[0, 3, 1], c1.clone()).unwrap();
96        gamma.set(&[1, 2, 1], c1.clone()).unwrap();
97        gamma.set(&[2, 1, 1], cn1.clone()).unwrap();
98        gamma.set(&[3, 0, 1], cn1.clone()).unwrap();
99
100        gamma.set(&[0, 3, 2], cni.clone()).unwrap();
101        gamma.set(&[1, 2, 2], ci.clone()).unwrap();
102        gamma.set(&[2, 1, 2], ci.clone()).unwrap();
103        gamma.set(&[3, 0, 2], cni.clone()).unwrap();
104
105        gamma.set(&[0, 2, 3], c1.clone()).unwrap();
106        gamma.set(&[1, 3, 3], cn1.clone()).unwrap();
107        gamma.set(&[2, 0, 3], cn1.clone()).unwrap();
108        gamma.set(&[3, 1, 3], c1.clone()).unwrap();
109
110        // gamma.to_dense()
111    })
112}
113
114#[allow(clippy::similar_names)]
115pub fn gamma_data_weyl<T, N>(
116    structure: Canonicalized<N>,
117    one: T,
118    zero: T,
119) -> Canonicalized<SparseTensor<Complex<T>, N>>
120where
121    T: Neg<Output = T> + Clone,
122    N: TensorStructure,
123{
124    let z = Complex::<T>::new(zero.clone(), zero.clone());
125    let c1 = Complex::<T>::new(one.clone(), zero.clone());
126    let cn1 = Complex::<T>::new(-one.clone(), zero.clone());
127    let ci = Complex::<T>::new(zero.clone(), one.clone());
128    let cni = Complex::<T>::new(zero.clone(), -one.clone());
129    sparse_from_logical(structure, z, |gamma| {
130        // Coordinates follow the public logical order: bispinor row, bispinor
131        // column, Minkowski. The layout maps them into canonical storage.
132
133        // dirac gamma matrices
134
135        gamma.set(&[0, 2, 0], c1.clone()).unwrap();
136        gamma.set(&[1, 3, 0], c1.clone()).unwrap();
137        gamma.set(&[2, 0, 0], c1.clone()).unwrap();
138        gamma.set(&[3, 1, 0], c1.clone()).unwrap();
139
140        gamma.set(&[0, 3, 1], c1.clone()).unwrap();
141        gamma.set(&[1, 2, 1], c1.clone()).unwrap();
142        gamma.set(&[2, 1, 1], cn1.clone()).unwrap();
143        gamma.set(&[3, 0, 1], cn1.clone()).unwrap();
144
145        gamma.set(&[0, 3, 2], cni.clone()).unwrap();
146        gamma.set(&[1, 2, 2], ci.clone()).unwrap();
147        gamma.set(&[2, 1, 2], ci.clone()).unwrap();
148        gamma.set(&[3, 0, 2], cni.clone()).unwrap();
149
150        gamma.set(&[0, 2, 3], c1.clone()).unwrap();
151        gamma.set(&[1, 3, 3], cn1.clone()).unwrap();
152        gamma.set(&[2, 0, 3], cn1.clone()).unwrap();
153        gamma.set(&[3, 1, 3], c1.clone()).unwrap();
154
155        // gamma.to_dense()
156    })
157}
158
159#[allow(clippy::similar_names)]
160pub fn gamma_transpose_weyl<T, N>(
161    structure: Canonicalized<N>,
162    one: T,
163    zero: T,
164) -> Canonicalized<SparseTensor<Complex<T>, N>>
165where
166    T: Neg<Output = T> + Clone,
167    N: TensorStructure,
168{
169    let z = Complex::<T>::new(zero.clone(), zero.clone());
170    let c1 = Complex::<T>::new(one.clone(), zero.clone());
171    let cn1 = Complex::<T>::new(-one.clone(), zero.clone());
172    let ci = Complex::<T>::new(zero.clone(), one.clone());
173    let cni = Complex::<T>::new(zero.clone(), -one.clone());
174    sparse_from_logical(structure, z, |gamma| {
175        // Coordinates follow the public logical order: bispinor row, bispinor
176        // column, Minkowski. The layout maps them into canonical storage.
177
178        // dirac gamma matrices
179
180        gamma.set(&[2, 0, 0], c1.clone()).unwrap();
181        gamma.set(&[3, 1, 0], c1.clone()).unwrap();
182        gamma.set(&[0, 2, 0], c1.clone()).unwrap();
183        gamma.set(&[1, 3, 0], c1.clone()).unwrap();
184
185        gamma.set(&[3, 0, 1], c1.clone()).unwrap();
186        gamma.set(&[2, 1, 1], c1.clone()).unwrap();
187        gamma.set(&[1, 2, 1], cn1.clone()).unwrap();
188        gamma.set(&[0, 3, 1], cn1.clone()).unwrap();
189
190        gamma.set(&[3, 0, 2], cni.clone()).unwrap();
191        gamma.set(&[2, 1, 2], ci.clone()).unwrap();
192        gamma.set(&[1, 2, 2], ci.clone()).unwrap();
193        gamma.set(&[0, 3, 2], cni.clone()).unwrap();
194
195        gamma.set(&[2, 0, 3], c1.clone()).unwrap();
196        gamma.set(&[3, 1, 3], cn1.clone()).unwrap();
197        gamma.set(&[0, 2, 3], cn1.clone()).unwrap();
198        gamma.set(&[1, 3, 3], c1.clone()).unwrap();
199
200        // gamma.to_dense()
201    })
202}
203
204#[allow(clippy::similar_names)]
205pub fn gamma_conj_data_weyl<T, N>(
206    structure: Canonicalized<N>,
207    one: T,
208    zero: T,
209) -> Canonicalized<SparseTensor<Complex<T>, N>>
210where
211    T: Neg<Output = T> + Clone,
212    N: TensorStructure,
213{
214    gamma_data_weyl(structure, one, zero).map_canonical(|tensor| {
215        tensor.map_data(|a| {
216            let Complex { re, im } = a;
217            Complex { re, im: -im }
218        })
219    })
220}
221
222#[allow(clippy::similar_names)]
223pub fn gamma_adj_data_weyl<T, N>(
224    structure: Canonicalized<N>,
225    one: T,
226    zero: T,
227) -> Canonicalized<SparseTensor<Complex<T>, N>>
228where
229    T: Neg<Output = T> + Clone,
230    N: TensorStructure,
231{
232    gamma_transpose_weyl(structure, one, zero).map_canonical(|tensor| {
233        tensor.map_data(|a| {
234            let Complex { re, im } = a;
235            Complex { re, im: -im }
236        })
237    })
238}
239
240pub fn gamma0_weyl<T, N>(
241    structure: Canonicalized<N>,
242    one: T,
243    zero: T,
244) -> Canonicalized<SparseTensor<Complex<T>, N>>
245where
246    T: Clone,
247    N: TensorStructure,
248{
249    let c1 = Complex::<T>::new(one, zero.clone());
250    let z = Complex::<T>::new(zero.clone(), zero.clone());
251    sparse_from_logical(structure, z, |gamma0| {
252        // ! No check on actual structure, should expext bis,bis,lor
253
254        // dirac gamma0 matrices
255
256        gamma0.set(&[0, 2], c1.clone()).unwrap();
257        gamma0.set(&[1, 3], c1.clone()).unwrap();
258        gamma0.set(&[2, 0], c1.clone()).unwrap();
259        gamma0.set(&[3, 1], c1.clone()).unwrap();
260    })
261}
262
263pub fn gamma5_dirac_data<T, N>(
264    structure: Canonicalized<N>,
265    one: T,
266    zero: T,
267) -> Canonicalized<SparseTensor<Complex<T>, N>>
268where
269    T: Clone,
270    N: TensorStructure,
271{
272    let c1 = Complex::<T>::new(one, zero.clone());
273
274    let z = Complex::<T>::new(zero.clone(), zero.clone());
275    sparse_from_logical(structure, z, |gamma5| {
276        gamma5.set(&[0, 2], c1.clone()).unwrap();
277        gamma5.set(&[1, 3], c1.clone()).unwrap();
278        gamma5.set(&[2, 0], c1.clone()).unwrap();
279        gamma5.set(&[3, 1], c1.clone()).unwrap();
280    })
281}
282
283pub fn gamma5_weyl_data<T, N>(
284    structure: Canonicalized<N>,
285    one: T,
286    zero: T,
287) -> Canonicalized<SparseTensor<Complex<T>, N>>
288where
289    T: Clone + Neg<Output = T>,
290    N: TensorStructure,
291{
292    let z = Complex::<T>::new(zero.clone(), zero.clone());
293    let c1 = Complex::<T>::new(one, zero);
294
295    sparse_from_logical(structure, z, |gamma5| {
296        gamma5.set(&[0, 0], -c1.clone()).unwrap();
297        gamma5.set(&[1, 1], -c1.clone()).unwrap();
298        gamma5.set(&[2, 2], c1.clone()).unwrap();
299        gamma5.set(&[3, 3], c1.clone()).unwrap();
300    })
301}
302
303#[allow(clippy::similar_names)]
304pub fn proj_m_data_dirac<T, N>(
305    structure: Canonicalized<N>,
306    half: T,
307    zero: T,
308) -> Canonicalized<SparseTensor<Complex<T>, N>>
309where
310    T: Clone + Neg<Output = T>,
311    N: TensorStructure,
312{
313    let z = Complex::<T>::new(zero.clone(), zero.clone());
314    // ProjM(1,2) Left chirality projector (( 1−γ5)/ 2 )_s1_s2
315
316    let chalf = Complex::<T>::new(half.clone(), zero.clone());
317    let cnhalf = Complex::<T>::new(-half, zero);
318
319    sparse_from_logical(structure, z, |proj_m| {
320        proj_m.set(&[0, 0], chalf.clone()).unwrap();
321        proj_m.set(&[1, 1], chalf.clone()).unwrap();
322        proj_m.set(&[2, 2], chalf.clone()).unwrap();
323        proj_m.set(&[3, 3], chalf.clone()).unwrap();
324
325        proj_m.set(&[0, 2], cnhalf.clone()).unwrap();
326        proj_m.set(&[1, 3], cnhalf.clone()).unwrap();
327        proj_m.set(&[2, 0], cnhalf.clone()).unwrap();
328        proj_m.set(&[3, 1], cnhalf.clone()).unwrap();
329    })
330}
331
332#[allow(clippy::similar_names)]
333pub fn proj_m_data_weyl<T, N>(
334    structure: Canonicalized<N>,
335    one: T,
336    zero: T,
337) -> Canonicalized<SparseTensor<Complex<T>, N>>
338where
339    T: Clone,
340    N: TensorStructure,
341{
342    let z = Complex::<T>::new(zero.clone(), zero.clone());
343    // ProjM(1,2) Left chirality projector (( 1−γ5)/ 2 )_s1_s2
344    let c1 = Complex::<T>::new(one, zero);
345    sparse_from_logical(structure, z, |proj_m| {
346        proj_m.set(&[0, 0], c1.clone()).unwrap();
347        proj_m.set(&[1, 1], c1.clone()).unwrap();
348    })
349}
350
351pub fn proj_p_data_dirac<T, N>(
352    structure: Canonicalized<N>,
353    half: T,
354    zero: T,
355) -> Canonicalized<SparseTensor<Complex<T>, N>>
356where
357    T: Clone,
358    N: TensorStructure,
359{
360    let z = Complex::<T>::new(zero.clone(), zero.clone());
361    // ProjP(1,2) Right chirality projector (( 1+γ5)/ 2 )_s1_s2
362    let chalf = Complex::<T>::new(half, zero);
363
364    sparse_from_logical(structure, z, |proj_p| {
365        proj_p
366            .set(&[0, 0], chalf.clone())
367            .unwrap_or_else(|_| unreachable!());
368        proj_p
369            .set(&[1, 1], chalf.clone())
370            .unwrap_or_else(|_| unreachable!());
371        proj_p
372            .set(&[2, 2], chalf.clone())
373            .unwrap_or_else(|_| unreachable!());
374        proj_p
375            .set(&[3, 3], chalf.clone())
376            .unwrap_or_else(|_| unreachable!());
377
378        proj_p
379            .set(&[0, 2], chalf.clone())
380            .unwrap_or_else(|_| unreachable!());
381        proj_p
382            .set(&[1, 3], chalf.clone())
383            .unwrap_or_else(|_| unreachable!());
384        proj_p
385            .set(&[2, 0], chalf.clone())
386            .unwrap_or_else(|_| unreachable!());
387        proj_p
388            .set(&[3, 1], chalf.clone())
389            .unwrap_or_else(|_| unreachable!());
390    })
391}
392
393#[allow(clippy::similar_names)]
394pub fn proj_p_data_weyl<T, N>(
395    structure: Canonicalized<N>,
396    one: T,
397    zero: T,
398) -> Canonicalized<SparseTensor<Complex<T>, N>>
399where
400    T: Clone,
401    N: TensorStructure,
402{
403    let z = Complex::<T>::new(zero.clone(), zero.clone());
404    // ProjM(1,2) Left chirality projector (( 1−γ5)/ 2 )_s1_s2
405    let c1 = Complex::<T>::new(one, zero);
406    sparse_from_logical(structure, z, |proj_p| {
407        proj_p.set(&[2, 2], c1.clone()).unwrap();
408        proj_p.set(&[3, 3], c1.clone()).unwrap();
409    })
410}
411
412/// Fundamental SU(3) generators in the normalization `Tr(T^a T^b)=1/2 delta^{ab}`.
413///
414/// The index order follows `CS.t_strct`: adjoint, fundamental, anti-fundamental.
415pub fn su3_generator_data<N>(
416    structure: Canonicalized<N>,
417) -> Canonicalized<SparseTensor<Complex<f64>, N>>
418where
419    N: TensorStructure,
420{
421    let z = Complex::new(0., 0.);
422    let h = 0.5;
423    let s = 1. / (2. * 3_f64.sqrt());
424    sparse_from_logical(structure, z, |t| {
425        t.set(&[0, 0, 1], Complex::new(h, 0.)).unwrap();
426        t.set(&[0, 1, 0], Complex::new(h, 0.)).unwrap();
427
428        t.set(&[1, 0, 1], Complex::new(0., -h)).unwrap();
429        t.set(&[1, 1, 0], Complex::new(0., h)).unwrap();
430
431        t.set(&[2, 0, 0], Complex::new(h, 0.)).unwrap();
432        t.set(&[2, 1, 1], Complex::new(-h, 0.)).unwrap();
433
434        t.set(&[3, 0, 2], Complex::new(h, 0.)).unwrap();
435        t.set(&[3, 2, 0], Complex::new(h, 0.)).unwrap();
436
437        t.set(&[4, 0, 2], Complex::new(0., -h)).unwrap();
438        t.set(&[4, 2, 0], Complex::new(0., h)).unwrap();
439
440        t.set(&[5, 1, 2], Complex::new(h, 0.)).unwrap();
441        t.set(&[5, 2, 1], Complex::new(h, 0.)).unwrap();
442
443        t.set(&[6, 1, 2], Complex::new(0., -h)).unwrap();
444        t.set(&[6, 2, 1], Complex::new(0., h)).unwrap();
445
446        t.set(&[7, 0, 0], Complex::new(s, 0.)).unwrap();
447        t.set(&[7, 1, 1], Complex::new(s, 0.)).unwrap();
448        t.set(&[7, 2, 2], Complex::new(-2. * s, 0.)).unwrap();
449    })
450}
451
452/// SU(3) structure constants for `[T^a,T^b]=i f^{abc} T^c`.
453pub fn su3_structure_f_data<N>(structure: Canonicalized<N>) -> Canonicalized<SparseTensor<f64, N>>
454where
455    N: TensorStructure,
456{
457    fn set_antisymmetric<N>(
458        f: &mut LogicalSparseInput<f64, N>,
459        a: usize,
460        b: usize,
461        c: usize,
462        value: f64,
463    ) where
464        N: TensorStructure,
465    {
466        f.set(&[a, b, c], value).unwrap();
467        f.set(&[b, c, a], value).unwrap();
468        f.set(&[c, a, b], value).unwrap();
469        f.set(&[b, a, c], -value).unwrap();
470        f.set(&[a, c, b], -value).unwrap();
471        f.set(&[c, b, a], -value).unwrap();
472    }
473
474    sparse_from_logical(structure, 0., |f| {
475        set_antisymmetric(f, 0, 1, 2, 1.);
476        set_antisymmetric(f, 0, 3, 6, 0.5);
477        set_antisymmetric(f, 0, 4, 5, -0.5);
478        set_antisymmetric(f, 1, 3, 5, 0.5);
479        set_antisymmetric(f, 1, 4, 6, 0.5);
480        set_antisymmetric(f, 2, 3, 4, 0.5);
481        set_antisymmetric(f, 2, 5, 6, -0.5);
482        set_antisymmetric(f, 3, 4, 7, 3_f64.sqrt() / 2.);
483        set_antisymmetric(f, 5, 6, 7, 3_f64.sqrt() / 2.);
484    })
485}
486
487/// Exact Atom-backed SU(3) generators, useful for symbolic tensor libraries.
488pub fn su3_generator_data_atom<N>(
489    structure: Canonicalized<N>,
490) -> Canonicalized<SparseTensor<Atom, N>>
491where
492    N: TensorStructure,
493{
494    let half = Atom::num(1) / Atom::num(2);
495    let sqrt3 = parse_lit!(sqrt(3));
496    let t8 = sqrt3.clone() / Atom::num(6);
497    sparse_from_logical(structure, Atom::Zero, |t| {
498        t.set(&[0, 0, 1], half.clone()).unwrap();
499        t.set(&[0, 1, 0], half.clone()).unwrap();
500
501        t.set(&[1, 0, 1], -Atom::i() * half.clone()).unwrap();
502        t.set(&[1, 1, 0], Atom::i() * half.clone()).unwrap();
503
504        t.set(&[2, 0, 0], half.clone()).unwrap();
505        t.set(&[2, 1, 1], -half.clone()).unwrap();
506
507        t.set(&[3, 0, 2], half.clone()).unwrap();
508        t.set(&[3, 2, 0], half.clone()).unwrap();
509
510        t.set(&[4, 0, 2], -Atom::i() * half.clone()).unwrap();
511        t.set(&[4, 2, 0], Atom::i() * half.clone()).unwrap();
512
513        t.set(&[5, 1, 2], half.clone()).unwrap();
514        t.set(&[5, 2, 1], half.clone()).unwrap();
515
516        t.set(&[6, 1, 2], -Atom::i() * half).unwrap();
517        t.set(&[6, 2, 1], Atom::i() / Atom::num(2)).unwrap();
518
519        t.set(&[7, 0, 0], t8.clone()).unwrap();
520        t.set(&[7, 1, 1], t8.clone()).unwrap();
521        t.set(&[7, 2, 2], -sqrt3 / Atom::num(3)).unwrap();
522    })
523}
524
525/// Exact Atom-backed SU(3) structure constants.
526pub fn su3_structure_f_data_atom<N>(
527    structure: Canonicalized<N>,
528) -> Canonicalized<SparseTensor<Atom, N>>
529where
530    N: TensorStructure,
531{
532    fn set_antisymmetric<N>(
533        f: &mut LogicalSparseInput<Atom, N>,
534        a: usize,
535        b: usize,
536        c: usize,
537        value: Atom,
538    ) where
539        N: TensorStructure,
540    {
541        f.set(&[a, b, c], value.clone()).unwrap();
542        f.set(&[b, c, a], value.clone()).unwrap();
543        f.set(&[c, a, b], value.clone()).unwrap();
544        f.set(&[b, a, c], -value.clone()).unwrap();
545        f.set(&[a, c, b], -value.clone()).unwrap();
546        f.set(&[c, b, a], -value).unwrap();
547    }
548
549    let half = Atom::num(1) / Atom::num(2);
550    let sqrt3_half = parse_lit!(sqrt(3)) / Atom::num(2);
551
552    sparse_from_logical(structure, Atom::Zero, |f| {
553        set_antisymmetric(f, 0, 1, 2, Atom::num(1));
554        set_antisymmetric(f, 0, 3, 6, half.clone());
555        set_antisymmetric(f, 0, 4, 5, -half.clone());
556        set_antisymmetric(f, 1, 3, 5, half.clone());
557        set_antisymmetric(f, 1, 4, 6, half.clone());
558        set_antisymmetric(f, 2, 3, 4, half.clone());
559        set_antisymmetric(f, 2, 5, 6, -half);
560        set_antisymmetric(f, 3, 4, 7, sqrt3_half.clone());
561        set_antisymmetric(f, 5, 6, 7, sqrt3_half);
562    })
563}
564
565#[allow(clippy::similar_names)]
566pub fn sigma_data<T, N>(
567    structure: Canonicalized<N>,
568    one: T,
569    zero: T,
570) -> Canonicalized<SparseTensor<Complex<T>, N>>
571where
572    T: Clone + Neg<Output = T>,
573    N: TensorStructure,
574{
575    let z = Complex::<T>::new(zero.clone(), zero.clone());
576    let c1 = Complex::<T>::new(one.clone(), zero.clone());
577    let cn1 = Complex::<T>::new(-one.clone(), zero.clone());
578    let ci = Complex::<T>::new(zero.clone(), one.clone());
579    let cni = Complex::<T>::new(zero.clone(), -one.clone());
580
581    sparse_from_logical(structure, z, |sigma| {
582        sigma.set(&[0, 2, 0, 1], c1.clone()).unwrap();
583        sigma.set(&[0, 2, 3, 0], c1.clone()).unwrap();
584        sigma.set(&[0, 3, 1, 2], c1.clone()).unwrap();
585        sigma.set(&[1, 0, 2, 2], c1.clone()).unwrap();
586        sigma.set(&[1, 1, 1, 2], c1.clone()).unwrap();
587        sigma.set(&[1, 3, 0, 2], c1.clone()).unwrap();
588        sigma.set(&[2, 2, 1, 0], c1.clone()).unwrap();
589        sigma.set(&[2, 2, 2, 1], c1.clone()).unwrap();
590        sigma.set(&[2, 3, 3, 2], c1.clone()).unwrap();
591        sigma.set(&[3, 0, 0, 2], c1.clone()).unwrap();
592        sigma.set(&[3, 3, 2, 2], c1.clone()).unwrap();
593        sigma.set(&[3, 1, 3, 2], c1.clone()).unwrap();
594        sigma.set(&[0, 1, 3, 0], ci.clone()).unwrap();
595        sigma.set(&[0, 3, 1, 1], ci.clone()).unwrap();
596        sigma.set(&[0, 3, 2, 0], ci.clone()).unwrap();
597        sigma.set(&[1, 0, 3, 3], ci.clone()).unwrap();
598        sigma.set(&[1, 1, 0, 3], ci.clone()).unwrap();
599        sigma.set(&[1, 1, 2, 0], ci.clone()).unwrap();
600        sigma.set(&[2, 1, 1, 0], ci.clone()).unwrap();
601        sigma.set(&[2, 3, 0, 0], ci.clone()).unwrap();
602        sigma.set(&[2, 3, 3, 1], ci.clone()).unwrap();
603        sigma.set(&[3, 0, 1, 3], ci.clone()).unwrap();
604        sigma.set(&[3, 1, 0, 0], ci.clone()).unwrap();
605        sigma.set(&[3, 1, 2, 3], ci.clone()).unwrap();
606        sigma.set(&[0, 0, 3, 2], cn1.clone()).unwrap();
607        sigma.set(&[0, 1, 0, 2], cn1.clone()).unwrap();
608        sigma.set(&[0, 2, 1, 3], cn1.clone()).unwrap();
609        sigma.set(&[1, 2, 0, 3], cn1.clone()).unwrap();
610        sigma.set(&[1, 2, 1, 1], cn1.clone()).unwrap();
611        sigma.set(&[1, 2, 2, 0], cn1.clone()).unwrap();
612        sigma.set(&[2, 0, 1, 2], cn1.clone()).unwrap();
613        sigma.set(&[2, 1, 2, 2], cn1.clone()).unwrap();
614        sigma.set(&[2, 2, 3, 3], cn1.clone()).unwrap();
615        sigma.set(&[3, 2, 0, 0], cn1.clone()).unwrap();
616        sigma.set(&[3, 2, 2, 3], cn1.clone()).unwrap();
617        sigma.set(&[3, 2, 3, 1], cn1.clone()).unwrap();
618        sigma.set(&[0, 0, 2, 3], cni.clone()).unwrap();
619        sigma.set(&[0, 0, 3, 1], cni.clone()).unwrap();
620        sigma.set(&[0, 1, 1, 3], cni.clone()).unwrap();
621        sigma.set(&[1, 0, 2, 1], cni.clone()).unwrap();
622        sigma.set(&[1, 3, 0, 1], cni.clone()).unwrap();
623        sigma.set(&[1, 3, 3, 0], cni.clone()).unwrap();
624        sigma.set(&[2, 0, 0, 3], cni.clone()).unwrap();
625        sigma.set(&[2, 0, 1, 1], cni.clone()).unwrap();
626        sigma.set(&[2, 1, 3, 3], cni.clone()).unwrap();
627        sigma.set(&[3, 0, 0, 1], cni.clone()).unwrap();
628        sigma.set(&[3, 3, 1, 0], cni.clone()).unwrap();
629        sigma.set(&[3, 3, 2, 1], cni.clone()).unwrap();
630    })
631}
632
633pub fn hep_lib<Aind: AbsInd, T: TensorLibraryData + Clone + Default>(
634    one: T,
635    zero: T,
636) -> TensorLibrary<MixedTensor<T, ExplicitKey<Aind>>, Aind>
637where
638{
639    let mut weyl = TensorLibrary::new();
640    initialize();
641    weyl.update_ids();
642
643    let gamma_key = gamma_data_weyl(AGS.gamma_strct::<Aind>(4), one.clone(), zero.clone())
644        .map_canonical(Into::into);
645    // println!("layout{:?}", gamma_key.layout());
646    weyl.insert_explicit(gamma_key);
647    let gamma_conj_key =
648        gamma_conj_data_weyl(AGS.gamma_conj_strct::<Aind>(4), one.clone(), zero.clone())
649            .map_canonical(Into::into);
650    // println!("layout{:?}", gamma_key.layout());
651    weyl.insert_explicit(gamma_conj_key);
652    let gamma_adj_key =
653        gamma_adj_data_weyl(AGS.gamma_adj_strct::<Aind>(4), one.clone(), zero.clone())
654            .map_canonical(Into::into);
655    // println!("layout{:?}", gamma_key.layout());
656    weyl.insert_explicit(gamma_adj_key);
657    let gamma0_key = gamma0_weyl(AGS.gamma0_strct::<Aind>(4), one.clone(), zero.clone())
658        .map_canonical(Into::into);
659    // println!("layout{:?}", gamma_key.layout());
660    weyl.insert_explicit(gamma0_key);
661
662    let gamma5_key = gamma5_weyl_data(AGS.gamma5_strct::<Aind>(4), one.clone(), zero.clone())
663        .map_canonical(Into::into);
664    weyl.insert_explicit(gamma5_key);
665
666    let projm_key = proj_m_data_weyl(AGS.projm_strct::<Aind>(4), one.clone(), zero.clone())
667        .map_canonical(Into::into);
668    weyl.insert_explicit(projm_key);
669
670    let projp_key = proj_p_data_weyl(AGS.projp_strct::<Aind>(4), one.clone(), zero.clone())
671        .map_canonical(Into::into);
672    weyl.insert_explicit(projp_key);
673
674    weyl
675}
676
677pub fn insert_su3_color_tensors<Aind: AbsInd>(
678    lib: &mut TensorLibrary<MixedTensor<f64, ExplicitKey<Aind>>, Aind>,
679) {
680    initialize();
681
682    let t_key = su3_generator_data(CS.t_strct::<Aind>(3, 8)).map_canonical(Into::into);
683    lib.insert_explicit(t_key);
684
685    let f_key = su3_structure_f_data(CS.f_strct::<Aind>(8)).map_canonical(Into::into);
686    lib.insert_explicit(f_key);
687}
688
689pub fn hep_lib_su3<Aind: AbsInd>() -> TensorLibrary<MixedTensor<f64, ExplicitKey<Aind>>, Aind> {
690    let mut lib = hep_lib(1., 0.);
691    insert_su3_color_tensors(&mut lib);
692    lib
693}
694
695pub fn hep_lib_atom<Aind: AbsInd, T>() -> TensorLibrary<T, Aind>
696where
697    T: LibraryTensor<Structure = ExplicitKey<Aind>>
698        + SetTensorData<SetData = <T as LibraryTensor>::Data>
699        + Clone
700        + From<ParamTensor<ExplicitKey<Aind>>>,
701    <T as LibraryTensor>::Data: TensorLibraryData,
702{
703    let mut weyl = TensorLibrary::new();
704    initialize();
705    weyl.update_ids();
706
707    let one = Atom::one();
708    let zero = Atom::Zero;
709
710    let gamma_key = gamma_data_weyl(AGS.gamma_strct::<Aind>(4), one.clone(), zero.clone())
711        .map_canonical(|tensor| {
712            ParamTensor::param(tensor.map_data(|a| a.re + a.im * Atom::i()).into()).into()
713        });
714    // println!("layout{:?}", gamma_key.layout());
715    weyl.insert_explicit(gamma_key);
716    let gamma_conj_key =
717        gamma_conj_data_weyl(AGS.gamma_conj_strct::<Aind>(4), one.clone(), zero.clone())
718            .map_canonical(|tensor| {
719                ParamTensor::param(tensor.map_data(|a| a.re + a.im * Atom::i()).into()).into()
720            });
721    // println!("layout{:?}", gamma_key.layout());
722    weyl.insert_explicit(gamma_conj_key);
723    let gamma_adj_key =
724        gamma_adj_data_weyl(AGS.gamma_adj_strct::<Aind>(4), one.clone(), zero.clone())
725            .map_canonical(|tensor| {
726                ParamTensor::param(tensor.map_data(|a| a.re + a.im * Atom::i()).into()).into()
727            });
728    // println!("layout{:?}", gamma_key.layout());
729    weyl.insert_explicit(gamma_adj_key);
730    let gamma0_key = gamma0_weyl(AGS.gamma0_strct::<Aind>(4), one.clone(), zero.clone())
731        .map_canonical(|tensor| {
732            ParamTensor::param(tensor.map_data(|a| a.re + a.im * Atom::i()).into()).into()
733        });
734    // println!("layout{:?}", gamma_key.layout());
735    weyl.insert_explicit(gamma0_key);
736
737    let gamma5_key = gamma5_weyl_data(AGS.gamma5_strct::<Aind>(4), one.clone(), zero.clone())
738        .map_canonical(|tensor| {
739            ParamTensor::param(tensor.map_data(|a| a.re + a.im * Atom::i()).into()).into()
740        });
741    weyl.insert_explicit(gamma5_key);
742
743    let projm_key = proj_m_data_weyl(AGS.projm_strct::<Aind>(4), one.clone(), zero.clone())
744        .map_canonical(|tensor| {
745            ParamTensor::param(tensor.map_data(|a| a.re + a.im * Atom::i()).into()).into()
746        });
747    weyl.insert_explicit(projm_key);
748
749    let projp_key = proj_p_data_weyl(AGS.projp_strct::<Aind>(4), one.clone(), zero.clone())
750        .map_canonical(|tensor| {
751            ParamTensor::param(tensor.map_data(|a| a.re + a.im * Atom::i()).into()).into()
752        });
753    weyl.insert_explicit(projp_key);
754
755    let color_t_key = su3_generator_data_atom(CS.t_strct::<Aind>(3, 8))
756        .map_canonical(|tensor| ParamTensor::param(tensor.into()).into());
757    weyl.insert_explicit(color_t_key);
758
759    let color_f_key = su3_structure_f_data_atom(CS.f_strct::<Aind>(8))
760        .map_canonical(|tensor| ParamTensor::param(tensor.into()).into());
761    weyl.insert_explicit(color_f_key);
762
763    weyl
764}
765
766pub type HepTensor<Aind> = MixedTensor<f64, ShadowedStructure<Aind>>;
767
768pub type HepNet<Aind> =
769    Network<NetworkStore<HepTensor<Aind>, Atom>, ExplicitKey<Aind>, Symbol, Aind>;
770
771pub static HEP_LIB: LazyLock<
772    TensorLibrary<MixedTensor<f64, ExplicitKey<AbstractIndex>>, AbstractIndex>,
773> = LazyLock::new(hep_lib_su3);
774
775pub static FUN_LIB: LazyLock<
776    SymbolLib<RealOrComplexTensor<f64, ShadowedStructure<AbstractIndex>>, PanicMissingConcrete>,
777> = LazyLock::new(|| {
778    let mut lib = PanicMissingConcrete::new_lib();
779    lib.insert(INBUILTS.conj, |a| match a {
780        RealOrComplexTensor::Complex(c) => RealOrComplexTensor::Complex(c.map_data(|x| x.conj())),
781        RealOrComplexTensor::Real(r) => RealOrComplexTensor::Real(r),
782    });
783    lib.insert_scalar_fallible(INBUILTS.conj, |scalar| Ok(scalar.spenso_conj()));
784    lib
785});
786
787#[cfg(test)]
788mod tests {
789
790    use spenso::{
791        network::{
792            ExecutionResult, Network, Sequential, SingleSmallestDegree, SmallestDegree,
793            SmallestDegreeIter, Steps,
794            parsing::{ParseSettings, ShadowedStructure, StrictTensorFilter},
795            store::NetworkStore,
796        },
797        structure::{HasStructure, abstract_index::AbstractIndex},
798        tensors::data::GetTensorData,
799    };
800    use symbolica::{
801        atom::{Atom, Symbol},
802        parse, parse_lit,
803    };
804
805    use super::*;
806
807    #[test]
808    fn su3_color_traces_are_independent_of_contraction_order() {
809        use spenso::network::{MinIntermediateCost, MinResultRank, library::function_lib::Wrap};
810
811        initialize();
812        type Net = Network<
813            NetworkStore<ParamTensor<ShadowedStructure<AbstractIndex>>, Atom>,
814            ExplicitKey<AbstractIndex>,
815            Symbol,
816        >;
817        let lib: TensorLibrary<ParamTensor<ExplicitKey<AbstractIndex>>, AbstractIndex> =
818            hep_lib_atom();
819        let functions: SymbolLib<ParamTensor<ShadowedStructure<AbstractIndex>>, Wrap> =
820            Wrap::new_lib();
821        for (expression, expected) in [
822            // The two crossed cyclic orderings used to give -1/6 rather than -2/3.
823            (
824                "g(coad(8,a),coad(8,b))*g(coad(8,c),coad(8,d))*trace(cof(3),t(coad(8,a),in,out),t(coad(8,c),in,out),t(coad(8,b),in,out),t(coad(8,d),in,out))",
825                parse!("-2/3"),
826            ),
827            (
828                "1/8*(2+g(coad(8,a),coad(8,b))*g(coad(8,c),coad(8,d))*trace(cof(3),sym(t(coad(8,a),in,out),t(coad(8,b),in,out),t(coad(8,c),in,out),t(coad(8,d),in,out))))",
829                parse!("2/3"),
830            ),
831        ] {
832            let expression = parse!(expression, default_namespace = "spenso");
833            let original =
834                Net::try_from_view(expression.as_view(), &lib, &ParseSettings::default()).unwrap();
835            for prepare in [false, true] {
836                let mut prepared = original.clone();
837                if prepare {
838                    prepared.graph.contract_ready_sum_boundaries();
839                }
840                macro_rules! check {
841                    ($strategy:ty) => {{
842                        let mut network = prepared.clone();
843                        network
844                            .execute::<Sequential, $strategy, _, _, _>(&lib, &functions)
845                            .unwrap();
846                        let ExecutionResult::Val(actual) = network.result_scalar().unwrap() else {
847                            panic!("expected a scalar color trace");
848                        };
849                        assert_eq!(
850                            actual.as_ref(),
851                            &expected,
852                            "strategy={}, prepare={prepare}",
853                            stringify!($strategy)
854                        );
855                    }};
856                }
857                check!(SmallestDegree);
858                check!(MinIntermediateCost);
859                check!(MinResultRank);
860            }
861        }
862    }
863
864    #[test]
865    fn scalar_conjugation_execution_preserves_exact_coefficients() {
866        initialize();
867        let mut network =
868            HepNet::<AbstractIndex>::from_scalar(parse!("1/3 + 2i/7")).fun(INBUILTS.conj);
869        network
870            .execute::<Sequential, SmallestDegree, _, _, _>(&*HEP_LIB, &*FUN_LIB)
871            .unwrap();
872        let ExecutionResult::Val(conjugated) = network.result_scalar().unwrap() else {
873            panic!("conjugation should produce a scalar value")
874        };
875
876        assert_eq!(conjugated.into_owned(), parse!("1/3 - 2i/7"));
877    }
878
879    #[test]
880    fn dirac_gamma_data_uses_storage_order() {
881        initialize();
882        let gamma =
883            gamma_data_dirac(AGS.gamma_strct::<AbstractIndex>(4), 1_i32, 0_i32).into_canonical();
884
885        // The final coordinate is the Minkowski component. These are the
886        // diagonal entries of gamma^0 in the Dirac basis.
887        assert_eq!(*gamma.get_ref([0, 0, 0]).unwrap(), Complex::new(1, 0));
888        assert_eq!(*gamma.get_ref([1, 1, 0]).unwrap(), Complex::new(1, 0));
889        assert_eq!(*gamma.get_ref([2, 2, 0]).unwrap(), Complex::new(-1, 0));
890        assert_eq!(*gamma.get_ref([3, 3, 0]).unwrap(), Complex::new(-1, 0));
891    }
892
893    #[test]
894    fn simple_scalar() {
895        initialize();
896        let gamma = AGS.gamma_strct(4);
897        let _a = HEP_LIB.get(gamma.canonical()).unwrap();
898
899        let expr = parse!("gamma(bis(4,l_5),bis(4,l_4),mink(4,l_4))*gamma(bis(4,l_6),bis(4,l_5),mink(4,l_4))*gamma(bis(4,l_4),bis(4,l_6),mink(4,l_5))*p(mink(4,l_5))
900            ",default_namespace="spenso");
901        // let expr = parse!(
902        // "gamma(bis(4,l_4),bis(4,l_6),mink(4,l_5))*p(mink(4,l_5))
903        // ",
904        // "spenso"
905        // );
906        // println!("{}", expr);
907
908        let mut net = Network::<
909            NetworkStore<MixedTensor<f64, ShadowedStructure<AbstractIndex>>, Atom>,
910            _,
911            Symbol,
912        >::try_from_view(
913            expr.as_view(),
914            &*HEP_LIB,
915            &ParseSettings::default().with_strict_tensor_filter(StrictTensorFilter::ContainsReps),
916        )
917        .unwrap();
918
919        println!(
920            "{}",
921            net.dot_display_impl(
922                |a| a.to_string(),
923                |a| Some(format!("{}", a.global_name.unwrap())),
924                |a| a.structure().global_name.unwrap().to_string(),
925                |a| a.to_string()
926            )
927        );
928
929        net.execute::<Steps<1>, SmallestDegreeIter<1>, _, _, _>(&*HEP_LIB, &*FUN_LIB)
930            .unwrap();
931        println!(
932            "{}",
933            net.dot_display_impl(
934                |a| a.to_string(),
935                |a| Some(format!("{}", a.global_name?)),
936                |a| a
937                    .structure()
938                    .global_name
939                    .map(|a| a.to_string())
940                    .unwrap_or("".to_string()),
941                |a| a.to_string()
942            )
943        );
944        net.execute::<Steps<1>, SmallestDegreeIter<2>, _, _, _>(&*HEP_LIB, &*FUN_LIB)
945            .unwrap();
946        println!(
947            "{}",
948            net.dot_display_impl(
949                |a| a.to_string(),
950                |a| Some(format!("{}", a.global_name?)),
951                |a| a
952                    .structure()
953                    .global_name
954                    .map(|a| a.to_string())
955                    .unwrap_or("".to_string()),
956                |a| a.to_string()
957            )
958        );
959
960        println!(
961            "{}",
962            net.dot_display_impl(
963                |a| a.to_string(),
964                |_| None,
965                |a| a.to_string(),
966                |a| a.to_string()
967            )
968        );
969        // if let ExecutionResult::Val(TensorOrScalarOrKey::Tensor { tensor, .. }) =
970        //     net.result().unwrap()
971        // {
972        //     // println!("YaY:{}", (&expr - &tensor.expression).expand());
973        //     // assert_eq!(expr, tensor.expression);
974        // } else {
975        //     panic!("Not tensor")
976        // }
977    }
978
979    #[test]
980    // #[should_panic]
981    fn parse_problem() {
982        initialize();
983        let gamma = AGS.gamma_strct(4);
984        let _a = HEP_LIB.get(gamma.canonical()).unwrap();
985
986        let expr = parse_lit!(
987            (-1 * G
988                ^ 3 * P(0, mink(4, 0))
989                    * P(2, mink(4, 26))
990                    * gamma(bis(4, 3), bis(4, 7), mink(4, 4))
991                    * gamma(bis(4, 7), bis(4, 6), mink(4, 1))
992                    * gamma(bis(4, 6), bis(4, 2), mink(4, 26))
993                    + -1 * G
994                ^ 3 * P(0, mink(4, 26))
995                    * P(1, mink(4, 1))
996                    * gamma(bis(4, 3), bis(4, 7), mink(4, 4))
997                    * gamma(bis(4, 7), bis(4, 6), mink(4, 0))
998                    * gamma(bis(4, 6), bis(4, 2), mink(4, 26))
999                    + -1 * G
1000                ^ 3 * P(0, mink(4, 26))
1001                    * P(1, mink(4, 5))
1002                    * g(mink(4, 0), mink(4, 1))
1003                    * gamma(bis(4, 3), bis(4, 7), mink(4, 4))
1004                    * gamma(bis(4, 7), bis(4, 6), mink(4, 5))
1005                    * gamma(bis(4, 6), bis(4, 2), mink(4, 26))
1006                    + -1 * G
1007                ^ 3 * P(0, mink(4, 5))
1008                    * P(2, mink(4, 26))
1009                    * g(mink(4, 0), mink(4, 1))
1010                    * gamma(bis(4, 3), bis(4, 7), mink(4, 4))
1011                    * gamma(bis(4, 6), bis(4, 2), mink(4, 26))
1012                    * gamma(bis(4, 7), bis(4, 6), mink(4, 5))
1013                    + -1 * G
1014                ^ 3 * P(1, mink(4, 1))
1015                    * P(1, mink(4, 26))
1016                    * gamma(bis(4, 3), bis(4, 7), mink(4, 4))
1017                    * gamma(bis(4, 6), bis(4, 2), mink(4, 26))
1018                    * gamma(bis(4, 7), bis(4, 6), mink(4, 0))
1019                    + -1 * G
1020                ^ 3 * P(1, mink(4, 26))
1021                    * P(1, mink(4, 5))
1022                    * g(mink(4, 0), mink(4, 1))
1023                    * gamma(bis(4, 3), bis(4, 7), mink(4, 4))
1024                    * gamma(bis(4, 6), bis(4, 2), mink(4, 26))
1025                    * gamma(bis(4, 7), bis(4, 6), mink(4, 5))
1026                    + -2 * G
1027                ^ 3 * P(0, mink(4, 1))
1028                    * P(0, mink(4, 26))
1029                    * gamma(bis(4, 3), bis(4, 7), mink(4, 4))
1030                    * gamma(bis(4, 6), bis(4, 2), mink(4, 26))
1031                    * gamma(bis(4, 7), bis(4, 6), mink(4, 0))
1032                    + -2 * G
1033                ^ 3 * P(0, mink(4, 1))
1034                    * P(1, mink(4, 26))
1035                    * gamma(bis(4, 3), bis(4, 7), mink(4, 4))
1036                    * gamma(bis(4, 6), bis(4, 2), mink(4, 26))
1037                    * gamma(bis(4, 7), bis(4, 6), mink(4, 0))
1038                    + -2 * G
1039                ^ 3 * P(0, mink(4, 5))
1040                    * Q(0, mink(4, 5))
1041                    * g(mink(4, 0), mink(4, 1))
1042                    * gamma(bis(4, 3), bis(4, 2), mink(4, 4))
1043                    + -2 * G
1044                ^ 3 * P(1, mink(4, 0))
1045                    * P(1, mink(4, 1))
1046                    * gamma(bis(4, 3), bis(4, 2), mink(4, 4))
1047                    + -2 * G
1048                ^ 3 * P(1, mink(4, 0))
1049                    * P(2, mink(4, 26))
1050                    * gamma(bis(4, 3), bis(4, 7), mink(4, 4))
1051                    * gamma(bis(4, 6), bis(4, 2), mink(4, 26))
1052                    * gamma(bis(4, 7), bis(4, 6), mink(4, 1))
1053                    + -2 * G
1054                ^ 3 * P(1, mink(4, 1))
1055                    * P(2, mink(4, 0))
1056                    * gamma(bis(4, 3), bis(4, 2), mink(4, 4))
1057                    + -2 * G
1058                ^ 3 * P(1, mink(4, 5))
1059                    * P(2, mink(4, 5))
1060                    * g(mink(4, 0), mink(4, 1))
1061                    * gamma(bis(4, 3), bis(4, 2), mink(4, 4))
1062                    + -4 * G
1063                ^ 3 * P(0, mink(4, 1))
1064                    * P(2, mink(4, 0))
1065                    * gamma(bis(4, 3), bis(4, 2), mink(4, 4))
1066                    + 2 * G
1067                ^ 3 * P(0, mink(4, 0))
1068                    * P(0, mink(4, 1))
1069                    * gamma(bis(4, 3), bis(4, 2), mink(4, 4))
1070                    + 2 * G
1071                ^ 3 * P(0, mink(4, 0))
1072                    * P(2, mink(4, 1))
1073                    * gamma(bis(4, 3), bis(4, 2), mink(4, 4))
1074                    + 2 * G
1075                ^ 3 * P(0, mink(4, 1))
1076                    * P(2, mink(4, 26))
1077                    * gamma(bis(4, 3), bis(4, 7), mink(4, 4))
1078                    * gamma(bis(4, 6), bis(4, 2), mink(4, 26))
1079                    * gamma(bis(4, 7), bis(4, 6), mink(4, 0))
1080                    + 2 * G
1081                ^ 3 * P(0, mink(4, 26))
1082                    * P(1, mink(4, 0))
1083                    * gamma(bis(4, 3), bis(4, 7), mink(4, 4))
1084                    * gamma(bis(4, 6), bis(4, 2), mink(4, 26))
1085                    * gamma(bis(4, 7), bis(4, 6), mink(4, 1))
1086                    + 2 * G
1087                ^ 3 * P(0, mink(4, 5))
1088                    * P(2, mink(4, 5))
1089                    * g(mink(4, 0), mink(4, 1))
1090                    * gamma(bis(4, 3), bis(4, 2), mink(4, 4))
1091                    + 2 * G
1092                ^ 3 * P(1, mink(4, 0))
1093                    * P(1, mink(4, 26))
1094                    * gamma(bis(4, 3), bis(4, 7), mink(4, 4))
1095                    * gamma(bis(4, 6), bis(4, 2), mink(4, 26))
1096                    * gamma(bis(4, 7), bis(4, 6), mink(4, 1))
1097                    + 2 * G
1098                ^ 3 * P(1, mink(4, i))
1099                ^ 2 * g(mink(4, 0), mink(4, 1)) * gamma(bis(4, 3), bis(4, 2), mink(4, 4)) + G
1100                ^ 3 * P(1, mink(4, 1))
1101                    * P(2, mink(4, 26))
1102                    * gamma(bis(4, 3), bis(4, 7), mink(4, 4))
1103                    * gamma(bis(4, 6), bis(4, 2), mink(4, 26))
1104                    * gamma(bis(4, 7), bis(4, 6), mink(4, 0))),
1105            default_namespace = "spenso"
1106        );
1107        // println!("{}", expr);
1108
1109        let mut net = Network::<
1110            NetworkStore<MixedTensor<f64, ShadowedStructure<AbstractIndex>>, Atom>,
1111            _,
1112            Symbol,
1113        >::try_from_view(
1114            expr.as_view(),
1115            &*HEP_LIB,
1116            &ParseSettings::default().with_strict_tensor_filter(StrictTensorFilter::ContainsReps),
1117        )
1118        .unwrap();
1119
1120        net.merge_ops();
1121        println!(
1122            "{}",
1123            net.dot_display_impl(
1124                |a| a.to_string(),
1125                |a| Some(format!("{}", a.global_name.unwrap())),
1126                |a| a.structure().global_name.unwrap().to_string(),
1127                |a| a.to_string()
1128            )
1129        );
1130
1131        // net.validate();
1132        net.execute::<Steps<1>, SingleSmallestDegree<true>, _, _, _>(&*HEP_LIB, &(*FUN_LIB))
1133            .unwrap();
1134        // net.validate();
1135        // net.execute::<Steps<1>, SmallestDegree, _, _>(&*HEP_LIB);
1136        // net.validate();
1137        // net.execute::<Steps<1>, SmallestDegree, _, _>(&*HEP_LIB);
1138        // net.validate();
1139        // net.execute::<Steps<1>, SmallestDegree, _, _>(&*HEP_LIB);
1140        // net.validate();
1141        // net.execute::<Steps<1>, SmallestDegree, _, _>(&*HEP_LIB);
1142        // net.validate();
1143        // net.execute::<Steps<1>, SmallestDegree, _, _>(&*HEP_LIB);
1144        // net.validate();
1145        // net.execute::<Steps<1>, SmallestDegree, _, _>(&*HEP_LIB);
1146        // net.validate();
1147        // net.execute::<Steps<1>, SmallestDegree, _, _>(&*HEP_LIB);
1148        // net.validate();
1149        // net.execute::<Steps<1>, SmallestDegree, _, _>(&*HEP_LIB);
1150        // net.validate();
1151        // net.execute::<Steps<1>, SmallestDegree, _, _>(&*HEP_LIB);
1152        // net.validate();
1153        // net.execute::<Steps<1>, ContractScalars, _, _>(&*HEP_LIB);
1154        // net.execute::<Steps<1>, SmallestDegree, _, _>(&*HEP_LIB);
1155        // net.execute::<StepsDebug<1>, SingleSmallestDegree<true>, _, _>(&*HEP_LIB);
1156        // net.execute::<Steps<1>, ContractScalars, _, _>(&*HEP_LIB);
1157
1158        //     .unwrap();
1159        // net.execute::<Steps<14>, SingleSmallestDegree<false>, _, _>(&*HEP_LIB)
1160        //     .unwrap();
1161        // net.execute::<Steps<1>, SingleSmallestDegree<true>, _, _>(&*HEP_LIB)
1162        //     .unwrap();
1163        // // net.execute::<Sequential, SmallestDegree, _, _>(&*HEP_LIB)
1164        //     .unwrap();
1165        // println!(
1166        //     "{}",
1167        //     net.dot_display_impl(|a| a.to_string(), |_| None, |a| a.to_string())
1168        // );
1169
1170        println!(
1171            "{}",
1172            net.dot_display_impl(
1173                |a| a.to_string(),
1174                |_| None,
1175                |a| a.structure().to_string().replace('\n', "\\n"),
1176                |a| a.to_string()
1177            )
1178        );
1179        // if let ExecutionResult::Val(TensorOrScalarOrKey::Tensor { tensor, .. }) =
1180        //     net.result().unwrap()
1181        // {
1182        //     // println!("YaY:{}", (&expr - &tensor.expression).expand());
1183        //     // assert_eq!(expr, tensor.expression);
1184        // } else {
1185        //     panic!("Not tensor")
1186        // }
1187    }
1188
1189    #[test]
1190    fn transpose_test() {}
1191}