lattice_qcd_rs/simulation/state.rs
1//! Module containing the simulation State.
2//!
3//! The trait [`LatticeState`] is the most basic state with just the link matrices.
4//! The default implementation is [`LatticeStateDefault`].
5//! More over a more advance state with the color electrical field is given by
6//! the trait [`LatticeStateWithEField`] with the default implementation
7//! [`LatticeStateEFSyncDefault`]. Thw word "Sync" appears. It means that the simulation
8//! for the equation of movement are done at the same time for both the link matrices and
9//! the electric field. It is given by te trait [`SimulationStateSynchronous`]. It can also
10//! be done using the leapfrog method with the trait [`SimulationStateLeapFrog`]
11//! and the wrapper [`SimulationStateLeap`].
12//!
13//! See item documentation for more details.
14
15use crossbeam::thread;
16use na::{ComplexField, SVector};
17use rayon::iter::ParallelBridge;
18use rayon::prelude::*;
19#[cfg(feature = "serde-serialize")]
20use serde::{Deserialize, Serialize};
21
22use super::{
23 super::{
24 error::{
25 LatticeInitializationError, MultiIntegrationError, StateInitializationError,
26 ThreadedStateInitializationError,
27 },
28 field::{EField, LinkMatrix, Su3Adjoint},
29 integrator::SymplecticIntegrator,
30 lattice::{
31 Direction, DirectionList, LatticeCyclic, LatticeElementToIndex, LatticeLink,
32 LatticeLinkCanonical, LatticePoint,
33 },
34 su3,
35 thread::{ThreadAnyError, ThreadError},
36 CMatrix3, Complex, Real, Vector8,
37 },
38 monte_carlo::MonteCarlo,
39};
40
41/// Default leap frog simulation state
42pub type LeapFrogStateDefault<const D: usize> =
43 SimulationStateLeap<LatticeStateEFSyncDefault<LatticeStateDefault<D>, D>, D>;
44
45/// Trait to represent a pure gauge lattice state of dimension `D`.
46///
47/// It defines only one field: `link_matrix` of type [`LinkMatrix`].
48///
49/// # Example
50/// They are many examples throughout the carte see by instance
51/// [`super::monte_carlo::hybrid_monte_carlo`].
52pub trait LatticeState<const D: usize> {
53 /// Get the link matrices of this state.
54 ///
55 /// This is the field that stores the link matrices.
56 /// # Example
57 /// ```
58 /// use lattice_qcd_rs::lattice::{DirectionEnum, LatticePoint};
59 /// use lattice_qcd_rs::simulation::{LatticeState, LatticeStateDefault};
60 ///
61 /// # use std::error::Error;
62 /// # fn main() -> Result<(), Box<dyn Error>> {
63 /// let point = LatticePoint::new_zero();
64 /// let state = LatticeStateDefault::<4>::new_cold(1_f64, 10_f64, 4)?;
65 /// let _plaquette = state.link_matrix().pij(
66 /// &point,
67 /// &DirectionEnum::XPos.into(),
68 /// &DirectionEnum::YPos.into(),
69 /// state.lattice(),
70 /// );
71 /// # Ok(())
72 /// # }
73 /// ```
74 fn link_matrix(&self) -> &LinkMatrix;
75
76 /// Replace the links matrices with the given input. It should panic if link matrix
77 /// is not of the correct size.
78 ///
79 /// # Panic
80 /// Panic if the length of link_matrix is different from
81 /// `lattice.get_number_of_canonical_links_space()`
82 fn set_link_matrix(&mut self, link_matrix: LinkMatrix);
83
84 /// Get the lattice into which the state exists.
85 fn lattice(&self) -> &LatticeCyclic<D>;
86
87 /// Returns the beta parameter of the states.
88 fn beta(&self) -> Real;
89
90 /// C_A constant of the model, usually it is 3.
91 const CA: Real;
92
93 /// Returns the Hamiltonian of the links configuration.
94 fn hamiltonian_links(&self) -> Real;
95
96 /// Do one monte carlo step with the given method.
97 ///
98 /// # Errors
99 /// The error form `MonteCarlo::get_next_element` is propagated.
100 ///
101 /// # Example
102 /// see [`super::monte_carlo::hybrid_monte_carlo`].
103 fn monte_carlo_step<M>(self, m: &mut M) -> Result<Self, M::Error>
104 where
105 Self: Sized,
106 M: MonteCarlo<Self, D> + ?Sized,
107 {
108 m.next_element(self)
109 }
110
111 /// Take the average of the trace of all plaquettes.
112 ///
113 /// # Example
114 /// see the crate documentation [`crate`].
115 fn average_trace_plaquette(&self) -> Option<Complex> {
116 self.link_matrix().average_trace_plaquette(self.lattice())
117 }
118}
119
120/// Trait for a way to create a [`LatticeState`] from some parameters.
121///
122/// It is separated from the [`LatticeState`] because not all [`LatticeState`] can be create in this way.
123/// By instance when there is also a field of conjugate momenta of the link matrices.
124///
125/// This is used by the Monte Carlo algorithms to create the new states.
126pub trait LatticeStateNew<const D: usize>: LatticeState<D> + Sized {
127 /// Error type
128 type Error;
129
130 /// Create a new simulation state.
131 ///
132 /// # Errors
133 /// Give an error if the parameter are incorrect or the length of `link_matrix` does not correspond
134 /// to `lattice`.
135 ///
136 /// # Example
137 /// ```
138 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
139 /// use lattice_qcd_rs::field::LinkMatrix;
140 /// use lattice_qcd_rs::lattice::LatticeCyclic;
141 /// use lattice_qcd_rs::simulation::{LatticeStateDefault, LatticeStateNew};
142 ///
143 /// let lattice = LatticeCyclic::new(1_f64, 4)?;
144 /// let links = LinkMatrix::new_cold(&lattice);
145 /// let state = LatticeStateDefault::<4>::new(lattice, 1_f64, links)?;
146 /// # Ok(())
147 /// # }
148 /// ```
149 fn new(
150 lattice: LatticeCyclic<D>,
151 beta: Real,
152 link_matrix: LinkMatrix,
153 ) -> Result<Self, Self::Error>;
154}
155
156/// Represent a lattice state where the conjugate momenta of the link matrices are included.
157///
158/// If you have a LatticeState and want the default way of adding the conjugate momenta look at
159/// [`LatticeStateEFSyncDefault`].
160///
161/// If you want to solve the equation of motion using an [`SymplecticIntegrator`] also implement
162/// [`SimulationStateSynchronous`] and the wrapper [`SimulationStateLeap`] can give you an [`SimulationStateLeapFrog`].
163///
164/// It is used for the [`super::monte_carlo::HybridMonteCarlo`] algorithm.
165pub trait LatticeStateWithEField<const D: usize>: LatticeState<D> {
166 /// Reset the e_field with radom value distributed as N(0, 1 / beta)
167 /// [`rand_distr::StandardNormal`].
168 ///
169 /// # Errors
170 /// Gives and error if N(0, 0.5/beta ) is not a valid distribution (for example beta = 0).
171 ///
172 /// Gives [`StateInitializationError::GaussProjectionError`] if the Gauss projection failed
173 // TODO explain why !!!
174 fn reset_e_field<Rng>(&mut self, rng: &mut Rng) -> Result<(), StateInitializationError>
175 where
176 Rng: rand::Rng + ?Sized,
177 {
178 let d = rand_distr::Normal::new(0_f64, 0.5_f64 / self.beta())?;
179 let new_e_field = EField::new_determinist(self.lattice(), rng, &d);
180 if !self.lattice().has_compatible_length_e_field(&new_e_field) {
181 return Err(StateInitializationError::IncompatibleSize);
182 }
183 self.set_e_field(
184 new_e_field
185 .project_to_gauss(self.link_matrix(), self.lattice())
186 .ok_or(StateInitializationError::GaussProjectionError)?,
187 );
188 Ok(())
189 }
190
191 /// The "Electrical" field of this state.
192 fn e_field(&self) -> &EField<D>;
193
194 /// Replace the electrical field with the given input. It should panic if the input is not of
195 /// the correct size.
196 ///
197 /// # Panic
198 /// Panic if the length of link_matrix is different from `lattice.get_number_of_points()`
199 fn set_e_field(&mut self, e_field: EField<D>);
200
201 /// return the time state, i.e. the number of time the simulation ran.
202 fn t(&self) -> usize;
203
204 /// Get the derivative \partial_t U(link), returns [`None`] if the link is outside of the lattice.
205 ///
206 /// It is used in order to apply the equation of motion.
207 fn derivative_u(
208 link: &LatticeLinkCanonical<D>,
209 link_matrix: &LinkMatrix,
210 e_field: &EField<D>,
211 lattice: &LatticeCyclic<D>,
212 ) -> Option<CMatrix3>;
213
214 /// Get the derivative \partial_t E(point), returns [`None`] if the link is outside of the lattice.
215 ///
216 /// It is used in order to apply the equation of motion.
217 fn derivative_e(
218 point: &LatticePoint<D>,
219 link_matrix: &LinkMatrix,
220 e_field: &EField<D>,
221 lattice: &LatticeCyclic<D>,
222 ) -> Option<SVector<Su3Adjoint, D>>;
223
224 /// Get the energy of the conjugate momenta configuration
225 fn hamiltonian_efield(&self) -> Real;
226
227 /// Get the total energy, by default [`LatticeStateWithEField::hamiltonian_efield`]
228 /// + [`LatticeState::hamiltonian_links`]
229 fn hamiltonian_total(&self) -> Real {
230 self.hamiltonian_links() + self.hamiltonian_efield()
231 }
232}
233
234/// Trait to create a simulation state.
235///
236/// It is used by the [`super::monte_carlo::HybridMonteCarlo`] algorithm to create new state.
237pub trait LatticeStateWithEFieldNew<const D: usize>
238where
239 Self: LatticeStateWithEField<D> + Sized,
240{
241 /// Error type
242 type Error: From<rand_distr::NormalError>;
243
244 /// Create a new simulation state
245 ///
246 /// # Errors
247 /// Give an error if the parameter are incorrect or the length of `link_matrix`
248 /// and `e_field` does not correspond to `lattice`
249 fn new(
250 lattice: LatticeCyclic<D>,
251 beta: Real,
252 e_field: EField<D>,
253 link_matrix: LinkMatrix,
254 t: usize,
255 ) -> Result<Self, Self::Error>;
256
257 /// Create a new state with e_field randomly distributed as [`rand_distr::Normal`]^.
258 ///
259 /// # Errors
260 /// Gives an error if N(0, 0.5/beta ) is not a valid distribution (for example beta = 0)
261 /// or propagate the error from [`LatticeStateWithEFieldNew::new`]
262 fn new_random_e<R>(
263 lattice: LatticeCyclic<D>,
264 beta: Real,
265 link_matrix: LinkMatrix,
266 rng: &mut R,
267 ) -> Result<Self, Self::Error>
268 where
269 R: rand::Rng + ?Sized,
270 {
271 // TODO verify
272 let d = rand_distr::Normal::new(0_f64, 0.5_f64 / beta)?;
273 let e_field = EField::new_determinist(&lattice, rng, &d)
274 .project_to_gauss(&link_matrix, &lattice)
275 .expect("Projection to gauss failed");
276 Self::new(lattice, beta, e_field, link_matrix, 0)
277 }
278}
279
280/// [`LatticeStateWithEField`] who represent link matrices at the same time position as
281/// its conjugate momenta
282/// `e_field`.
283///
284/// If you have a LatticeState and want the default way of adding the conjugate momenta and doing
285/// simulation look at
286/// [`LatticeStateEFSyncDefault`].
287///
288/// I would advice of implementing this trait and not [`SimulationStateLeapFrog`], as there is
289/// a wrapper ([`SimulationStateLeap`]) for [`SimulationStateLeapFrog`].
290/// Also not implementing both trait gives you a compile time verification that you did not
291/// considered a leap frog state as a sync one.
292pub trait SimulationStateSynchronous<const D: usize>
293where
294 Self: LatticeStateWithEField<D> + Clone,
295{
296 /// does half a step for the conjugate momenta.
297 ///
298 /// # Errors
299 /// Return an error if the integration could not be done.
300 /// # Example
301 /// see [`SimulationStateLeapFrog::simulate_leap`]
302 fn simulate_to_leapfrog<I, State>(
303 &self,
304 integrator: &I,
305 delta_t: Real,
306 ) -> Result<State, I::Error>
307 where
308 State: SimulationStateLeapFrog<D>,
309 I: SymplecticIntegrator<Self, State, D> + ?Sized,
310 {
311 integrator.integrate_sync_leap(self, delta_t)
312 }
313
314 /// Does `number_of_steps` with `delta_t` at each step using a leap_frog algorithm by fist
315 /// doing half a step and then finishing by doing half step.
316 ///
317 /// # Errors
318 /// Return an error if the integration could not be done
319 /// or [`MultiIntegrationError::ZeroIntegration`] is the number of step is zero.
320 // TODO example
321 fn simulate_using_leapfrog_n<I, State>(
322 &self,
323 integrator: &I,
324 delta_t: Real,
325 number_of_steps: usize,
326 ) -> Result<Self, MultiIntegrationError<I::Error>>
327 where
328 State: SimulationStateLeapFrog<D>,
329 I: SymplecticIntegrator<Self, State, D> + ?Sized,
330 {
331 if number_of_steps == 0 {
332 return Err(MultiIntegrationError::ZeroIntegration);
333 }
334 let mut state_leap = self
335 .simulate_to_leapfrog(integrator, delta_t)
336 .map_err(|error| MultiIntegrationError::IntegrationError(0, error))?;
337 if number_of_steps > 1 {
338 let result = state_leap.simulate_leap_n(integrator, delta_t, number_of_steps - 1);
339 match result {
340 Ok(state) => state_leap = state,
341 Err(error) => {
342 match error {
343 MultiIntegrationError::IntegrationError(i, error) => {
344 return Err(MultiIntegrationError::IntegrationError(i + 1, error))
345 }
346 MultiIntegrationError::ZeroIntegration => {
347 // We cannot have 0 step integration as it is verified by the if
348 unreachable!();
349 }
350 }
351 }
352 }
353 }
354 let state_sync = state_leap
355 .simulate_to_synchronous(integrator, delta_t)
356 .map_err(|error| MultiIntegrationError::IntegrationError(number_of_steps, error))?;
357 Ok(state_sync)
358 }
359
360 /// Does the same thing as [`SimulationStateSynchronous::simulate_using_leapfrog_n`]
361 /// but use the default wrapper [`SimulationStateLeap`] for the leap frog state.
362 ///
363 /// # Errors
364 /// Return an error if the integration could not be done.
365 // TODO example
366 fn simulate_using_leapfrog_n_auto<I>(
367 &self,
368 integrator: &I,
369 delta_t: Real,
370 number_of_steps: usize,
371 ) -> Result<Self, MultiIntegrationError<I::Error>>
372 where
373 I: SymplecticIntegrator<Self, SimulationStateLeap<Self, D>, D> + ?Sized,
374 {
375 self.simulate_using_leapfrog_n(integrator, delta_t, number_of_steps)
376 }
377
378 /// Does a simulation step using the sync algorithm
379 ///
380 /// # Errors
381 /// Return an error if the integration could not be done.
382 // TODO example
383 fn simulate_sync<I, T>(&self, integrator: &I, delta_t: Real) -> Result<Self, I::Error>
384 where
385 I: SymplecticIntegrator<Self, T, D> + ?Sized,
386 T: SimulationStateLeapFrog<D>,
387 {
388 integrator.integrate_sync_sync(self, delta_t)
389 }
390
391 /// Does `numbers_of_times` of step of size `delta_t` using the sync algorithm
392 ///
393 /// # Errors
394 /// Return an error if the integration could not be done
395 /// or [`MultiIntegrationError::ZeroIntegration`] is the number of step is zero.
396 // TODO example
397 fn simulate_sync_n<I, T>(
398 &self,
399 integrator: &I,
400 delta_t: Real,
401 numbers_of_times: usize,
402 ) -> Result<Self, MultiIntegrationError<I::Error>>
403 where
404 I: SymplecticIntegrator<Self, T, D> + ?Sized,
405 T: SimulationStateLeapFrog<D>,
406 {
407 if numbers_of_times == 0 {
408 return Err(MultiIntegrationError::ZeroIntegration);
409 }
410 let mut state = self
411 .simulate_sync(integrator, delta_t)
412 .map_err(|error| MultiIntegrationError::IntegrationError(0, error))?;
413 for i in 1..numbers_of_times {
414 state = state
415 .simulate_sync(integrator, delta_t)
416 .map_err(|error| MultiIntegrationError::IntegrationError(i, error))?;
417 }
418 Ok(state)
419 }
420
421 /// Integrate the state using the symplectic algorithm ( by going to leapfrog and back to sync)
422 ///
423 /// # Errors
424 /// Return an error if the integration could not be done
425 ///
426 /// # Example
427 /// ```
428 /// # use std::error::Error;
429 /// #
430 /// # fn main() -> Result<(), Box<dyn Error>> {
431 /// use lattice_qcd_rs::integrator::{SymplecticEulerRayon, SymplecticIntegrator};
432 /// use lattice_qcd_rs::simulation::{
433 /// LatticeStateDefault, LatticeStateEFSyncDefault, LatticeStateWithEField,
434 /// SimulationStateSynchronous,
435 /// };
436 /// use rand::SeedableRng;
437 ///
438 /// let mut rng = rand::rngs::StdRng::seed_from_u64(0); // change with your seed
439 /// let mut state = LatticeStateEFSyncDefault::new_random_e_state(
440 /// LatticeStateDefault::<3>::new_determinist(1_f64, 2_f64, 4, &mut rng)?,
441 /// &mut rng,
442 /// );
443 /// let h = state.hamiltonian_total();
444 ///
445 /// let integrator = SymplecticEulerRayon::default();
446 /// for _ in 0..1 {
447 /// // Realistically you would want more steps
448 /// state = state.simulate_symplectic(&integrator, 0.000_001_f64)?;
449 /// }
450 /// let h2 = state.hamiltonian_total();
451 ///
452 /// println!("The error on the Hamiltonian is {}", h - h2);
453 /// # Ok(())
454 /// # }
455 /// ```
456 fn simulate_symplectic<I, T>(&self, integrator: &I, delta_t: Real) -> Result<Self, I::Error>
457 where
458 I: SymplecticIntegrator<Self, T, D> + ?Sized,
459 T: SimulationStateLeapFrog<D>,
460 {
461 integrator.integrate_symplectic(self, delta_t)
462 }
463
464 /// Does `numbers_of_times` of step of size `delta_t` using the symplectic algorithm
465 ///
466 /// # Errors
467 /// Return an error if the integration could not be done
468 /// or [`MultiIntegrationError::ZeroIntegration`] is the number of step is zero.
469 // TODO example
470 fn simulate_symplectic_n<I, T>(
471 &self,
472 integrator: &I,
473 delta_t: Real,
474 numbers_of_times: usize,
475 ) -> Result<Self, MultiIntegrationError<I::Error>>
476 where
477 I: SymplecticIntegrator<Self, T, D> + ?Sized,
478 T: SimulationStateLeapFrog<D>,
479 {
480 if numbers_of_times == 0 {
481 return Err(MultiIntegrationError::ZeroIntegration);
482 }
483 let mut state = self
484 .simulate_symplectic(integrator, delta_t)
485 .map_err(|error| MultiIntegrationError::IntegrationError(0, error))?;
486 for i in 1..numbers_of_times {
487 state = state
488 .simulate_symplectic(integrator, delta_t)
489 .map_err(|error| MultiIntegrationError::IntegrationError(i, error))?;
490 }
491 Ok(state)
492 }
493
494 /// Does the same thing as [`SimulationStateSynchronous::simulate_symplectic_n`]
495 /// but use the default wrapper [`SimulationStateLeap`] for the leap frog state.
496 ///
497 /// # Errors
498 /// Return an error if the integration could not be done.
499 // TODO example
500 fn simulate_symplectic_n_auto<I>(
501 &self,
502 integrator: &I,
503 delta_t: Real,
504 number_of_steps: usize,
505 ) -> Result<Self, MultiIntegrationError<I::Error>>
506 where
507 I: SymplecticIntegrator<Self, SimulationStateLeap<Self, D>, D> + ?Sized,
508 {
509 self.simulate_symplectic_n(integrator, delta_t, number_of_steps)
510 }
511}
512
513/// [`LatticeStateWithEField`] who represent link matrices at time T and its conjugate
514/// momenta at time T + 1/2.
515///
516/// If you have a [`SimulationStateSynchronous`] look at the wrapper [`SimulationStateLeap`].
517pub trait SimulationStateLeapFrog<const D: usize>
518where
519 Self: LatticeStateWithEField<D>,
520{
521 /// Simulate the state to synchronous by finishing the half step.
522 ///
523 /// # Errors
524 /// Return an error if the integration could not be done.
525 ///
526 /// # Example
527 /// see [`SimulationStateLeapFrog::simulate_leap`]
528 fn simulate_to_synchronous<I, State>(
529 &self,
530 integrator: &I,
531 delta_t: Real,
532 ) -> Result<State, I::Error>
533 where
534 Self: Sized,
535 State: SimulationStateSynchronous<D> + ?Sized,
536 I: SymplecticIntegrator<State, Self, D> + ?Sized,
537 {
538 integrator.integrate_leap_sync(self, delta_t)
539 }
540
541 /// Does one simulation step using the leap frog algorithm.
542 ///
543 /// # Errors
544 /// Return an error if the integration could not be done.
545 ///
546 /// # Example
547 /// ```
548 /// # use std::error::Error;
549 /// #
550 /// # fn main() -> Result<(), Box<dyn Error>> {
551 /// use lattice_qcd_rs::integrator::{SymplecticEulerRayon, SymplecticIntegrator};
552 /// use lattice_qcd_rs::simulation::{
553 /// LatticeStateDefault, LatticeStateEFSyncDefault, LatticeStateWithEField, SimulationStateSynchronous, SimulationStateLeapFrog,
554 /// };
555 /// use rand::SeedableRng;
556 ///
557 /// let mut rng = rand::rngs::StdRng::seed_from_u64(0); // change with your seed
558 /// let state = LatticeStateEFSyncDefault::new_random_e_state(
559 /// LatticeStateDefault::<3>::new_determinist(1_f64, 2_f64, 4, &mut rng)?,
560 /// &mut rng,
561 /// );
562 /// let h = state.hamiltonian_total();
563 /// let integrator = SymplecticEulerRayon::default();
564 /// let mut leap_frog = state.simulate_to_leapfrog(&integrator,0.000_001_f64)?;
565 /// drop(state);
566 /// for _ in 0..2 {
567 /// // Realistically you would want more steps
568 /// leap_frog = leap_frog.simulate_leap(&integrator, 0.000_001_f64)?;
569 /// }
570 /// let state = leap_frog.simulate_to_synchronous(&integrator, 0.000_001_f64)?;
571 /// let h2 = state.hamiltonian_total();
572 ///
573 /// println!("The error on the Hamiltonian is {}", h - h2);
574 /// # Ok(())
575 /// # }
576 /// ```
577 fn simulate_leap<I, T>(&self, integrator: &I, delta_t: Real) -> Result<Self, I::Error>
578 where
579 Self: Sized,
580 I: SymplecticIntegrator<T, Self, D> + ?Sized,
581 T: SimulationStateSynchronous<D> + ?Sized,
582 {
583 integrator.integrate_leap_leap(self, delta_t)
584 }
585
586 /// does `numbers_of_times` simulation set of size `delta_t` using the leap frog algorithm.
587 ///
588 /// # Errors
589 /// Return an error if the integration could not be done
590 /// or [`MultiIntegrationError::ZeroIntegration`] is the number of step is zero.
591 ///
592 /// # Example
593 /// /// # Example
594 /// ```
595 /// # use std::error::Error;
596 /// #
597 /// # fn main() -> Result<(), Box<dyn Error>> {
598 /// use lattice_qcd_rs::integrator::{SymplecticEulerRayon, SymplecticIntegrator};
599 /// use lattice_qcd_rs::simulation::{
600 /// LatticeStateDefault, LatticeStateEFSyncDefault, LatticeStateWithEField, SimulationStateSynchronous, SimulationStateLeapFrog,
601 /// };
602 /// use rand::SeedableRng;
603 ///
604 /// let mut rng = rand::rngs::StdRng::seed_from_u64(0); // change with your seed
605 /// let state = LatticeStateEFSyncDefault::new_random_e_state(
606 /// LatticeStateDefault::<3>::new_determinist(1_f64, 2_f64, 4, &mut rng)?,
607 /// &mut rng,
608 /// );
609 /// let h = state.hamiltonian_total();
610 /// let integrator = SymplecticEulerRayon::default();
611 /// let mut leap_frog = state.simulate_to_leapfrog(&integrator,0.000_001_f64)?;
612 /// drop(state);
613 ///
614 /// // Realistically you would want more steps
615 /// leap_frog = leap_frog.simulate_leap_n(&integrator, 0.000_001_f64, 10)?;
616 ///
617 /// let state = leap_frog.simulate_to_synchronous(&integrator, 0.000_001_f64)?;
618 /// let h2 = state.hamiltonian_total();
619 ///
620 /// println!("The error on the Hamiltonian is {}", h - h2);
621 /// # Ok(())
622 /// # }
623 /// ```
624 fn simulate_leap_n<I, T>(
625 &self,
626 integrator: &I,
627 delta_t: Real,
628 numbers_of_times: usize,
629 ) -> Result<Self, MultiIntegrationError<I::Error>>
630 where
631 Self: Sized,
632 I: SymplecticIntegrator<T, Self, D> + ?Sized,
633 T: SimulationStateSynchronous<D> + ?Sized,
634 {
635 if numbers_of_times == 0 {
636 return Err(MultiIntegrationError::ZeroIntegration);
637 }
638 let mut state = self
639 .simulate_leap(integrator, delta_t)
640 .map_err(|error| MultiIntegrationError::IntegrationError(0, error))?;
641 for i in 1..(numbers_of_times) {
642 state = state
643 .simulate_leap(integrator, delta_t)
644 .map_err(|error| MultiIntegrationError::IntegrationError(i, error))?;
645 }
646 Ok(state)
647 }
648}
649
650/// Represent a simulation state at a set time.
651///
652/// It has the default pure gauge hamiltonian
653#[derive(Debug, PartialEq, Clone)]
654#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
655pub struct LatticeStateDefault<const D: usize> {
656 lattice: LatticeCyclic<D>,
657 beta: Real,
658 link_matrix: LinkMatrix,
659}
660
661impl<const D: usize> LatticeStateDefault<D> {
662 /// Create a cold configuration. i.e. all the links are set to the unit matrix.
663 ///
664 /// With the lattice of size `size` and dimension `number_of_points` ( see [`LatticeCyclic::new`] )
665 /// and beta parameter `beta`.
666 ///
667 /// # Errors
668 /// Returns [`StateInitializationError::LatticeInitializationError`] if the parameter is invalid
669 /// for [`LatticeCyclic`].
670 /// Or propagate the error form [`Self::new`].
671 pub fn new_cold(
672 size: Real,
673 beta: Real,
674 number_of_points: usize,
675 ) -> Result<Self, StateInitializationError> {
676 let lattice = LatticeCyclic::new(size, number_of_points)?;
677 let link_matrix = LinkMatrix::new_cold(&lattice);
678 Self::new(lattice, beta, link_matrix)
679 }
680
681 /// Create a "hot" configuration, i.e. the link matrices are chosen randomly.
682 ///
683 /// With the lattice of size `size` and dimension `number_of_points` ( see [`LatticeCyclic::new`] )
684 /// and beta parameter `beta`.
685 ///
686 /// The creation is determinists meaning that it is reproducible:
687 ///
688 /// # Errors
689 /// Returns [`StateInitializationError::LatticeInitializationError`] if the parameter is invalid for [`LatticeCyclic`].
690 /// Or propagate the error form [`Self::new`].
691 ///
692 /// # Example
693 /// This example demonstrate how to reproduce the same configuration
694 /// ```
695 /// # use lattice_qcd_rs::{simulation::LatticeStateDefault, lattice::LatticeCyclic, dim};
696 /// use rand::{rngs::StdRng, SeedableRng};
697 ///
698 /// let mut rng_1 = StdRng::seed_from_u64(0);
699 /// let mut rng_2 = StdRng::seed_from_u64(0);
700 /// // They have the same seed and should generate the same numbers
701 /// assert_eq!(
702 /// LatticeStateDefault::<4>::new_determinist(1_f64, 1_f64, 4, &mut rng_1).unwrap(),
703 /// LatticeStateDefault::<4>::new_determinist(1_f64, 1_f64, 4, &mut rng_2).unwrap()
704 /// );
705 /// ```
706 pub fn new_determinist(
707 size: Real,
708 beta: Real,
709 number_of_points: usize,
710 rng: &mut impl rand::Rng,
711 ) -> Result<Self, StateInitializationError> {
712 let lattice = LatticeCyclic::new(size, number_of_points)?;
713 let link_matrix = LinkMatrix::new_determinist(&lattice, rng);
714 Self::new(lattice, beta, link_matrix)
715 }
716
717 /// Correct the numerical drift, reprojecting all the link matrices to SU(3).
718 /// see [`LinkMatrix::normalize`].
719 ///
720 /// # Example
721 /// ```
722 /// use lattice_qcd_rs::error::ImplementationError;
723 /// use lattice_qcd_rs::prelude::*;
724 /// use rand::SeedableRng;
725 /// # use std::error::Error;
726 ///
727 /// # fn main() -> Result<(), Box<dyn Error>> {
728 /// let mut rng = rand::rngs::StdRng::seed_from_u64(0); // change with your seed
729 ///
730 /// let size = 1_f64;
731 /// let number_of_pts = 3;
732 /// let beta = 1_f64;
733 ///
734 /// let mut simulation =
735 /// LatticeStateDefault::<4>::new_determinist(size, beta, number_of_pts, &mut rng)?;
736 ///
737 /// let spread_parameter = 0.1_f64;
738 /// let mut mc = MetropolisHastingsSweep::new(1, spread_parameter, rng)
739 /// .ok_or(ImplementationError::OptionWithUnexpectedNone)?;
740 ///
741 /// for _ in 0..2 {
742 /// for _ in 0..10 {
743 /// simulation = simulation.monte_carlo_step(&mut mc)?;
744 /// }
745 /// // the more we advance te more the link matrices
746 /// // will deviate form SU(3), so we reproject to SU(3)
747 /// // every 10 steps.
748 /// simulation.normalize_link_matrices();
749 /// }
750 /// #
751 /// # Ok(())
752 /// # }
753 /// ```
754 pub fn normalize_link_matrices(&mut self) {
755 self.link_matrix.normalize();
756 }
757
758 /// Get a mutable reference to the link matrix at `link`
759 pub fn link_mut(&mut self, link: &LatticeLinkCanonical<D>) -> Option<&mut CMatrix3> {
760 let index = link.to_index(&self.lattice);
761 if index < self.link_matrix.len() {
762 Some(&mut self.link_matrix[index])
763 }
764 else {
765 None
766 }
767 }
768
769 /// Absorbs self anf return the link_matrix as owned
770 #[allow(clippy::missing_const_for_fn)] // false positive
771 pub fn link_matrix_owned(self) -> LinkMatrix {
772 self.link_matrix
773 }
774}
775
776impl<const D: usize> LatticeStateNew<D> for LatticeStateDefault<D> {
777 type Error = StateInitializationError;
778
779 fn new(
780 lattice: LatticeCyclic<D>,
781 beta: Real,
782 link_matrix: LinkMatrix,
783 ) -> Result<Self, Self::Error> {
784 if !lattice.has_compatible_length_links(&link_matrix) {
785 return Err(StateInitializationError::IncompatibleSize);
786 }
787 Ok(Self {
788 lattice,
789 beta,
790 link_matrix,
791 })
792 }
793}
794
795impl<const D: usize> LatticeState<D> for LatticeStateDefault<D> {
796 const CA: Real = 3_f64;
797
798 getter!(
799 /// The link matrices of this state.
800 link_matrix,
801 LinkMatrix
802 );
803
804 getter!(lattice, LatticeCyclic<D>);
805
806 getter_copy!(beta, Real);
807
808 /// # Panic
809 /// Panic if the length of link_matrix is different from `lattice.get_number_of_canonical_links_space()`
810 fn set_link_matrix(&mut self, link_matrix: LinkMatrix) {
811 if self.lattice.number_of_canonical_links_space() != link_matrix.len() {
812 panic!("Link matrices are not of the correct size");
813 }
814 self.link_matrix = link_matrix;
815 }
816
817 /// Get the default pure gauge Hamiltonian.
818 ///
819 /// # Panic
820 /// Panic if plaquettes cannot be found
821 fn hamiltonian_links(&self) -> Real {
822 // here it is ok to use par_bridge() as we do not care for the order
823 self.lattice()
824 .get_points()
825 .par_bridge()
826 .map(|el| {
827 Direction::positive_directions()
828 .iter()
829 .map(|dir_i| {
830 Direction::positive_directions()
831 .iter()
832 .filter(|dir_j| dir_i.index() < dir_j.index())
833 .map(|dir_j| {
834 1_f64
835 - self
836 .link_matrix()
837 .pij(&el, dir_i, dir_j, self.lattice())
838 .expect("Plaquette not found")
839 .trace()
840 .real()
841 / Self::CA
842 })
843 .sum::<Real>()
844 })
845 .sum::<Real>()
846 })
847 .sum::<Real>()
848 * self.beta()
849 }
850}
851
852/// wrapper for a simulation state using leap frog ([`SimulationStateLeap`]) using a synchronous type
853/// ([`SimulationStateSynchronous`]).
854#[derive(Debug, PartialEq, Clone, Eq, PartialOrd, Ord, Hash)]
855#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
856pub struct SimulationStateLeap<State, const D: usize>
857where
858 State: SimulationStateSynchronous<D> + ?Sized,
859{
860 state: State,
861}
862
863impl<State, const D: usize> SimulationStateLeap<State, D>
864where
865 State: SimulationStateSynchronous<D> + LatticeStateWithEField<D> + ?Sized,
866{
867 getter!(
868 /// get a reference to the state
869 pub const,
870 state,
871 State
872 );
873
874 /// Create a new SimulationStateLeap directly from a state without applying any modification.
875 ///
876 /// In most cases wou will prefer to build it using [`LatticeStateNew`] or [`Self::from_synchronous`].
877 pub const fn new_from_state(state: State) -> Self {
878 Self { state }
879 }
880
881 /// get a mutable reference to the state
882 pub fn state_mut(&mut self) -> &mut State {
883 &mut self.state
884 }
885
886 /// Create a leap state from a sync one by integrating by half a step the e_field.
887 ///
888 /// # Errors
889 /// Returns an error if the integration failed.
890 pub fn from_synchronous<I>(s: &State, integrator: &I, delta_t: Real) -> Result<Self, I::Error>
891 where
892 I: SymplecticIntegrator<State, Self, D> + ?Sized,
893 {
894 s.simulate_to_leapfrog(integrator, delta_t)
895 }
896
897 /// Get the gauss coefficient `G(x) = \sum_i E_i(x) - U_{-i}(x) E_i(x - i) U^\dagger_{-i}(x)`.
898 pub fn gauss(&self, point: &LatticePoint<D>) -> Option<CMatrix3> {
899 self.e_field()
900 .gauss(self.link_matrix(), point, self.lattice())
901 }
902}
903
904impl<State, const D: usize> Default for SimulationStateLeap<State, D>
905where
906 State: SimulationStateSynchronous<D> + Default + ?Sized,
907{
908 fn default() -> Self {
909 Self::new_from_state(State::default())
910 }
911}
912
913impl<State, const D: usize> std::fmt::Display for SimulationStateLeap<State, D>
914where
915 State: SimulationStateSynchronous<D> + std::fmt::Display + ?Sized,
916{
917 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
918 write!(f, "leapfrog {}", self.state())
919 }
920}
921
922impl<State: SimulationStateSynchronous<D> + LatticeStateWithEField<D>, const D: usize> AsRef<State>
923 for SimulationStateLeap<State, D>
924{
925 fn as_ref(&self) -> &State {
926 self.state()
927 }
928}
929
930impl<State: SimulationStateSynchronous<D> + LatticeStateWithEField<D>, const D: usize> AsMut<State>
931 for SimulationStateLeap<State, D>
932{
933 fn as_mut(&mut self) -> &mut State {
934 self.state_mut()
935 }
936}
937
938/// This state is a leap frog state
939impl<State, const D: usize> SimulationStateLeapFrog<D> for SimulationStateLeap<State, D> where
940 State: SimulationStateSynchronous<D> + LatticeStateWithEField<D> + ?Sized
941{
942}
943
944/// We just transmit the function of `State`, there is nothing new.
945impl<State, const D: usize> LatticeState<D> for SimulationStateLeap<State, D>
946where
947 State: LatticeStateWithEField<D> + SimulationStateSynchronous<D> + ?Sized,
948{
949 const CA: Real = State::CA;
950
951 /// The link matrices of this state.
952 fn link_matrix(&self) -> &LinkMatrix {
953 self.state().link_matrix()
954 }
955
956 /// # Panic
957 /// panic under the same condition as `State::set_link_matrix`
958 fn set_link_matrix(&mut self, link_matrix: LinkMatrix) {
959 self.state.set_link_matrix(link_matrix);
960 }
961
962 fn lattice(&self) -> &LatticeCyclic<D> {
963 self.state().lattice()
964 }
965
966 fn beta(&self) -> Real {
967 self.state().beta()
968 }
969
970 fn hamiltonian_links(&self) -> Real {
971 self.state().hamiltonian_links()
972 }
973}
974
975impl<State, const D: usize> LatticeStateWithEFieldNew<D> for SimulationStateLeap<State, D>
976where
977 State: LatticeStateWithEField<D> + SimulationStateSynchronous<D> + LatticeStateWithEFieldNew<D>,
978{
979 type Error = State::Error;
980
981 fn new(
982 lattice: LatticeCyclic<D>,
983 beta: Real,
984 e_field: EField<D>,
985 link_matrix: LinkMatrix,
986 t: usize,
987 ) -> Result<Self, Self::Error> {
988 let state = State::new(lattice, beta, e_field, link_matrix, t)?;
989 Ok(Self { state })
990 }
991}
992
993/// We just transmit the function of `State`, there is nothing new.
994impl<State, const D: usize> LatticeStateWithEField<D> for SimulationStateLeap<State, D>
995where
996 State: LatticeStateWithEField<D> + SimulationStateSynchronous<D> + ?Sized,
997{
998 project!(hamiltonian_efield, state, Real);
999
1000 project!(
1001 /// The "Electrical" field of this state.
1002 e_field,
1003 state,
1004 &EField<D>
1005 );
1006
1007 project_mut!(
1008 /// # Panic
1009 /// panic under the same condition as `State::set_e_field`
1010 set_e_field,
1011 state,
1012 (),
1013 e_field: EField<D>
1014 );
1015
1016 project!(
1017 /// return the time state, i.e. the number of time the simulation ran.
1018 t,
1019 state,
1020 usize
1021 );
1022
1023 fn derivative_u(
1024 link: &LatticeLinkCanonical<D>,
1025 link_matrix: &LinkMatrix,
1026 e_field: &EField<D>,
1027 lattice: &LatticeCyclic<D>,
1028 ) -> Option<CMatrix3> {
1029 State::derivative_u(link, link_matrix, e_field, lattice)
1030 }
1031
1032 fn derivative_e(
1033 point: &LatticePoint<D>,
1034 link_matrix: &LinkMatrix,
1035 e_field: &EField<D>,
1036 lattice: &LatticeCyclic<D>,
1037 ) -> Option<SVector<Su3Adjoint, D>> {
1038 State::derivative_e(point, link_matrix, e_field, lattice)
1039 }
1040}
1041
1042/// wrapper to implement [`LatticeStateWithEField`] from a [`LatticeState`] using
1043/// the default implementation of conjugate momenta.
1044///
1045/// It also implement [`SimulationStateSynchronous`].
1046#[derive(Debug, PartialEq, Clone)]
1047#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
1048pub struct LatticeStateEFSyncDefault<State, const D: usize>
1049where
1050 State: LatticeState<D> + ?Sized,
1051{
1052 #[cfg_attr(
1053 feature = "serde-serialize",
1054 serde(bound(
1055 serialize = "SVector<Su3Adjoint, D>: Serialize",
1056 deserialize = "SVector<Su3Adjoint, D>: Deserialize<'de>"
1057 ))
1058 )]
1059 e_field: EField<D>,
1060 t: usize,
1061 lattice_state: State, // the DST must be at the end
1062}
1063
1064impl<State, const D: usize> LatticeStateEFSyncDefault<State, D>
1065where
1066 State: LatticeState<D> + ?Sized,
1067{
1068 /// Absorbs self and return the state as owned.
1069 /// It essentially deconstruct the structure.
1070 #[allow(clippy::missing_const_for_fn)] // false positive
1071 pub fn state_owned(self) -> State
1072 where
1073 State: Sized,
1074 {
1075 self.lattice_state
1076 }
1077
1078 /// Get a reference to the state.
1079 pub const fn lattice_state(&self) -> &State {
1080 &self.lattice_state
1081 }
1082
1083 /// Get a mutable reference to the state.
1084 pub fn lattice_state_mut(&mut self) -> &mut State {
1085 &mut self.lattice_state
1086 }
1087
1088 /// Take a state and generate a new random one and try projecting it to the Gauss law.
1089 ///
1090 /// # Panic
1091 /// Panics if N(0, 0.5/beta ) is not a valid distribution (for example beta = 0).
1092 /// Panics if the field could not be projected to the Gauss law.
1093 pub fn new_random_e_state(lattice_state: State, rng: &mut impl rand::Rng) -> Self
1094 where
1095 State: Sized,
1096 {
1097 let d = rand_distr::Normal::new(0_f64, 0.5_f64 / lattice_state.beta())
1098 .expect("Distribution not valid, check Beta.");
1099 let e_field = EField::new_determinist(lattice_state.lattice(), rng, &d)
1100 .project_to_gauss(lattice_state.link_matrix(), lattice_state.lattice())
1101 .unwrap();
1102 // TODO error management
1103 Self {
1104 lattice_state,
1105 e_field,
1106 t: 0,
1107 }
1108 }
1109
1110 /// Create a new Self from a state and a cold configuration of the e field (i.e. set to 0)
1111 pub fn new_e_cold(lattice_state: State) -> Self
1112 where
1113 State: Sized,
1114 {
1115 let e_field = EField::new_cold(lattice_state.lattice());
1116 Self {
1117 lattice_state,
1118 e_field,
1119 t: 0,
1120 }
1121 }
1122
1123 /// Get a mutable reference to the efield
1124 pub fn e_field_mut(&mut self) -> &mut EField<D> {
1125 &mut self.e_field
1126 }
1127}
1128
1129impl<State, const D: usize> LatticeStateEFSyncDefault<State, D>
1130where
1131 Self: LatticeStateWithEField<D>,
1132 State: LatticeState<D> + ?Sized,
1133{
1134 /// Get the gauss coefficient `G(x) = \sum_i E_i(x) - U_{-i}(x) E_i(x - i) U^\dagger_{-i}(x)`.
1135 pub fn gauss(&self, point: &LatticePoint<D>) -> Option<CMatrix3> {
1136 self.e_field
1137 .gauss(self.link_matrix(), point, self.lattice())
1138 }
1139}
1140
1141impl<State, const D: usize> LatticeStateEFSyncDefault<State, D>
1142where
1143 Self: LatticeStateWithEFieldNew<D>,
1144 <Self as LatticeStateWithEFieldNew<D>>::Error: From<LatticeInitializationError>,
1145 State: LatticeState<D>,
1146{
1147 /// Generate a hot (i.e. random) initial state.
1148 ///
1149 /// Single threaded generation with a given random number generator.
1150 /// `size` is the size parameter of the lattice and `number_of_points` is the number of points
1151 /// in each spatial dimension of the lattice. See [`LatticeCyclic::new`] for more info.
1152 ///
1153 /// useful to reproduce a set of data but slower than
1154 /// [`LatticeStateEFSyncDefault::new_random_threaded`].
1155 ///
1156 /// # Errors
1157 /// Return [`StateInitializationError::LatticeInitializationError`] if the parameter is invalid
1158 /// for [`LatticeCyclic`].
1159 /// Or propagates the error form [`Self::new`].
1160 ///
1161 /// # Example
1162 /// ```
1163 /// # use lattice_qcd_rs::{simulation::{LatticeStateEFSyncDefault, LatticeStateDefault}, lattice::LatticeCyclic};
1164 /// use rand::{SeedableRng,rngs::StdRng};
1165 ///
1166 /// let mut rng_1 = StdRng::seed_from_u64(0);
1167 /// let mut rng_2 = StdRng::seed_from_u64(0);
1168 /// // They have the same seed and should generate the same numbers
1169 /// let distribution = rand::distributions::Uniform::from(-1_f64..1_f64);
1170 /// assert_eq!(
1171 /// LatticeStateEFSyncDefault::<LatticeStateDefault<4>, 4>::new_determinist(1_f64, 1_f64, 4, &mut rng_1, &distribution).unwrap(),
1172 /// LatticeStateEFSyncDefault::<LatticeStateDefault<4>, 4>::new_determinist(1_f64, 1_f64, 4, &mut rng_2, &distribution).unwrap()
1173 /// );
1174 /// ```
1175 pub fn new_determinist<R>(
1176 size: Real,
1177 beta: Real,
1178 number_of_points: usize,
1179 rng: &mut R,
1180 d: &impl rand_distr::Distribution<Real>,
1181 ) -> Result<Self, <Self as LatticeStateWithEFieldNew<D>>::Error>
1182 where
1183 R: rand::Rng + ?Sized,
1184 {
1185 let lattice = LatticeCyclic::new(size, number_of_points)?;
1186 let e_field = EField::new_determinist(&lattice, rng, d);
1187 let link_matrix = LinkMatrix::new_determinist(&lattice, rng);
1188 Self::new(lattice, beta, e_field, link_matrix, 0)
1189 }
1190
1191 /// Generate a configuration with cold e_field and hot link matrices
1192 ///
1193 /// # Errors
1194 /// Return [`StateInitializationError::LatticeInitializationError`] if the parameter is invalid
1195 /// for [`LatticeCyclic`].
1196 /// Or propagates the error form [`Self::new`].
1197 pub fn new_determinist_cold_e_hot_link<R>(
1198 size: Real,
1199 beta: Real,
1200 number_of_points: usize,
1201 rng: &mut R,
1202 ) -> Result<Self, <Self as LatticeStateWithEFieldNew<D>>::Error>
1203 where
1204 R: rand::Rng + ?Sized,
1205 {
1206 let lattice = LatticeCyclic::new(size, number_of_points)?;
1207 let e_field = EField::new_cold(&lattice);
1208 let link_matrix = LinkMatrix::new_determinist(&lattice, rng);
1209
1210 Self::new(lattice, beta, e_field, link_matrix, 0)
1211 }
1212
1213 /// Generate a new cold state.
1214 ///
1215 /// It meas that the link matrices are set to the identity and electrical field are set to 0.
1216 ///
1217 /// # Errors
1218 /// Return [`StateInitializationError::LatticeInitializationError`] if the parameter is invalid
1219 /// for [`LatticeCyclic`].
1220 /// Or propagates the error form [`Self::new`].
1221 pub fn new_cold(
1222 size: Real,
1223 beta: Real,
1224 number_of_points: usize,
1225 ) -> Result<Self, <Self as LatticeStateWithEFieldNew<D>>::Error> {
1226 let lattice = LatticeCyclic::new(size, number_of_points)?;
1227 let link_matrix = LinkMatrix::new_cold(&lattice);
1228 let e_field = EField::new_cold(&lattice);
1229 Self::new(lattice, beta, e_field, link_matrix, 0)
1230 }
1231}
1232
1233impl<State, const D: usize> LatticeStateEFSyncDefault<State, D>
1234where
1235 Self: LatticeStateWithEFieldNew<D, Error = StateInitializationError>,
1236 State: LatticeState<D>,
1237{
1238 /// Generate a hot (i.e. random) initial state.
1239 ///
1240 /// Multi threaded generation of random data. Due to the non deterministic way threads
1241 /// operate a set cannot be reproduce easily, In that case use
1242 /// [`LatticeStateEFSyncDefault::new_determinist`].
1243 ///
1244 /// # Errors
1245 /// Return [`StateInitializationError::LatticeInitializationError`] if the parameter is invalid
1246 /// for [`LatticeCyclic`].
1247 /// Return [`ThreadError::ThreadNumberIncorrect`] if `number_of_points = 0`.
1248 /// Returns an error if a thread panicked. Finally, propagates the error form [`Self::new`].
1249 pub fn new_random_threaded<Distribution>(
1250 size: Real,
1251 beta: Real,
1252 number_of_points: usize,
1253 d: &Distribution,
1254 number_of_thread: usize,
1255 ) -> Result<Self, ThreadedStateInitializationError>
1256 where
1257 Distribution: rand_distr::Distribution<Real> + Sync,
1258 {
1259 if number_of_thread == 0 {
1260 return Err(ThreadedStateInitializationError::ThreadingError(
1261 ThreadError::ThreadNumberIncorrect,
1262 ));
1263 }
1264 else if number_of_thread == 1 {
1265 let mut rng = rand::thread_rng();
1266 return Self::new_determinist(size, beta, number_of_points, &mut rng, d)
1267 .map_err(|err| err.into());
1268 }
1269 let lattice = LatticeCyclic::new(size, number_of_points).map_err(|err| {
1270 ThreadedStateInitializationError::StateInitializationError(err.into())
1271 })?;
1272 thread::scope(|s| {
1273 let lattice_clone = lattice.clone();
1274 let handel = s.spawn(move |_| EField::new_random(&lattice_clone, d));
1275 let link_matrix = LinkMatrix::new_random_threaded(&lattice, number_of_thread - 1)?;
1276 let e_field = handel.join().map_err(|err| {
1277 ThreadedStateInitializationError::ThreadingError(
1278 ThreadAnyError::Panic(vec![err]).into(),
1279 )
1280 })?;
1281 // TODO not very clean: improve
1282 Self::new(lattice, beta, e_field, link_matrix, 0)
1283 .map_err(ThreadedStateInitializationError::StateInitializationError)
1284 })
1285 .map_err(|err| {
1286 ThreadedStateInitializationError::ThreadingError(
1287 ThreadAnyError::Panic(vec![err]).into(),
1288 )
1289 })?
1290 }
1291}
1292
1293/// This is an sync State
1294impl<State, const D: usize> SimulationStateSynchronous<D> for LatticeStateEFSyncDefault<State, D>
1295where
1296 State: LatticeState<D> + Clone + ?Sized,
1297 Self: LatticeStateWithEField<D>,
1298{
1299}
1300
1301impl<State, const D: usize> LatticeState<D> for LatticeStateEFSyncDefault<State, D>
1302where
1303 State: LatticeState<D> + ?Sized,
1304{
1305 const CA: Real = State::CA;
1306
1307 fn link_matrix(&self) -> &LinkMatrix {
1308 self.lattice_state.link_matrix()
1309 }
1310
1311 /// # Panic
1312 /// panic under the same condition as `State::set_link_matrix`
1313 fn set_link_matrix(&mut self, link_matrix: LinkMatrix) {
1314 self.lattice_state.set_link_matrix(link_matrix);
1315 }
1316
1317 fn lattice(&self) -> &LatticeCyclic<D> {
1318 self.lattice_state.lattice()
1319 }
1320
1321 fn beta(&self) -> Real {
1322 self.lattice_state.beta()
1323 }
1324
1325 fn hamiltonian_links(&self) -> Real {
1326 self.lattice_state.hamiltonian_links()
1327 }
1328}
1329
1330impl<State, const D: usize> LatticeStateWithEFieldNew<D> for LatticeStateEFSyncDefault<State, D>
1331where
1332 State: LatticeState<D> + LatticeStateNew<D>,
1333 Self: LatticeStateWithEField<D>,
1334 StateInitializationError: Into<State::Error>,
1335 State::Error: From<rand_distr::NormalError>,
1336{
1337 type Error = State::Error;
1338
1339 /// create a new simulation state. If `e_field` or `link_matrix` does not have the corresponding
1340 /// amount of data compared to lattice it fails to create the state.
1341 /// `t` is the number of time the simulation ran. i.e. the time sate.
1342 fn new(
1343 lattice: LatticeCyclic<D>,
1344 beta: Real,
1345 e_field: EField<D>,
1346 link_matrix: LinkMatrix,
1347 t: usize,
1348 ) -> Result<Self, Self::Error> {
1349 if !lattice.has_compatible_length_e_field(&e_field) {
1350 return Err(StateInitializationError::IncompatibleSize.into());
1351 }
1352 let lattice_state_r = State::new(lattice, beta, link_matrix);
1353 match lattice_state_r {
1354 Ok(lattice_state) => Ok(Self {
1355 e_field,
1356 t,
1357 lattice_state,
1358 }),
1359 Err(err) => Err(err),
1360 }
1361 }
1362}
1363
1364impl<const D: usize> LatticeStateWithEField<D>
1365 for LatticeStateEFSyncDefault<LatticeStateDefault<D>, D>
1366where
1367 Direction<D>: DirectionList,
1368{
1369 /// By default \sum_x Tr(E_i E_i)
1370 fn hamiltonian_efield(&self) -> Real {
1371 self.lattice()
1372 .get_points()
1373 .par_bridge()
1374 .map(|el| {
1375 Direction::positive_directions()
1376 .iter()
1377 .map(|dir_i| {
1378 let e_i = self.e_field().e_field(&el, dir_i, self.lattice()).unwrap();
1379 e_i.trace_squared()
1380 })
1381 .sum::<Real>()
1382 })
1383 .sum::<Real>()
1384 * self.beta()
1385 }
1386
1387 /// The "Electrical" field of this state.
1388 fn e_field(&self) -> &EField<D> {
1389 &self.e_field
1390 }
1391
1392 /// # Panic
1393 /// Panic if the length of link_matrix is different from `lattice.get_number_of_points()`
1394 fn set_e_field(&mut self, e_field: EField<D>) {
1395 if self.lattice().number_of_points() != e_field.len() {
1396 panic!("e_field is not of the correct size");
1397 }
1398 self.e_field = e_field;
1399 }
1400
1401 /// return the time state, i.e. the number of time the simulation ran.
1402 fn t(&self) -> usize {
1403 self.t
1404 }
1405
1406 /// Get the derive of U_i(x).
1407 fn derivative_u(
1408 link: &LatticeLinkCanonical<D>,
1409 link_matrix: &LinkMatrix,
1410 e_field: &EField<D>,
1411 lattice: &LatticeCyclic<D>,
1412 ) -> Option<CMatrix3> {
1413 let c = Complex::new(0_f64, (2_f64 * Self::CA).sqrt());
1414 let u_i = link_matrix.matrix(&LatticeLink::from(*link), lattice)?;
1415 let e_i = e_field.e_field(link.pos(), link.dir(), lattice)?;
1416 Some(e_i.to_matrix() * u_i * c * Complex::from(1_f64 / lattice.size()))
1417 }
1418
1419 /// Get the derive of E(x) (as a vector of Su3Adjoint).
1420 fn derivative_e(
1421 point: &LatticePoint<D>,
1422 link_matrix: &LinkMatrix,
1423 _e_field: &EField<D>,
1424 lattice: &LatticeCyclic<D>,
1425 ) -> Option<SVector<Su3Adjoint, D>> {
1426 let c = -(2_f64 / Self::CA).sqrt();
1427 let dir_pos = Direction::<D>::positive_directions();
1428 let iterator = dir_pos.iter().map(|dir| {
1429 let u_i = link_matrix.matrix(&LatticeLink::new(*point, *dir), lattice)?;
1430 let sum_s: CMatrix3 = Direction::<D>::directions()
1431 .iter()
1432 .filter(|dir_2| dir_2.to_positive() != *dir)
1433 .map(|dir_2| {
1434 link_matrix
1435 .sij(point, dir, dir_2, lattice)
1436 .map(|el| el.adjoint())
1437 })
1438 .sum::<Option<CMatrix3>>()?;
1439 Some(Su3Adjoint::new(Vector8::<Real>::from_fn(|index, _| {
1440 c * (su3::GENERATORS[index] * u_i * sum_s).trace().imaginary() / lattice.size()
1441 })))
1442 });
1443 let mut return_vector = SVector::<_, D>::from_element(Su3Adjoint::default());
1444 for (index, element) in iterator.enumerate() {
1445 return_vector[index] = element?;
1446 }
1447 Some(return_vector)
1448 }
1449}
1450
1451#[cfg(test)]
1452mod test {
1453
1454 use super::*;
1455 use crate::error::StateInitializationError;
1456
1457 #[test]
1458 fn leap_frog_simulation() -> Result<(), StateInitializationError> {
1459 let state =
1460 LatticeStateEFSyncDefault::<LatticeStateDefault<3>, 3>::new_cold(1_f64, 6_f64, 4)?;
1461
1462 let mut leap_frog = SimulationStateLeap::new_from_state(state.clone());
1463 assert_eq!(&state, leap_frog.as_ref());
1464
1465 assert_eq!(
1466 state.gauss(&LatticePoint::default()),
1467 leap_frog.gauss(&LatticePoint::default())
1468 );
1469
1470 let _: &mut LatticeStateEFSyncDefault<LatticeStateDefault<3>, 3> = leap_frog.as_mut();
1471 Ok(())
1472 }
1473}