1use scirs2_core::ndarray::{Array1, Array4};
9use scirs2_core::random::{thread_rng, Rng};
10use scirs2_core::Complex64;
11use serde::{Deserialize, Serialize};
12use std::collections::{HashMap, VecDeque};
13use std::f64::consts::PI;
14
15use crate::error::{Result, SimulatorError};
16use crate::scirs2_integration::SciRS2Backend;
17use scirs2_core::random::prelude::*;
18
19#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct QFTConfig {
22 pub spacetime_dimensions: usize,
24 pub lattice_size: Vec<usize>,
26 pub lattice_spacing: f64,
28 pub field_theory: FieldTheoryType,
30 pub boundary_conditions: QFTBoundaryConditions,
32 pub temperature: f64,
34 pub chemical_potential: f64,
36 pub coupling_constants: HashMap<String, f64>,
38 pub gauge_invariant: bool,
40 pub renormalization_scheme: RenormalizationScheme,
42 pub mc_steps: usize,
44}
45
46impl Default for QFTConfig {
47 fn default() -> Self {
48 let mut couplings = HashMap::new();
49 couplings.insert("g".to_string(), 0.1);
50 couplings.insert("lambda".to_string(), 0.01);
51
52 Self {
53 spacetime_dimensions: 4,
54 lattice_size: vec![16, 16, 16, 32],
55 lattice_spacing: 1.0,
56 field_theory: FieldTheoryType::ScalarPhi4,
57 boundary_conditions: QFTBoundaryConditions::Periodic,
58 temperature: 0.0,
59 chemical_potential: 0.0,
60 coupling_constants: couplings,
61 gauge_invariant: true,
62 renormalization_scheme: RenormalizationScheme::DimensionalRegularization,
63 mc_steps: 10000,
64 }
65 }
66}
67
68#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
70pub enum FieldTheoryType {
71 ScalarPhi4,
73 QED,
75 YangMills,
77 QCD,
79 ChiralFermions,
81 Higgs,
83 StandardModel,
85 NonLinearSigma,
87 GrossNeveu,
89 SineGordon,
91 Custom,
93}
94
95#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
97pub enum QFTBoundaryConditions {
98 Periodic,
100 Antiperiodic,
102 Open,
104 Twisted,
106 Mixed,
108}
109
110#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
112pub enum RenormalizationScheme {
113 DimensionalRegularization,
115 PauliVillars,
117 MomentumCutoff,
119 ZetaFunction,
121 MSBar,
123 OnShell,
125}
126
127#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
129pub enum FieldOperatorType {
130 Scalar,
132 Spinor,
134 Vector,
136 Tensor,
138 Creation,
140 Annihilation,
142}
143
144#[derive(Debug, Clone, Serialize, Deserialize)]
146pub struct FieldOperator {
147 pub field_type: FieldOperatorType,
149 pub position: Vec<f64>,
151 pub momentum: Option<Vec<f64>>,
153 pub component: usize,
155 pub time_ordering: TimeOrdering,
157 pub normal_ordering: bool,
159}
160
161#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
163pub enum TimeOrdering {
164 TimeOrdered,
166 NormalOrdered,
168 AntiTimeOrdered,
170 Causal,
172}
173
174#[derive(Debug, Clone, Serialize, Deserialize)]
176pub struct PathIntegralConfig {
177 pub time_slices: usize,
179 pub time_step: f64,
181 pub action_type: ActionType,
183 pub mc_algorithm: MonteCarloAlgorithm,
185 pub importance_sampling: bool,
187 pub target_acceptance_rate: f64,
189}
190
191#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
193pub enum ActionType {
194 Euclidean,
196 Minkowski,
198 WickRotated,
200 Effective,
202 Wilson,
204 Improved,
206}
207
208#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
210pub enum MonteCarloAlgorithm {
211 Metropolis,
213 Langevin,
215 HybridMonteCarlo,
217 Cluster,
219 Worm,
221 Multicanonical,
223}
224
225#[derive(Debug, Clone, Serialize, Deserialize)]
227pub struct GaugeFieldConfig {
228 pub gauge_group: GaugeGroup,
230 pub num_colors: usize,
232 pub gauge_coupling: f64,
234 pub gauge_fixing: GaugeFixing,
236 pub wilson_loops: Vec<WilsonLoop>,
238}
239
240#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
242pub enum GaugeGroup {
243 U1,
245 SU(usize),
247 SO(usize),
249 Sp(usize),
251}
252
253#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
255pub enum GaugeFixing {
256 Coulomb,
258 Landau,
260 Axial,
262 Temporal,
264 None,
266}
267
268#[derive(Debug, Clone, Serialize, Deserialize)]
270pub struct WilsonLoop {
271 pub path: Vec<(usize, usize)>,
273 pub size: (usize, usize),
275 pub vev: Option<Complex64>,
277}
278
279#[derive(Debug, Clone, Serialize, Deserialize)]
281pub struct RGFlow {
282 pub scale: f64,
284 pub beta_functions: HashMap<String, f64>,
286 pub anomalous_dimensions: HashMap<String, f64>,
288 pub running_couplings: HashMap<String, f64>,
290 pub fixed_points: Vec<FixedPoint>,
292}
293
294#[derive(Debug, Clone, Serialize, Deserialize)]
296pub struct FixedPoint {
297 pub couplings: HashMap<String, f64>,
299 pub eigenvalues: Vec<Complex64>,
301 pub fp_type: FixedPointType,
303}
304
305#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
307pub enum FixedPointType {
308 IRStable,
310 UVStable,
312 Saddle,
314 Gaussian,
316 Interacting,
318}
319
320#[derive(Debug, Clone, Serialize, Deserialize)]
322pub struct CorrelationFunction {
323 pub operators: Vec<FieldOperator>,
325 pub separations: Vec<Vec<f64>>,
327 pub values: Array1<Complex64>,
329 pub errors: Array1<f64>,
331 pub connected: bool,
333}
334
335#[derive(Debug, Clone, Serialize, Deserialize)]
337pub struct ScatteringProcess {
338 pub initial_state: Vec<ParticleState>,
340 pub final_state: Vec<ParticleState>,
342 pub cms_energy: f64,
344 pub scattering_angle: f64,
346 pub cross_section: Option<f64>,
348 pub s_matrix_element: Option<Complex64>,
350}
351
352#[derive(Debug, Clone, Serialize, Deserialize)]
354pub struct ParticleState {
355 pub particle_type: String,
357 pub momentum: [f64; 4],
359 pub spin_state: Vec<Complex64>,
361 pub quantum_numbers: HashMap<String, i32>,
363}
364
365#[derive(Debug)]
367pub struct QuantumFieldTheorySimulator {
368 config: QFTConfig,
370 field_configs: HashMap<String, Array4<Complex64>>,
372 gauge_configs: Option<GaugeFieldConfig>,
374 path_integral: Option<PathIntegralSampler>,
376 rg_flow: Option<RGFlow>,
378 correlations: HashMap<String, CorrelationFunction>,
380 backend: Option<SciRS2Backend>,
382 stats: QFTStats,
384}
385
386#[derive(Debug)]
388pub struct PathIntegralSampler {
389 config: PathIntegralConfig,
391 current_config: Array4<Complex64>,
393 action_evaluator: ActionEvaluator,
395 mc_state: MonteCarloState,
397 sample_history: VecDeque<Array4<Complex64>>,
399}
400
401#[derive(Debug)]
403pub struct ActionEvaluator {
404 theory_type: FieldTheoryType,
406 couplings: HashMap<String, f64>,
408 lattice_params: LatticeParameters,
410}
411
412#[derive(Debug, Clone, Serialize, Deserialize)]
414pub struct LatticeParameters {
415 pub spacing: f64,
417 pub bare_mass: f64,
419 pub hopping_parameter: f64,
421 pub plaquette_size: usize,
423}
424
425#[derive(Debug)]
427pub struct MonteCarloState {
428 pub step: usize,
430 pub acceptance_rate: f64,
432 pub accepted_moves: usize,
434 pub proposed_moves: usize,
436 pub current_action: f64,
438 pub autocorr_time: Option<f64>,
440}
441
442#[derive(Debug, Clone, Serialize, Deserialize)]
444pub struct QFTStats {
445 pub simulation_time: f64,
447 pub field_evaluations: usize,
449 pub pi_samples: usize,
451 pub correlation_calculations: usize,
453 pub rg_steps: usize,
455 pub avg_plaquette: Option<f64>,
457 pub topological_charge: Option<f64>,
459}
460
461impl QuantumFieldTheorySimulator {
462 pub fn new(config: QFTConfig) -> Result<Self> {
464 let mut field_configs = HashMap::new();
465
466 let field_shape = (
468 config.lattice_size[0],
469 config.lattice_size[1],
470 config.lattice_size.get(2).copied().unwrap_or(1),
471 config.lattice_size.get(3).copied().unwrap_or(1),
472 );
473
474 match config.field_theory {
475 FieldTheoryType::ScalarPhi4 => {
476 field_configs.insert("phi".to_string(), Array4::zeros(field_shape));
477 }
478 FieldTheoryType::QED => {
479 field_configs.insert("psi".to_string(), Array4::zeros(field_shape));
481 for mu in 0..config.spacetime_dimensions {
483 field_configs.insert(format!("A_{mu}"), Array4::zeros(field_shape));
484 }
485 }
486 FieldTheoryType::YangMills | FieldTheoryType::QCD => {
487 let num_colors: usize = match config.field_theory {
489 FieldTheoryType::QCD => 3,
490 _ => 2,
491 };
492
493 for mu in 0..config.spacetime_dimensions {
494 for a in 0..num_colors.pow(2) - 1 {
495 field_configs.insert(format!("A_{mu}_{a}"), Array4::zeros(field_shape));
497 }
498 }
499
500 if config.field_theory == FieldTheoryType::QCD {
501 for flavor in 0..6 {
503 field_configs.insert(format!("q_{flavor}"), Array4::zeros(field_shape));
505 }
506 }
507 }
508 _ => {
509 field_configs.insert("default".to_string(), Array4::zeros(field_shape));
510 }
511 }
512
513 Ok(Self {
514 config,
515 field_configs,
516 gauge_configs: None,
517 path_integral: None,
518 rg_flow: None,
519 correlations: HashMap::new(),
520 backend: None,
521 stats: QFTStats::default(),
522 })
523 }
524
525 pub fn initialize_path_integral(&mut self, pi_config: PathIntegralConfig) -> Result<()> {
527 let field_shape = (
528 self.config.lattice_size[0],
529 self.config.lattice_size[1],
530 self.config.lattice_size.get(2).copied().unwrap_or(1),
531 pi_config.time_slices,
532 );
533
534 let lattice_params = LatticeParameters {
535 spacing: self.config.lattice_spacing,
536 bare_mass: self
537 .config
538 .coupling_constants
539 .get("m0")
540 .copied()
541 .unwrap_or(1.0),
542 hopping_parameter: 1.0
543 / 2.0f64.mul_add(
544 self.config
545 .coupling_constants
546 .get("m0")
547 .copied()
548 .unwrap_or(1.0),
549 8.0,
550 ),
551 plaquette_size: 1,
552 };
553
554 let action_evaluator = ActionEvaluator {
555 theory_type: self.config.field_theory,
556 couplings: self.config.coupling_constants.clone(),
557 lattice_params,
558 };
559
560 let mc_state = MonteCarloState {
561 step: 0,
562 acceptance_rate: 0.0,
563 accepted_moves: 0,
564 proposed_moves: 0,
565 current_action: 0.0,
566 autocorr_time: None,
567 };
568
569 self.path_integral = Some(PathIntegralSampler {
570 config: pi_config,
571 current_config: Array4::zeros(field_shape),
572 action_evaluator,
573 mc_state,
574 sample_history: VecDeque::new(),
575 });
576
577 Ok(())
578 }
579
580 pub fn setup_gauge_fields(&mut self, gauge_config: GaugeFieldConfig) -> Result<()> {
582 let mut wilson_loops = Vec::new();
584
585 for r in 1..=4 {
587 for t in 1..=4 {
588 wilson_loops.push(WilsonLoop {
589 path: self.generate_wilson_loop_path(r, t)?,
590 size: (r, t),
591 vev: None,
592 });
593 }
594 }
595
596 let mut gauge_conf = gauge_config;
597 gauge_conf.wilson_loops = wilson_loops;
598
599 self.initialize_gauge_field_matrices(&gauge_conf)?;
601
602 self.gauge_configs = Some(gauge_conf);
603 Ok(())
604 }
605
606 fn generate_wilson_loop_path(
608 &self,
609 spatial_size: usize,
610 temporal_size: usize,
611 ) -> Result<Vec<(usize, usize)>> {
612 let mut path = Vec::new();
613
614 let start_x = 0;
616 let start_t = 0;
617
618 for i in 0..spatial_size {
620 path.push((start_x + i, start_t));
621 }
622
623 for i in 0..temporal_size {
625 path.push((start_x + spatial_size - 1, start_t + i));
626 }
627
628 for i in 0..spatial_size {
630 path.push((start_x + spatial_size - 1 - i, start_t + temporal_size - 1));
631 }
632
633 for i in 0..temporal_size {
635 path.push((start_x, start_t + temporal_size - 1 - i));
636 }
637
638 Ok(path)
639 }
640
641 fn initialize_gauge_field_matrices(&mut self, gauge_config: &GaugeFieldConfig) -> Result<()> {
643 let field_shape = (
644 self.config.lattice_size[0],
645 self.config.lattice_size[1],
646 self.config.lattice_size.get(2).copied().unwrap_or(1),
647 self.config.lattice_size.get(3).copied().unwrap_or(1),
648 );
649
650 match gauge_config.gauge_group {
651 GaugeGroup::U1 => {
652 for mu in 0..self.config.spacetime_dimensions {
654 self.field_configs.insert(
655 format!("U_{mu}"),
656 Array4::from_elem(field_shape, Complex64::new(1.0, 0.0)),
657 );
658 }
659 }
660 GaugeGroup::SU(n) => {
661 for mu in 0..self.config.spacetime_dimensions {
663 for i in 0..n {
664 for j in 0..n {
665 let initial_value = if i == j {
666 Complex64::new(1.0, 0.0)
667 } else {
668 Complex64::new(0.0, 0.0)
669 };
670
671 self.field_configs.insert(
672 format!("U_{mu}_{i}{j}"),
673 Array4::from_elem(field_shape, initial_value),
674 );
675 }
676 }
677 }
678 }
679 _ => {
680 return Err(SimulatorError::InvalidConfiguration(
681 "Unsupported gauge group".to_string(),
682 ));
683 }
684 }
685
686 Ok(())
687 }
688
689 pub fn run_path_integral_mc(&mut self, num_steps: usize) -> Result<Vec<f64>> {
691 let pi_sampler = self.path_integral.as_mut().ok_or_else(|| {
692 SimulatorError::InvalidConfiguration("Path integral not initialized".to_string())
693 })?;
694
695 let mut action_history = Vec::with_capacity(num_steps);
696
697 for step in 0..num_steps {
698 let proposed_config = Self::propose_field_update(&pi_sampler.current_config)?;
700
701 let current_action = pi_sampler
703 .action_evaluator
704 .evaluate_action(&pi_sampler.current_config)?;
705 let proposed_action = pi_sampler
706 .action_evaluator
707 .evaluate_action(&proposed_config)?;
708
709 let delta_action = proposed_action - current_action;
710
711 let accept_prob = (-delta_action).exp().min(1.0);
713 let rand_val: f64 = thread_rng().gen();
714
715 pi_sampler.mc_state.proposed_moves += 1;
716
717 if rand_val < accept_prob {
718 pi_sampler.current_config = proposed_config;
720 pi_sampler.mc_state.current_action = proposed_action;
721 pi_sampler.mc_state.accepted_moves += 1;
722
723 if pi_sampler.sample_history.len() >= 1000 {
725 pi_sampler.sample_history.pop_front();
726 }
727 pi_sampler
728 .sample_history
729 .push_back(pi_sampler.current_config.clone());
730 }
731
732 pi_sampler.mc_state.acceptance_rate = pi_sampler.mc_state.accepted_moves as f64
734 / pi_sampler.mc_state.proposed_moves as f64;
735
736 action_history.push(pi_sampler.mc_state.current_action);
737 pi_sampler.mc_state.step = step;
738
739 self.stats.pi_samples += 1;
741 }
742
743 Ok(action_history)
744 }
745
746 fn propose_field_update(current_config: &Array4<Complex64>) -> Result<Array4<Complex64>> {
748 let mut proposed = current_config.clone();
749 let update_fraction = 0.1; let total_sites = proposed.len();
752 let sites_to_update = (total_sites as f64 * update_fraction) as usize;
753
754 let mut rng = thread_rng();
755 for _ in 0..sites_to_update {
756 let i = rng.gen_range(0..proposed.shape()[0]);
757 let j = rng.gen_range(0..proposed.shape()[1]);
758 let k = rng.gen_range(0..proposed.shape()[2]);
759 let l = rng.gen_range(0..proposed.shape()[3]);
760
761 let delta_real: f64 = rng.gen::<f64>() - 0.5;
763 let delta_imag: f64 = rng.gen::<f64>() - 0.5;
764 let delta = Complex64::new(delta_real * 0.1, delta_imag * 0.1);
765
766 proposed[[i, j, k, l]] += delta;
767 }
768
769 Ok(proposed)
770 }
771
772 pub fn calculate_correlation_function(
774 &mut self,
775 operators: &[FieldOperator],
776 max_separation: usize,
777 ) -> Result<CorrelationFunction> {
778 let separations: Vec<Vec<f64>> = (0..=max_separation)
779 .map(|r| vec![r as f64, 0.0, 0.0, 0.0])
780 .collect();
781
782 let mut values = Array1::zeros(separations.len());
783 let mut errors = Array1::zeros(separations.len());
784
785 if let Some(pi_sampler) = &self.path_integral {
787 if !pi_sampler.sample_history.is_empty() {
788 for (sep_idx, separation) in separations.iter().enumerate() {
789 let mut correlator_samples = Vec::new();
790
791 for config in &pi_sampler.sample_history {
792 let corr_value =
793 self.evaluate_correlator_on_config(operators, separation, config)?;
794 correlator_samples.push(corr_value);
795 }
796
797 let mean = correlator_samples.iter().sum::<Complex64>()
799 / correlator_samples.len() as f64;
800 let variance = correlator_samples
801 .iter()
802 .map(|x| (x - mean).norm_sqr())
803 .sum::<f64>()
804 / correlator_samples.len() as f64;
805 let std_error = (variance / correlator_samples.len() as f64).sqrt();
806
807 values[sep_idx] = mean;
808 errors[sep_idx] = std_error;
809 }
810 }
811 }
812
813 let correlation_fn = CorrelationFunction {
814 operators: operators.to_vec(),
815 separations,
816 values,
817 errors,
818 connected: true,
819 };
820
821 self.stats.correlation_calculations += 1;
822 Ok(correlation_fn)
823 }
824
825 fn evaluate_correlator_on_config(
827 &self,
828 operators: &[FieldOperator],
829 separation: &[f64],
830 config: &Array4<Complex64>,
831 ) -> Result<Complex64> {
832 if operators.len() != 2 {
834 return Err(SimulatorError::InvalidConfiguration(
835 "Only 2-point correlators currently supported".to_string(),
836 ));
837 }
838
839 let r = separation[0] as usize;
840 let lattice_size = config.shape()[0];
841
842 if r >= lattice_size {
843 return Ok(Complex64::new(0.0, 0.0));
844 }
845
846 let mut correlator = Complex64::new(0.0, 0.0);
847 let mut norm = 0.0;
848
849 for x0 in 0..lattice_size {
851 for y0 in 0..config.shape()[1] {
852 for z0 in 0..config.shape()[2] {
853 for t0 in 0..config.shape()[3] {
854 let x1 = (x0 + r) % lattice_size;
855
856 let field_0 = config[[x0, y0, z0, t0]];
857 let field_r = config[[x1, y0, z0, t0]];
858
859 correlator += field_0.conj() * field_r;
860 norm += 1.0;
861 }
862 }
863 }
864 }
865
866 Ok(correlator / norm)
867 }
868
869 pub fn calculate_wilson_loops(&mut self) -> Result<HashMap<String, Complex64>> {
871 let gauge_config = self.gauge_configs.as_ref().ok_or_else(|| {
872 SimulatorError::InvalidConfiguration("Gauge fields not initialized".to_string())
873 })?;
874
875 let mut wilson_values = HashMap::new();
876
877 for (loop_idx, wilson_loop) in gauge_config.wilson_loops.iter().enumerate() {
878 let loop_value = self.evaluate_wilson_loop(wilson_loop)?;
879 wilson_values.insert(
880 format!("WL_{}x{}", wilson_loop.size.0, wilson_loop.size.1),
881 loop_value,
882 );
883 }
884
885 Ok(wilson_values)
886 }
887
888 fn evaluate_wilson_loop(&self, wilson_loop: &WilsonLoop) -> Result<Complex64> {
890 let mut loop_value = Complex64::new(1.0, 0.0);
891
892 for (i, &(x, t)) in wilson_loop.path.iter().enumerate() {
894 let next_site = wilson_loop.path.get(i + 1).unwrap_or(&wilson_loop.path[0]);
895
896 let mu = if next_site.0 == x { 3 } else { 0 }; if let Some(gauge_field) = self.field_configs.get(&format!("U_{mu}")) {
900 if x < gauge_field.shape()[0] && t < gauge_field.shape()[3] {
901 let link_value = gauge_field[[x, 0, 0, t]];
902 loop_value *= link_value;
903 }
904 }
905 }
906
907 Ok(loop_value)
908 }
909
910 pub fn analyze_rg_flow(&mut self, energy_scales: &[f64]) -> Result<RGFlow> {
912 let mut beta_functions = HashMap::new();
913 let mut anomalous_dimensions = HashMap::new();
914 let mut running_couplings = HashMap::new();
915
916 for (coupling_name, &coupling_value) in &self.config.coupling_constants {
918 running_couplings.insert(coupling_name.clone(), coupling_value);
919 }
920
921 let mut rg_flow = RGFlow {
922 scale: energy_scales[0],
923 beta_functions,
924 anomalous_dimensions,
925 running_couplings: running_couplings.clone(),
926 fixed_points: Vec::new(),
927 };
928
929 for &scale in energy_scales {
931 let beta_g = self.calculate_beta_function("g", scale, &running_couplings)?;
932 let beta_lambda = self.calculate_beta_function("lambda", scale, &running_couplings)?;
933
934 rg_flow.beta_functions.insert("g".to_string(), beta_g);
935 rg_flow
936 .beta_functions
937 .insert("lambda".to_string(), beta_lambda);
938
939 let dt = 0.01; if let Some(g) = running_couplings.get_mut("g") {
942 *g += beta_g * dt;
943 }
944 if let Some(lambda) = running_couplings.get_mut("lambda") {
945 *lambda += beta_lambda * dt;
946 }
947
948 rg_flow.scale = scale;
949 rg_flow.running_couplings = running_couplings.clone();
950 self.stats.rg_steps += 1;
951 }
952
953 rg_flow.fixed_points = self.find_rg_fixed_points(&rg_flow.beta_functions)?;
955
956 self.rg_flow = Some(rg_flow.clone());
957 Ok(rg_flow)
958 }
959
960 fn calculate_beta_function(
962 &self,
963 coupling_name: &str,
964 scale: f64,
965 couplings: &HashMap<String, f64>,
966 ) -> Result<f64> {
967 match self.config.field_theory {
968 FieldTheoryType::ScalarPhi4 => {
969 match coupling_name {
970 "lambda" => {
971 let lambda = couplings.get("lambda").copied().unwrap_or(0.0);
973 Ok(3.0 * lambda.powi(2) / (16.0 * PI.powi(2)))
974 }
975 "g" => {
976 Ok(0.0)
978 }
979 _ => Ok(0.0),
980 }
981 }
982 FieldTheoryType::QED => {
983 match coupling_name {
984 "e" | "g" => {
985 let e = couplings
987 .get("e")
988 .or_else(|| couplings.get("g"))
989 .copied()
990 .unwrap_or(0.0);
991 Ok(e.powi(3) / (12.0 * PI.powi(2)))
992 }
993 _ => Ok(0.0),
994 }
995 }
996 FieldTheoryType::YangMills => {
997 match coupling_name {
998 "g" => {
999 let g = couplings.get("g").copied().unwrap_or(0.0);
1001 let n_colors = 3.0; let b0 = 11.0 * n_colors / (12.0 * PI);
1003 Ok(-b0 * g.powi(3))
1004 }
1005 _ => Ok(0.0),
1006 }
1007 }
1008 _ => Ok(0.0),
1009 }
1010 }
1011
1012 fn find_rg_fixed_points(
1014 &self,
1015 beta_functions: &HashMap<String, f64>,
1016 ) -> Result<Vec<FixedPoint>> {
1017 let mut fixed_points = Vec::new();
1018
1019 let mut gaussian_couplings = HashMap::new();
1021 for coupling_name in beta_functions.keys() {
1022 gaussian_couplings.insert(coupling_name.clone(), 0.0);
1023 }
1024
1025 fixed_points.push(FixedPoint {
1026 couplings: gaussian_couplings,
1027 eigenvalues: vec![Complex64::new(-1.0, 0.0)], fp_type: FixedPointType::Gaussian,
1029 });
1030
1031 for lambda_star in [0.1, 0.5, 1.0, 2.0] {
1034 let mut test_couplings = HashMap::new();
1035 test_couplings.insert("lambda".to_string(), lambda_star);
1036 test_couplings.insert("g".to_string(), 0.0);
1037
1038 let beta_lambda = self.calculate_beta_function("lambda", 1.0, &test_couplings)?;
1039
1040 if beta_lambda.abs() < 1e-6 {
1041 fixed_points.push(FixedPoint {
1043 couplings: test_couplings,
1044 eigenvalues: vec![Complex64::new(1.0, 0.0)], fp_type: FixedPointType::Interacting,
1046 });
1047 }
1048 }
1049
1050 Ok(fixed_points)
1051 }
1052
1053 pub fn calculate_scattering_cross_section(
1055 &mut self,
1056 process: &ScatteringProcess,
1057 ) -> Result<f64> {
1058 let cms_energy = process.cms_energy;
1062 let num_initial = process.initial_state.len();
1063 let num_final = process.final_state.len();
1064
1065 let coupling = self
1067 .config
1068 .coupling_constants
1069 .get("g")
1070 .copied()
1071 .unwrap_or(0.1);
1072
1073 let cross_section = match self.config.field_theory {
1074 FieldTheoryType::ScalarPhi4 => {
1075 if num_initial == 2 && num_final == 2 {
1077 let lambda = self
1078 .config
1079 .coupling_constants
1080 .get("lambda")
1081 .copied()
1082 .unwrap_or(0.01);
1083 lambda.powi(2) / cms_energy.powi(2)
1084 } else {
1085 0.0
1086 }
1087 }
1088 FieldTheoryType::QED => {
1089 let alpha = coupling.powi(2) / (4.0 * PI);
1091 alpha.powi(2) / cms_energy.powi(2)
1092 }
1093 _ => coupling.powi(2) / cms_energy.powi(2),
1094 };
1095
1096 Ok(cross_section)
1097 }
1098
1099 pub const fn get_statistics(&self) -> &QFTStats {
1101 &self.stats
1102 }
1103
1104 pub fn export_field_configuration(&self, field_name: &str) -> Result<Array4<Complex64>> {
1106 self.field_configs.get(field_name).cloned().ok_or_else(|| {
1107 SimulatorError::InvalidConfiguration(format!("Field '{field_name}' not found"))
1108 })
1109 }
1110}
1111
1112impl ActionEvaluator {
1113 pub fn evaluate_action(&self, config: &Array4<Complex64>) -> Result<f64> {
1115 match self.theory_type {
1116 FieldTheoryType::ScalarPhi4 => self.evaluate_phi4_action(config),
1117 FieldTheoryType::QED => self.evaluate_qed_action(config),
1118 FieldTheoryType::YangMills => self.evaluate_yang_mills_action(config),
1119 _ => self.evaluate_generic_action(config),
1120 }
1121 }
1122
1123 fn evaluate_phi4_action(&self, config: &Array4<Complex64>) -> Result<f64> {
1125 let mut action = 0.0;
1126 let lattice_spacing = self.lattice_params.spacing;
1127 let mass_sq = self.lattice_params.bare_mass.powi(2);
1128 let lambda = self.couplings.get("lambda").copied().unwrap_or(0.01);
1129
1130 let shape = config.shape();
1131 let (nx, ny, nz, nt) = (shape[0], shape[1], shape[2], shape[3]);
1132
1133 for x in 0..nx {
1135 for y in 0..ny {
1136 for z in 0..nz {
1137 for t in 0..nt {
1138 let phi = config[[x, y, z, t]];
1139
1140 let mut laplacian = Complex64::new(0.0, 0.0);
1142
1143 let phi_xp = config[[(x + 1) % nx, y, z, t]];
1145 let phi_xm = config[[(x + nx - 1) % nx, y, z, t]];
1146 laplacian += (phi_xp + phi_xm - 2.0 * phi) / lattice_spacing.powi(2);
1147
1148 let phi_yp = config[[x, (y + 1) % ny, z, t]];
1150 let phi_ym = config[[x, (y + ny - 1) % ny, z, t]];
1151 laplacian += (phi_yp + phi_ym - 2.0 * phi) / lattice_spacing.powi(2);
1152
1153 if nz > 1 {
1155 let phi_zp = config[[x, y, (z + 1) % nz, t]];
1156 let phi_zm = config[[x, y, (z + nz - 1) % nz, t]];
1157 laplacian += (phi_zp + phi_zm - 2.0 * phi) / lattice_spacing.powi(2);
1158 }
1159
1160 if nt > 1 {
1162 let phi_tp = config[[x, y, z, (t + 1) % nt]];
1163 let phi_tm = config[[x, y, z, (t + nt - 1) % nt]];
1164 laplacian += (phi_tp + phi_tm - 2.0 * phi) / lattice_spacing.powi(2);
1165 }
1166
1167 let kinetic = -phi.conj() * laplacian;
1169
1170 let mass_term = mass_sq * phi.norm_sqr();
1172
1173 let interaction = lambda * phi.norm_sqr().powi(2);
1175
1176 action += (kinetic.re + mass_term + interaction) * lattice_spacing.powi(4);
1177 }
1178 }
1179 }
1180 }
1181
1182 Ok(action)
1183 }
1184
1185 fn evaluate_qed_action(&self, config: &Array4<Complex64>) -> Result<f64> {
1187 let mut action = 0.0;
1189 let lattice_spacing = self.lattice_params.spacing;
1190 let e = self.couplings.get("e").copied().unwrap_or(0.1);
1191
1192 action += config.iter().map(|phi| phi.norm_sqr()).sum::<f64>() * lattice_spacing.powi(4);
1195
1196 Ok(action)
1197 }
1198
1199 fn evaluate_yang_mills_action(&self, config: &Array4<Complex64>) -> Result<f64> {
1201 let mut action = 0.0;
1203 let lattice_spacing = self.lattice_params.spacing;
1204 let g = self.couplings.get("g").copied().unwrap_or(0.1);
1205 let beta = 2.0 / g.powi(2); let shape = config.shape();
1209 let (nx, ny, nz, nt) = (shape[0], shape[1], shape[2], shape[3]);
1210
1211 for x in 0..nx {
1212 for y in 0..ny {
1213 for z in 0..nz {
1214 for t in 0..nt {
1215 let plaquette_xy = self.calculate_plaquette(config, x, y, z, t, 0, 1)?;
1217 let plaquette_xt = self.calculate_plaquette(config, x, y, z, t, 0, 3)?;
1218 let plaquette_yt = self.calculate_plaquette(config, x, y, z, t, 1, 3)?;
1219
1220 action +=
1221 beta * (3.0 - plaquette_xy.re - plaquette_xt.re - plaquette_yt.re);
1222 }
1223 }
1224 }
1225 }
1226
1227 Ok(action)
1228 }
1229
1230 fn calculate_plaquette(
1232 &self,
1233 config: &Array4<Complex64>,
1234 x: usize,
1235 y: usize,
1236 z: usize,
1237 t: usize,
1238 mu: usize,
1239 nu: usize,
1240 ) -> Result<Complex64> {
1241 let shape = config.shape();
1243
1244 let u_mu = config[[x, y, z, t]]; let u_nu_shifted = config[[
1247 (x + usize::from(mu == 0)) % shape[0],
1248 (y + usize::from(mu == 1)) % shape[1],
1249 z,
1250 t,
1251 ]]; let u_mu_shifted = config[[
1254 (x + usize::from(nu == 0)) % shape[0],
1255 (y + usize::from(nu == 1)) % shape[1],
1256 z,
1257 t,
1258 ]]; let u_nu = config[[x, y, z, t]]; let plaquette = u_mu * u_nu_shifted * u_mu_shifted.conj() * u_nu.conj();
1264
1265 Ok(plaquette)
1266 }
1267
1268 fn evaluate_generic_action(&self, config: &Array4<Complex64>) -> Result<f64> {
1270 let lattice_spacing = self.lattice_params.spacing;
1271
1272 let action = config.iter().map(|phi| phi.norm_sqr()).sum::<f64>() * lattice_spacing.powi(4);
1274
1275 Ok(action)
1276 }
1277}
1278
1279impl Default for QFTStats {
1280 fn default() -> Self {
1281 Self {
1282 simulation_time: 0.0,
1283 field_evaluations: 0,
1284 pi_samples: 0,
1285 correlation_calculations: 0,
1286 rg_steps: 0,
1287 avg_plaquette: None,
1288 topological_charge: None,
1289 }
1290 }
1291}
1292
1293#[cfg(test)]
1294mod tests {
1295 use super::*;
1296
1297 #[test]
1298 fn test_qft_simulator_creation() {
1299 let config = QFTConfig::default();
1300 let simulator = QuantumFieldTheorySimulator::new(config);
1301 assert!(simulator.is_ok());
1302 }
1303
1304 #[test]
1305 fn test_scalar_phi4_configuration() {
1306 let mut config = QFTConfig::default();
1307 config.field_theory = FieldTheoryType::ScalarPhi4;
1308 config.lattice_size = vec![8, 8, 8, 16];
1309
1310 let simulator = QuantumFieldTheorySimulator::new(config);
1311 assert!(simulator.is_ok());
1312
1313 let sim = simulator.unwrap();
1314 assert!(sim.field_configs.contains_key("phi"));
1315 }
1316
1317 #[test]
1318 fn test_qed_configuration() {
1319 let mut config = QFTConfig::default();
1320 config.field_theory = FieldTheoryType::QED;
1321 config.spacetime_dimensions = 4;
1322
1323 let simulator = QuantumFieldTheorySimulator::new(config);
1324 assert!(simulator.is_ok());
1325
1326 let sim = simulator.unwrap();
1327 assert!(sim.field_configs.contains_key("psi"));
1328 assert!(sim.field_configs.contains_key("A_0"));
1329 assert!(sim.field_configs.contains_key("A_1"));
1330 assert!(sim.field_configs.contains_key("A_2"));
1331 assert!(sim.field_configs.contains_key("A_3"));
1332 }
1333
1334 #[test]
1335 fn test_path_integral_initialization() {
1336 let config = QFTConfig::default();
1337 let mut simulator = QuantumFieldTheorySimulator::new(config).unwrap();
1338
1339 let pi_config = PathIntegralConfig {
1340 time_slices: 32,
1341 time_step: 0.1,
1342 action_type: ActionType::Euclidean,
1343 mc_algorithm: MonteCarloAlgorithm::Metropolis,
1344 importance_sampling: true,
1345 target_acceptance_rate: 0.5,
1346 };
1347
1348 let result = simulator.initialize_path_integral(pi_config);
1349 assert!(result.is_ok());
1350 assert!(simulator.path_integral.is_some());
1351 }
1352
1353 #[test]
1354 fn test_gauge_field_setup_u1() {
1355 let config = QFTConfig::default();
1356 let mut simulator = QuantumFieldTheorySimulator::new(config).unwrap();
1357
1358 let gauge_config = GaugeFieldConfig {
1359 gauge_group: GaugeGroup::U1,
1360 num_colors: 1,
1361 gauge_coupling: 0.1,
1362 gauge_fixing: GaugeFixing::Landau,
1363 wilson_loops: Vec::new(),
1364 };
1365
1366 let result = simulator.setup_gauge_fields(gauge_config);
1367 assert!(result.is_ok());
1368 assert!(simulator.gauge_configs.is_some());
1369 }
1370
1371 #[test]
1372 fn test_gauge_field_setup_su3() {
1373 let config = QFTConfig::default();
1374 let mut simulator = QuantumFieldTheorySimulator::new(config).unwrap();
1375
1376 let gauge_config = GaugeFieldConfig {
1377 gauge_group: GaugeGroup::SU(3),
1378 num_colors: 3,
1379 gauge_coupling: 0.2,
1380 gauge_fixing: GaugeFixing::Coulomb,
1381 wilson_loops: Vec::new(),
1382 };
1383
1384 let result = simulator.setup_gauge_fields(gauge_config);
1385 assert!(result.is_ok());
1386 assert!(simulator.gauge_configs.is_some());
1387
1388 assert!(simulator.field_configs.contains_key("U_0_00"));
1390 assert!(simulator.field_configs.contains_key("U_0_11"));
1391 assert!(simulator.field_configs.contains_key("U_0_22"));
1392 }
1393
1394 #[test]
1395 fn test_wilson_loop_generation() {
1396 let config = QFTConfig::default();
1397 let simulator = QuantumFieldTheorySimulator::new(config).unwrap();
1398
1399 let path = simulator.generate_wilson_loop_path(2, 3).unwrap();
1400 assert_eq!(path.len(), 10); assert_eq!(path[0], path[path.len() - 1]);
1404 }
1405
1406 #[test]
1407 fn test_action_evaluation_phi4() {
1408 let config = QFTConfig::default();
1409 let simulator = QuantumFieldTheorySimulator::new(config).unwrap();
1410
1411 let lattice_params = LatticeParameters {
1412 spacing: 1.0,
1413 bare_mass: 1.0,
1414 hopping_parameter: 0.125,
1415 plaquette_size: 1,
1416 };
1417
1418 let mut couplings = HashMap::new();
1419 couplings.insert("lambda".to_string(), 0.1);
1420
1421 let evaluator = ActionEvaluator {
1422 theory_type: FieldTheoryType::ScalarPhi4,
1423 couplings,
1424 lattice_params,
1425 };
1426
1427 let field_config = Array4::zeros((4, 4, 4, 4));
1428 let action = evaluator.evaluate_action(&field_config);
1429 assert!(action.is_ok());
1430 assert_eq!(action.unwrap(), 0.0); }
1432
1433 #[test]
1434 fn test_beta_function_phi4() {
1435 let config = QFTConfig::default();
1436 let simulator = QuantumFieldTheorySimulator::new(config).unwrap();
1437
1438 let mut couplings = HashMap::new();
1439 couplings.insert("lambda".to_string(), 0.1);
1440
1441 let beta_lambda = simulator.calculate_beta_function("lambda", 1.0, &couplings);
1442 assert!(beta_lambda.is_ok());
1443
1444 let beta_val = beta_lambda.unwrap();
1445 assert!(beta_val > 0.0); }
1447
1448 #[test]
1449 fn test_beta_function_qed() {
1450 let mut config = QFTConfig::default();
1451 config.field_theory = FieldTheoryType::QED;
1452 let simulator = QuantumFieldTheorySimulator::new(config).unwrap();
1453
1454 let mut couplings = HashMap::new();
1455 couplings.insert("e".to_string(), 0.3);
1456
1457 let beta_e = simulator.calculate_beta_function("e", 1.0, &couplings);
1458 assert!(beta_e.is_ok());
1459
1460 let beta_val = beta_e.unwrap();
1461 assert!(beta_val > 0.0); }
1463
1464 #[test]
1465 fn test_beta_function_yang_mills() {
1466 let mut config = QFTConfig::default();
1467 config.field_theory = FieldTheoryType::YangMills;
1468 let simulator = QuantumFieldTheorySimulator::new(config).unwrap();
1469
1470 let mut couplings = HashMap::new();
1471 couplings.insert("g".to_string(), 0.5);
1472
1473 let beta_g = simulator.calculate_beta_function("g", 1.0, &couplings);
1474 assert!(beta_g.is_ok());
1475
1476 let beta_val = beta_g.unwrap();
1477 assert!(beta_val < 0.0); }
1479
1480 #[test]
1481 fn test_rg_flow_analysis() {
1482 let config = QFTConfig::default();
1483 let mut simulator = QuantumFieldTheorySimulator::new(config).unwrap();
1484
1485 let energy_scales = [0.1, 1.0, 10.0, 100.0];
1486 let rg_flow = simulator.analyze_rg_flow(&energy_scales);
1487 assert!(rg_flow.is_ok());
1488
1489 let flow = rg_flow.unwrap();
1490 assert!(flow.beta_functions.contains_key("lambda"));
1491 assert!(!flow.fixed_points.is_empty());
1492 assert!(flow
1493 .fixed_points
1494 .iter()
1495 .any(|fp| fp.fp_type == FixedPointType::Gaussian));
1496 }
1497
1498 #[test]
1499 fn test_scattering_cross_section_phi4() {
1500 let config = QFTConfig::default();
1501 let mut simulator = QuantumFieldTheorySimulator::new(config).unwrap();
1502
1503 let process = ScatteringProcess {
1504 initial_state: vec![
1505 ParticleState {
1506 particle_type: "phi".to_string(),
1507 momentum: [1.0, 0.0, 0.0, 0.0],
1508 spin_state: vec![Complex64::new(1.0, 0.0)],
1509 quantum_numbers: HashMap::new(),
1510 },
1511 ParticleState {
1512 particle_type: "phi".to_string(),
1513 momentum: [-1.0, 0.0, 0.0, 0.0],
1514 spin_state: vec![Complex64::new(1.0, 0.0)],
1515 quantum_numbers: HashMap::new(),
1516 },
1517 ],
1518 final_state: vec![
1519 ParticleState {
1520 particle_type: "phi".to_string(),
1521 momentum: [0.0, 1.0, 0.0, 0.0],
1522 spin_state: vec![Complex64::new(1.0, 0.0)],
1523 quantum_numbers: HashMap::new(),
1524 },
1525 ParticleState {
1526 particle_type: "phi".to_string(),
1527 momentum: [0.0, -1.0, 0.0, 0.0],
1528 spin_state: vec![Complex64::new(1.0, 0.0)],
1529 quantum_numbers: HashMap::new(),
1530 },
1531 ],
1532 cms_energy: 2.0,
1533 scattering_angle: PI / 2.0,
1534 cross_section: None,
1535 s_matrix_element: None,
1536 };
1537
1538 let cross_section = simulator.calculate_scattering_cross_section(&process);
1539 assert!(cross_section.is_ok());
1540
1541 let sigma = cross_section.unwrap();
1542 assert!(sigma > 0.0);
1543 assert!(sigma.is_finite());
1544 }
1545
1546 #[test]
1547 fn test_field_operator_creation() {
1548 let field_op = FieldOperator {
1549 field_type: FieldOperatorType::Scalar,
1550 position: vec![0.0, 0.0, 0.0, 0.0],
1551 momentum: None,
1552 component: 0,
1553 time_ordering: TimeOrdering::TimeOrdered,
1554 normal_ordering: true,
1555 };
1556
1557 assert_eq!(field_op.field_type, FieldOperatorType::Scalar);
1558 assert_eq!(field_op.position.len(), 4);
1559 assert!(field_op.normal_ordering);
1560 }
1561
1562 #[test]
1563 #[ignore]
1564 fn test_path_integral_monte_carlo_short() {
1565 let config = QFTConfig::default();
1566 let mut simulator = QuantumFieldTheorySimulator::new(config).unwrap();
1567
1568 let pi_config = PathIntegralConfig {
1569 time_slices: 8,
1570 time_step: 0.1,
1571 action_type: ActionType::Euclidean,
1572 mc_algorithm: MonteCarloAlgorithm::Metropolis,
1573 importance_sampling: true,
1574 target_acceptance_rate: 0.5,
1575 };
1576
1577 simulator.initialize_path_integral(pi_config).unwrap();
1578
1579 let result = simulator.run_path_integral_mc(100);
1581 assert!(result.is_ok());
1582
1583 let action_history = result.unwrap();
1584 assert_eq!(action_history.len(), 100);
1585 assert!(action_history.iter().all(|&a| a.is_finite()));
1586 }
1587
1588 #[test]
1589 #[ignore]
1590 fn test_correlation_function_calculation() {
1591 let config = QFTConfig::default();
1592 let mut simulator = QuantumFieldTheorySimulator::new(config).unwrap();
1593
1594 let pi_config = PathIntegralConfig {
1595 time_slices: 8,
1596 time_step: 0.1,
1597 action_type: ActionType::Euclidean,
1598 mc_algorithm: MonteCarloAlgorithm::Metropolis,
1599 importance_sampling: true,
1600 target_acceptance_rate: 0.5,
1601 };
1602
1603 simulator.initialize_path_integral(pi_config).unwrap();
1604
1605 simulator.run_path_integral_mc(50).unwrap();
1607
1608 let operators = vec![
1609 FieldOperator {
1610 field_type: FieldOperatorType::Scalar,
1611 position: vec![0.0, 0.0, 0.0, 0.0],
1612 momentum: None,
1613 component: 0,
1614 time_ordering: TimeOrdering::TimeOrdered,
1615 normal_ordering: true,
1616 },
1617 FieldOperator {
1618 field_type: FieldOperatorType::Scalar,
1619 position: vec![1.0, 0.0, 0.0, 0.0],
1620 momentum: None,
1621 component: 0,
1622 time_ordering: TimeOrdering::TimeOrdered,
1623 normal_ordering: true,
1624 },
1625 ];
1626
1627 let correlation = simulator.calculate_correlation_function(&operators, 4);
1628 assert!(correlation.is_ok());
1629
1630 let corr_fn = correlation.unwrap();
1631 assert_eq!(corr_fn.separations.len(), 5); assert_eq!(corr_fn.values.len(), 5);
1633 assert_eq!(corr_fn.errors.len(), 5);
1634 }
1635
1636 #[test]
1637 fn test_export_field_configuration() {
1638 let config = QFTConfig::default();
1639 let simulator = QuantumFieldTheorySimulator::new(config).unwrap();
1640
1641 let field_config = simulator.export_field_configuration("phi");
1642 assert!(field_config.is_ok());
1643
1644 let config_array = field_config.unwrap();
1645 assert_eq!(config_array.ndim(), 4);
1646
1647 let invalid_field = simulator.export_field_configuration("nonexistent");
1649 assert!(invalid_field.is_err());
1650 }
1651
1652 #[test]
1653 fn test_statistics_tracking() {
1654 let config = QFTConfig::default();
1655 let simulator = QuantumFieldTheorySimulator::new(config).unwrap();
1656
1657 let stats = simulator.get_statistics();
1658 assert_eq!(stats.field_evaluations, 0);
1659 assert_eq!(stats.pi_samples, 0);
1660 assert_eq!(stats.correlation_calculations, 0);
1661 assert_eq!(stats.rg_steps, 0);
1662 }
1663}