lattice_qcd_rs/simulation/monte_carlo/mod.rs
1//! Module for Monte-Carlo algrorithme, see the trait [`MonteCarlo`].
2//!
3//! This is one of the way to carry out simulation. This work by taking a state and progressively changing it (most of the time randomly).
4//!
5//! # Examples
6//! see [`MetropolisHastingsSweep`], [`HeatBathSweep`], [`overrelaxation`] etc...
7
8use std::marker::PhantomData;
9
10use na::ComplexField;
11use rand_distr::Distribution;
12#[cfg(feature = "serde-serialize")]
13use serde::{Deserialize, Serialize};
14
15use super::state::{LatticeState, LatticeStateDefault};
16use crate::{
17 field::LinkMatrix,
18 lattice::{Direction, LatticeCyclic, LatticeLink, LatticeLinkCanonical},
19 Complex, Real,
20};
21
22pub mod heat_bath;
23pub mod hybrid;
24pub mod hybrid_monte_carlo;
25pub mod metropolis_hastings;
26pub mod metropolis_hastings_sweep;
27pub mod overrelaxation;
28
29pub use heat_bath::*;
30pub use hybrid::*;
31pub use hybrid_monte_carlo::*;
32pub use metropolis_hastings::*;
33pub use metropolis_hastings_sweep::*;
34pub use overrelaxation::*;
35
36/// Monte-Carlo algorithm, giving the next element in the simulation.
37/// It is also a Markov chain.
38///
39/// # Example
40/// ```
41/// # use std::error::Error;
42/// #
43/// # fn main() -> Result<(), Box<dyn Error>> {
44/// use lattice_qcd_rs::error::ImplementationError;
45/// use lattice_qcd_rs::simulation::{
46/// LatticeState, LatticeStateDefault, MetropolisHastingsSweep, MonteCarlo,
47/// };
48/// use rand::SeedableRng;
49///
50/// let rng = rand::rngs::StdRng::seed_from_u64(0); // change with your seed
51/// let mut mh = MetropolisHastingsSweep::new(1, 0.1_f64, rng)
52/// .ok_or(ImplementationError::OptionWithUnexpectedNone)?;
53/// // Realistically you want more steps than 10
54///
55/// let mut state = LatticeStateDefault::<3>::new_cold(1_f64, 6_f64, 4)?;
56/// for _ in 0..10 {
57/// state = mh.next_element(state)?;
58/// // or state.monte_carlo_step(&mut hmc)?;
59/// // operation to track the progress or the evolution
60/// }
61/// // operation at the end of the simulation
62/// # Ok(())
63/// # }
64/// ```
65pub trait MonteCarlo<State, const D: usize>
66where
67 State: LatticeState<D>,
68{
69 /// Error returned while getting the next element.
70 type Error;
71
72 /// Do one Monte Carlo simulation step.
73 ///
74 /// # Errors
75 /// Return an error if the simulation failed
76 fn next_element(&mut self, state: State) -> Result<State, Self::Error>;
77}
78
79/// Some times it is easier to just implement a potential next element, the rest is done automatically using an [`McWrapper`].
80///
81/// To get an [`MonteCarlo`] use the wrapper [`McWrapper`].
82/// # Example
83/// ```
84/// # use std::error::Error;
85/// #
86/// # fn main() -> Result<(), Box<dyn Error>> {
87/// use lattice_qcd_rs::error::ImplementationError;
88/// use lattice_qcd_rs::simulation::{
89/// LatticeState, LatticeStateDefault, McWrapper, MetropolisHastingsDiagnostic, MonteCarlo,
90/// };
91/// use rand::SeedableRng;
92///
93/// let rng = rand::rngs::StdRng::seed_from_u64(0); // change with your seed
94/// let mh = MetropolisHastingsDiagnostic::new(1, 0.1_f64)
95/// .ok_or(ImplementationError::OptionWithUnexpectedNone)?;
96/// let mut wrapper = McWrapper::new(mh, rng);
97///
98/// // Realistically you want more steps than 10
99///
100/// let mut state = LatticeStateDefault::<3>::new_cold(1_f64, 6_f64, 4)?;
101/// for _ in 0..100 {
102/// state = state.monte_carlo_step(&mut wrapper)?;
103/// println!(
104/// "probability of acceptance during last step {}, does it accepted the change ? {}",
105/// mh.prob_replace_last(),
106/// mh.has_replace_last()
107/// );
108/// // or state.monte_carlo_step(&mut wrapper)?;
109/// // operation to track the progress or the evolution
110/// }
111/// // operation at the end of the simulation
112/// let (_, rng) = wrapper.deconstruct(); // get the rng back
113/// // continue with further operation using the same rng ...
114/// # Ok(())
115/// # }
116/// ```
117pub trait MonteCarloDefault<State, const D: usize>
118where
119 State: LatticeState<D>,
120{
121 /// Error returned while getting the next element.
122 type Error;
123
124 /// Generate a radom element from the previous element (like a Markov chain).
125 ///
126 /// # Errors
127 /// Gives an error if a potential next element cannot be generated.
128 fn potential_next_element<Rng>(
129 &mut self,
130 state: &State,
131 rng: &mut Rng,
132 ) -> Result<State, Self::Error>
133 where
134 Rng: rand::Rng + ?Sized;
135
136 /// probability of the next element to replace the current one.
137 ///
138 /// by default it is Exp(-H_old) / Exp(-H_new).
139 fn probability_of_replacement(old_state: &State, new_state: &State) -> Real {
140 (old_state.hamiltonian_links() - new_state.hamiltonian_links())
141 .exp()
142 .min(1_f64)
143 .max(0_f64)
144 }
145
146 /// Get the next element in the chain either the old state or a new one replacing it.
147 ///
148 /// # Errors
149 /// Gives an error if a potential next element cannot be generated.
150 fn next_element_default<Rng>(
151 &mut self,
152 state: State,
153 rng: &mut Rng,
154 ) -> Result<State, Self::Error>
155 where
156 Rng: rand::Rng + ?Sized,
157 {
158 let potential_next = self.potential_next_element(&state, rng)?;
159 let proba = Self::probability_of_replacement(&state, &potential_next)
160 .min(1_f64)
161 .max(0_f64);
162 let d = rand::distributions::Bernoulli::new(proba).unwrap();
163 if d.sample(rng) {
164 Ok(potential_next)
165 }
166 else {
167 Ok(state)
168 }
169 }
170}
171
172/// A wrapper used to implement [`MonteCarlo`] from a [`MonteCarloDefault`]
173///
174/// # Example
175/// ```
176/// # use std::error::Error;
177/// #
178/// # fn main() -> Result<(), Box<dyn Error>> {
179/// use lattice_qcd_rs::error::ImplementationError;
180/// use lattice_qcd_rs::simulation::{
181/// LatticeState, LatticeStateDefault, McWrapper, MetropolisHastingsDiagnostic, MonteCarlo,
182/// };
183/// use rand::SeedableRng;
184///
185/// let rng = rand::rngs::StdRng::seed_from_u64(0); // change with your seed
186/// let mh = MetropolisHastingsDiagnostic::new(1, 0.1_f64)
187/// .ok_or(ImplementationError::OptionWithUnexpectedNone)?;
188/// let mut wrapper = McWrapper::new(mh, rng);
189///
190/// // Realistically you want more steps than 10
191///
192/// let mut state = LatticeStateDefault::<3>::new_cold(1_f64, 6_f64, 4)?;
193/// for _ in 0..100 {
194/// state = state.monte_carlo_step(&mut wrapper)?;
195/// println!(
196/// "probability of acceptance during last step {}, does it accepted the change ? {}",
197/// mh.prob_replace_last(),
198/// mh.has_replace_last()
199/// );
200/// // or state.monte_carlo_step(&mut wrapper)?;
201/// // operation to track the progress or the evolution
202/// }
203/// // operation at the end of the simulation
204/// let (_, rng) = wrapper.deconstruct(); // get the rng back
205/// # Ok(())
206/// # }
207/// ```
208#[derive(Clone, Debug, PartialEq, Eq, Hash)]
209#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
210pub struct McWrapper<MCD, State, Rng, const D: usize>
211where
212 MCD: MonteCarloDefault<State, D>,
213 State: LatticeState<D>,
214 Rng: rand::Rng,
215{
216 mcd: MCD,
217 rng: Rng,
218 _phantom: PhantomData<State>,
219}
220
221impl<MCD, State, Rng, const D: usize> McWrapper<MCD, State, Rng, D>
222where
223 MCD: MonteCarloDefault<State, D>,
224 State: LatticeState<D>,
225 Rng: rand::Rng,
226{
227 getter!(
228 /// Get a ref to the rng.
229 pub const,
230 rng,
231 Rng
232 );
233
234 /// Create the wrapper.
235 pub const fn new(mcd: MCD, rng: Rng) -> Self {
236 Self {
237 mcd,
238 rng,
239 _phantom: PhantomData,
240 }
241 }
242
243 /// deconstruct the structure to get back the rng if necessary
244 #[allow(clippy::missing_const_for_fn)] // false positive
245 pub fn deconstruct(self) -> (MCD, Rng) {
246 (self.mcd, self.rng)
247 }
248
249 /// Get a reference to the [`MonteCarloDefault`] inside the wrapper.
250 pub const fn mcd(&self) -> &MCD {
251 &self.mcd
252 }
253
254 /// Get a mutable reference to the rng
255 pub fn rng_mut(&mut self) -> &mut Rng {
256 &mut self.rng
257 }
258}
259
260impl<MCD, State, Rng, const D: usize> AsRef<Rng> for McWrapper<MCD, State, Rng, D>
261where
262 MCD: MonteCarloDefault<State, D>,
263 State: LatticeState<D>,
264 Rng: rand::Rng,
265{
266 fn as_ref(&self) -> &Rng {
267 self.rng()
268 }
269}
270
271impl<MCD, State, Rng, const D: usize> AsMut<Rng> for McWrapper<MCD, State, Rng, D>
272where
273 MCD: MonteCarloDefault<State, D>,
274 State: LatticeState<D>,
275 Rng: rand::Rng,
276{
277 fn as_mut(&mut self) -> &mut Rng {
278 self.rng_mut()
279 }
280}
281
282impl<T, State, Rng, const D: usize> MonteCarlo<State, D> for McWrapper<T, State, Rng, D>
283where
284 T: MonteCarloDefault<State, D>,
285 State: LatticeState<D>,
286 Rng: rand::Rng,
287{
288 type Error = T::Error;
289
290 fn next_element(&mut self, state: State) -> Result<State, Self::Error> {
291 self.mcd.next_element_default(state, &mut self.rng)
292 }
293}
294
295impl<MCD, State, Rng, const D: usize> Default for McWrapper<MCD, State, Rng, D>
296where
297 MCD: MonteCarloDefault<State, D> + Default,
298 State: LatticeState<D>,
299 Rng: rand::Rng + Default,
300{
301 fn default() -> Self {
302 Self::new(MCD::default(), Rng::default())
303 }
304}
305
306impl<MCD, State, Rng, const D: usize> std::fmt::Display for McWrapper<MCD, State, Rng, D>
307where
308 MCD: MonteCarloDefault<State, D> + std::fmt::Display,
309 State: LatticeState<D>,
310 Rng: rand::Rng + std::fmt::Display,
311{
312 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
313 write!(
314 f,
315 "Monte Carlo wrapper method {} with rng {}",
316 self.mcd(),
317 self.rng(),
318 )
319 }
320}
321
322/// Get the delta of energy by changing a link.
323#[inline]
324fn delta_s_old_new_cmp<const D: usize>(
325 link_matrix: &LinkMatrix,
326 lattice: &LatticeCyclic<D>,
327 link: &LatticeLinkCanonical<D>,
328 new_link: &na::Matrix3<Complex>,
329 beta: Real,
330 old_matrix: &na::Matrix3<Complex>,
331) -> Real {
332 let a = staple(link_matrix, lattice, link);
333 -((new_link - old_matrix) * a).trace().real() * beta / LatticeStateDefault::<D>::CA
334}
335
336// TODO move in state
337/// return the staple
338#[inline]
339fn staple<const D: usize>(
340 link_matrix: &LinkMatrix,
341 lattice: &LatticeCyclic<D>,
342 link: &LatticeLinkCanonical<D>,
343) -> na::Matrix3<Complex> {
344 let dir_j = link.dir();
345 Direction::<D>::positive_directions()
346 .iter()
347 .filter(|dir_i| *dir_i != dir_j)
348 .map(|dir_i| {
349 let el_1 = link_matrix
350 .sij(link.pos(), dir_j, dir_i, lattice)
351 .unwrap()
352 .adjoint();
353 let l_1 = LatticeLink::new(lattice.add_point_direction(*link.pos(), dir_j), -dir_i);
354 let u1 = link_matrix.matrix(&l_1, lattice).unwrap();
355 let l_2 = LatticeLink::new(lattice.add_point_direction(*link.pos(), &-dir_i), *dir_j);
356 let u2 = link_matrix.matrix(&l_2, lattice).unwrap().adjoint();
357 let l_3 = LatticeLink::new(lattice.add_point_direction(*link.pos(), &-dir_i), *dir_i);
358 let u3 = link_matrix.matrix(&l_3, lattice).unwrap();
359 el_1 + u1 * u2 * u3
360 })
361 .sum()
362}