lattice_qcd_rs/integrator/mod.rs
1//! Numerical integrators to carry out simulations.
2//!
3//! See [`SymplecticIntegrator`]. The simulations are done on [`LatticeStateWithEField`]
4//! It also require a notion of [`SimulationStateSynchronous`]
5//! and [`SimulationStateLeapFrog`].
6//!
7//! Even thought it is effortless to implement both [`SimulationStateSynchronous`]
8//! and [`SimulationStateLeapFrog`].
9//! I advice against it and implement only [`SimulationStateSynchronous`] and
10//! use [`super::simulation::SimulationStateLeap`]
11//! for leap frog states as it gives a compile time check that you did not forget
12//! doing a half steps.
13//!
14//! This library gives two implementations of [`SymplecticIntegrator`]:
15//! [`SymplecticEuler`] and [`SymplecticEulerRayon`].
16//! I would advice using [`SymplecticEulerRayon`] if you do not mind the little
17//! more memory it uses.
18//! # Example
19//! let us create a basic random state and let us simulate.
20//! ```
21//! # use std::error::Error;
22//! #
23//! # fn main() -> Result<(), Box<dyn Error>> {
24//! use lattice_qcd_rs::integrator::{SymplecticEuler, SymplecticIntegrator};
25//! use lattice_qcd_rs::simulation::{
26//! LatticeStateDefault, LatticeStateEFSyncDefault, LatticeStateWithEField,
27//! };
28//! use rand::SeedableRng;
29//!
30//! let mut rng = rand::rngs::StdRng::seed_from_u64(0); // change with your seed
31//! let state1 = LatticeStateEFSyncDefault::new_random_e_state(
32//! LatticeStateDefault::<3>::new_determinist(100_f64, 1_f64, 4, &mut rng)?,
33//! &mut rng,
34//! );
35//! let integrator = SymplecticEuler::default();
36//! let state2 = integrator.integrate_sync_sync(&state1, 0.000_1_f64)?;
37//! let state3 = integrator.integrate_sync_sync(&state2, 0.000_1_f64)?;
38//! # Ok(())
39//! # }
40//! ```
41//! Let us then compute and compare the Hamiltonian.
42//! ```
43//! # use std::error::Error;
44//! #
45//! # fn main() -> Result<(), Box<dyn Error>> {
46//! # use lattice_qcd_rs::integrator::{SymplecticEuler, SymplecticIntegrator};
47//! # use lattice_qcd_rs::simulation::{
48//! # LatticeStateDefault, LatticeStateWithEField, LatticeStateEFSyncDefault,
49//! # };
50//! # use rand::SeedableRng;
51//! #
52//! # let mut rng = rand::rngs::StdRng::seed_from_u64(0); // change with your seed
53//! # let state1 = LatticeStateEFSyncDefault::new_random_e_state(
54//! # LatticeStateDefault::<3>::new_determinist(100_f64, 1_f64, 4, &mut rng)?,
55//! # &mut rng,
56//! # );
57//! # let integrator = SymplecticEuler::default();
58//! # let state2 = integrator.integrate_sync_sync(&state1, 0.000_1_f64)?;
59//! # let state3 = integrator.integrate_sync_sync(&state2, 0.000_1_f64)?;
60//! let h = state1.hamiltonian_total();
61//! let h2 = state3.hamiltonian_total();
62//! println!("The error on the Hamiltonian is {}", h - h2);
63//! # Ok(())
64//! # }
65//! ```
66//! See [`SimulationStateSynchronous`] for more convenient methods.
67
68use na::SVector;
69
70use super::{
71 field::{EField, LinkMatrix, Su3Adjoint},
72 lattice::{LatticeCyclic, LatticeLink, LatticeLinkCanonical, LatticePoint},
73 simulation::{LatticeStateWithEField, SimulationStateLeapFrog, SimulationStateSynchronous},
74 CMatrix3, Complex, Real,
75};
76
77pub mod symplectic_euler;
78pub mod symplectic_euler_rayon;
79
80pub use symplectic_euler::SymplecticEuler;
81pub use symplectic_euler_rayon::SymplecticEulerRayon;
82
83/// Define an symplectic numerical integrator
84///
85/// The integrator evolve the state in time.
86///
87/// The integrator should be capable of switching between Sync state
88/// (q (or link matrices) at time T , p (or e_field) at time T )
89/// and leap frog (a at time T, p at time T + 1/2)
90///
91/// # Example
92/// For an example see the module level documentation [`super::integrator`].
93pub trait SymplecticIntegrator<StateSync, StateLeap, const D: usize>
94where
95 StateSync: SimulationStateSynchronous<D>,
96 StateLeap: SimulationStateLeapFrog<D>,
97{
98 /// Type of error returned by the Integrator.
99 type Error;
100
101 /// Integrate a sync state to a sync state by advancing the link matrices and the electrical field simultaneously.
102 ///
103 /// # Example
104 /// see the module level documentation [`super::integrator`].
105 ///
106 /// # Errors
107 /// Return an error if the integration encounter a problem
108 fn integrate_sync_sync(&self, l: &StateSync, delta_t: Real) -> Result<StateSync, Self::Error>;
109
110 /// Integrate a leap state to a leap state using leap frog algorithm.
111 ///
112 ///
113 /// # Example
114 /// ```
115 /// # use std::error::Error;
116 /// #
117 /// # fn main() -> Result<(), Box<dyn Error>> {
118 /// use lattice_qcd_rs::integrator::{SymplecticEulerRayon, SymplecticIntegrator};
119 /// use lattice_qcd_rs::simulation::{
120 /// LatticeStateDefault, LatticeStateEFSyncDefault, LatticeStateWithEField,
121 /// };
122 /// use rand::SeedableRng;
123 ///
124 /// let mut rng = rand::rngs::StdRng::seed_from_u64(0); // change with your seed
125 /// let state = LatticeStateEFSyncDefault::new_random_e_state(
126 /// LatticeStateDefault::<3>::new_determinist(1_f64, 2_f64, 4, &mut rng)?,
127 /// &mut rng,
128 /// );
129 /// let h = state.hamiltonian_total();
130 /// let integrator = SymplecticEulerRayon::default();
131 /// let mut leap_frog = integrator.integrate_sync_leap(&state, 0.000_001_f64)?;
132 /// drop(state);
133 /// for _ in 0..2 {
134 /// // Realistically you would want more steps
135 /// leap_frog = integrator.integrate_leap_leap(&leap_frog, 0.000_001_f64)?;
136 /// }
137 /// let state = integrator.integrate_leap_sync(&leap_frog, 0.000_001_f64)?;
138 /// let h2 = state.hamiltonian_total();
139 ///
140 /// println!("The error on the Hamiltonian is {}", h - h2);
141 /// # Ok(())
142 /// # }
143 /// ```
144 ///
145 /// # Errors
146 /// Return an error if the integration encounter a problem
147 fn integrate_leap_leap(&self, l: &StateLeap, delta_t: Real) -> Result<StateLeap, Self::Error>;
148
149 /// Integrate a sync state to a leap state by doing a half step for the conjugate momenta.
150 ///
151 /// # Example
152 /// see [`SymplecticIntegrator::integrate_leap_leap`]
153 ///
154 /// # Errors
155 /// Return an error if the integration encounter a problem
156 fn integrate_sync_leap(&self, l: &StateSync, delta_t: Real) -> Result<StateLeap, Self::Error>;
157
158 /// Integrate a leap state to a sync state by finishing doing a step for the position and finishing
159 /// the half step for the conjugate momenta.
160 ///
161 /// # Example
162 /// see [`SymplecticIntegrator::integrate_leap_leap`]
163 ///
164 /// # Errors
165 /// Return an error if the integration encounter a problem
166 fn integrate_leap_sync(&self, l: &StateLeap, delta_t: Real) -> Result<StateSync, Self::Error>;
167
168 /// Integrate a Sync state by going to leap and then back to sync.
169 /// This is the symplectic method of integration, which should conserve approximately the hamiltonian
170 ///
171 /// Note that you might want to override this method as it can save you from a clone.
172 ///
173 /// # Example
174 /// ```
175 /// # use std::error::Error;
176 /// #
177 /// # fn main() -> Result<(), Box<dyn Error>> {
178 /// use lattice_qcd_rs::integrator::{SymplecticEulerRayon, SymplecticIntegrator};
179 /// use lattice_qcd_rs::simulation::{
180 /// LatticeStateDefault, LatticeStateEFSyncDefault, LatticeStateWithEField,
181 /// };
182 /// use rand::SeedableRng;
183 ///
184 /// let mut rng = rand::rngs::StdRng::seed_from_u64(0); // change with your seed
185 /// let mut state = LatticeStateEFSyncDefault::new_random_e_state(
186 /// LatticeStateDefault::<3>::new_determinist(1_f64, 2_f64, 4, &mut rng)?,
187 /// &mut rng,
188 /// );
189 /// let h = state.hamiltonian_total();
190 ///
191 /// let integrator = SymplecticEulerRayon::default();
192 /// for _ in 0..1 {
193 /// // Realistically you would want more steps
194 /// state = integrator.integrate_symplectic(&state, 0.000_001_f64)?;
195 /// }
196 /// let h2 = state.hamiltonian_total();
197 ///
198 /// println!("The error on the Hamiltonian is {}", h - h2);
199 /// # Ok(())
200 /// # }
201 /// ```
202 ///
203 /// # Errors
204 /// Return an error if the integration encounter a problem
205 fn integrate_symplectic(&self, l: &StateSync, delta_t: Real) -> Result<StateSync, Self::Error> {
206 self.integrate_leap_sync(&self.integrate_sync_leap(l, delta_t)?, delta_t)
207 }
208}
209
210/// function for link integration.
211/// This must succeed as it is use while doing parallel computation. Returning a Option is undesirable.
212/// As it can panic if a out of bound link is passed it needs to stay private.
213///
214/// # Panic
215/// It panics if a out of bound link is passed.
216fn integrate_link<State, const D: usize>(
217 link: &LatticeLinkCanonical<D>,
218 link_matrix: &LinkMatrix,
219 e_field: &EField<D>,
220 lattice: &LatticeCyclic<D>,
221 delta_t: Real,
222) -> CMatrix3
223where
224 State: LatticeStateWithEField<D>,
225{
226 let canonical_link = LatticeLink::from(*link);
227 let initial_value = link_matrix
228 .matrix(&canonical_link, lattice)
229 .expect("Link matrix not found");
230 initial_value
231 + State::derivative_u(link, link_matrix, e_field, lattice).expect("Derivative not found")
232 * Complex::from(delta_t)
233}
234
235/// function for "Electrical" field integration.
236/// Like [`integrate_link`] this must succeed.
237///
238/// # Panics
239/// It panics if a out of bound link is passed.
240fn integrate_efield<State, const D: usize>(
241 point: &LatticePoint<D>,
242 link_matrix: &LinkMatrix,
243 e_field: &EField<D>,
244 lattice: &LatticeCyclic<D>,
245 delta_t: Real,
246) -> SVector<Su3Adjoint, D>
247where
248 State: LatticeStateWithEField<D>,
249{
250 let initial_value = e_field.e_vec(point, lattice).expect("E Field not found");
251 let deriv =
252 State::derivative_e(point, link_matrix, e_field, lattice).expect("Derivative not found");
253 initial_value + deriv.map(|el| el * delta_t)
254}