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 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 })
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 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 })
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 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 })
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 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 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 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 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 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
412pub 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
452pub 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
487pub 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
525pub 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 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 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 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 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 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 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 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 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 (
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 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 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 }
978
979 #[test]
980 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 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.execute::<Steps<1>, SingleSmallestDegree<true>, _, _, _>(&*HEP_LIB, &(*FUN_LIB))
1133 .unwrap();
1134 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 }
1188
1189 #[test]
1190 fn transpose_test() {}
1191}