1use{
2 crate::{*, traits::*},
3 rand::Rng,
4 num_traits::{Bounded, ops::wrapping::*, identities::*},
5 std::{marker::PhantomData, io::Write, num::*}
6};
7
8#[cfg(feature = "serde_support")]
9use serde::{Serialize, Deserialize};
10
11#[derive(Debug, Clone)]
21#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
22pub struct WangLandau1T<Hist, Rng, Ensemble, S, Res, Energy>{
23 pub(crate) ensemble: Ensemble,
24 pub(crate) rng: Rng,
25 pub(crate) marker_res: PhantomData<Res>,
26 pub(crate) steps: Vec<S>,
27 mode: WangLandauMode,
28 pub(crate) log_density: Vec<f64>,
29 pub(crate) log_f: f64,
30 pub(crate) log_f_threshold: f64,
31 pub(crate) step_size: usize,
32 step_count: usize,
33 accepted_steps_total: usize,
34 recected_steps_total: usize,
35 accepted_steps_current: usize,
36 recected_steps_current: usize,
37 pub(crate) old_bin: usize,
38 pub(crate) hist: Hist,
39 pub(crate) old_energy: Option<Energy>,
40 check_refine_every: usize,
41}
42
43impl<Hist, R, E, S, Res, Energy> GlueAble<Hist> for WangLandau1T<Hist, R, E, S, Res, Energy>
44 where Hist: Clone
45{
46 fn push_glue_entry_ignoring(
47 &self,
48 job: &mut GlueJob<Hist>,
49 ignore_idx: &[usize]
50 ) {
51 if !ignore_idx.contains(&0)
52 {
53 let sim_progress = SimProgress::LogF(self.log_f);
54 let rejected = self.total_steps_rejected() as u64;
55 let accepted = self.total_steps_accepted() as u64;
56
57 let stats = IntervalSimStats{
58 sim_progress,
59 interval_sim_type: SimulationType::WangLandau1T,
60 rejected_steps: rejected,
61 accepted_steps: accepted,
62 replica_exchanges: None,
63 proposed_replica_exchanges: None,
64 merged_over_walkers: NonZeroUsize::new(1).unwrap()
65 };
66
67 let glue_entry = GlueEntry{
68 hist: self.hist.clone(),
69 prob: self.log_density.clone(),
70 log_base: LogBase::BaseE,
71 interval_stats: stats
72 };
73 job.collection.push(glue_entry);
74 }
75 }
76}
77
78impl<Hist, Rng, Ensemble, S, Res, Energy>WangLandau1T<Hist, Rng, Ensemble, S, Res, Energy>
79{
80 pub fn into_inner(self) -> (Ensemble, Hist, Rng)
82 {
83 (self.ensemble, self.hist, self.rng)
84 }
85}
86
87
88impl<Hist, R, E, S, Res, Energy> WangLandau
89 for WangLandau1T<Hist, R, E, S, Res, Energy>
90{
91 #[inline(always)]
92 fn log_f(&self) -> f64
93 {
94 self.log_f
95 }
96
97 #[inline(always)]
98 fn log_f_threshold(&self) -> f64
99 {
100 self.log_f_threshold
101 }
102
103 fn set_log_f_threshold(&mut self, log_f_threshold: f64) -> Result<f64, WangLandauErrors>
104 {
105 if !log_f_threshold.is_finite() || log_f_threshold.is_sign_negative() {
106 return Err(WangLandauErrors::InvalidLogFThreshold);
107 }
108 let old_threshold = self.log_f_threshold;
109 self.log_f_threshold = log_f_threshold;
110 Ok(old_threshold)
111 }
112
113 #[inline(always)]
114 fn log_density(&self) -> &Vec<f64>
115 {
116 &self.log_density
117 }
118
119 fn write_log<W: Write>(&self, mut writer: W) -> Result<(), std::io::Error> {
120 writeln!(writer,
121 "#Acceptance prob_total: {}\n#Acceptance prob current: {}\n#total_steps: {}\n#log_f: {:e}\n#Current_mode {:?}",
122 self.fraction_accepted_total(),
123 self.fraction_accepted_current(),
124 self.step_counter(),
125 self.log_f(),
126 self.mode
127 )?;
128 writeln!(
129 writer,
130 "#total_steps_accepted: {}\n#total_steps_rejected: {}\n#current_accepted_steps: {}\n#current_rejected_steps: {}",
131 self.accepted_steps_total,
132 self.recected_steps_total,
133 self.accepted_steps_current,
134 self.recected_steps_current
135 )
136 }
137
138 #[inline(always)]
139 fn mode(&self) -> WangLandauMode
140 {
141 self.mode
142 }
143
144 #[inline(always)]
145 fn step_counter(&self) -> usize
146 {
147 self.step_count
148 }
149
150 #[inline(always)]
151 fn total_steps_rejected(&self) -> usize {
152 self.recected_steps_total
153 }
154
155 #[inline(always)]
156 fn total_steps_accepted(&self) -> usize {
157 self.accepted_steps_total
158 }
159}
160
161impl<Hist, R, E, S, Res, Energy>
162 WangLandau1T<Hist, R, E, S, Res, Energy>
163where
164 Hist: Histogram + HistogramVal<Energy>
165{
166 pub fn is_initialized(&self) -> bool
170 {
171 match &self.old_energy{
172 None => false,
173 Some(e) => {
174 self.hist.is_inside(e)
175 }
176 }
177 }
178}
179
180#[derive(Clone, Copy, Debug)]
182pub enum SetInitialError{
183 DimensionError,
186 NonFiniteEncountered,
188 InvalidLogF
190}
191
192
193impl <Hist, R, E, S, Res, Energy> WangLandauEnsemble<E>
194 for WangLandau1T<Hist, R, E, S, Res, Energy>
195{
196 #[inline(always)]
197 fn ensemble(&self) -> &E {
198 &self.ensemble
199 }
200
201 #[inline(always)]
202 unsafe fn ensemble_mut(&mut self) -> &mut E {
203 &mut self.ensemble
204 }
205}
206
207impl <Hist, R, E, S, Res, Energy> WangLandauEnergy<Energy>
208 for WangLandau1T<Hist, R, E, S, Res, Energy>
209{
210 #[inline(always)]
211 fn energy(&self) -> Option<&Energy> {
212 self.old_energy.as_ref()
213 }
214}
215
216impl <Hist, R, E, S, Res, Energy> WangLandauHist<Hist>
217 for WangLandau1T<Hist, R, E, S, Res, Energy>
218{
219 #[inline(always)]
220 fn hist(&self) -> &Hist {
221 &self.hist
222 }
223}
224
225impl<Hist, R, E, S, Res, Energy>
226 WangLandau1T<Hist, R, E, S, Res, Energy>
227{
228 fn fraction_accepted_total(&self) -> f64
231 {
232 let sum = self.accepted_steps_total + self.recected_steps_total;
233 self.accepted_steps_total as f64 / sum as f64
234 }
235
236 fn fraction_accepted_current(&self) -> f64
240 {
241 let total = self.accepted_steps_current + self.recected_steps_current;
242 if total == 0 {
243 f64::NAN
244 } else {
245 self.accepted_steps_current as f64 / total as f64
246 }
247 }
248
249 pub fn set_initial_probability_guess(mut self, new_guess: Vec<f64>, new_log_f: f64) -> Result<Self, SetInitialError>
259 where Hist: Histogram
260 {
261 if 0.0 >= new_log_f || new_log_f > 10.0 {
262 Err(SetInitialError::InvalidLogF)
263 }
264 else if new_guess.len() != self.log_density.len()
265 {
266 Err(SetInitialError::DimensionError)
267 } else if new_guess.iter().any(|val| !val.is_finite())
268 {
269 Err(SetInitialError::NonFiniteEncountered)
270 } else {
271 self.log_density = new_guess;
272 self.log_f = new_log_f;
273 self.step_count = 0;
274 self.accepted_steps_current = 0;
275 self.accepted_steps_total = 0;
276 self.recected_steps_current = 0;
277 self.recected_steps_total = 0;
278 self.mode = WangLandauMode::RefineOriginal;
279 self.hist.reset();
280 self.old_energy = None;
281 self.old_bin = usize::MAX;
282 Ok(self)
283 }
284 }
285}
286
287
288impl<Hist, R, E, S, Res, Energy>
289 WangLandau1T<Hist, R, E, S, Res, Energy>
290where
291 R: Rng,
292 E: MarkovChain<S, Res>,
293 Energy: Clone,
294 Hist: Histogram + HistogramVal<Energy>
295{
296 pub fn new(
313 log_f_threshold: f64,
314 ensemble: E,
315 rng: R,
316 step_size: usize,
317 histogram: Hist,
318 check_refine_every: usize
319 )-> Result<Self, WangLandauErrors>
320 {
321 if !log_f_threshold.is_finite() || log_f_threshold.is_sign_negative()
322 {
323 return Err(WangLandauErrors::InvalidLogFThreshold);
324 }
325 else if check_refine_every == 0 {
326 return Err(WangLandauErrors::CheckRefineEvery0)
327 }
328 let log_density = vec![0.0; histogram.bin_count()];
329 let steps = Vec::with_capacity(step_size);
330
331 Ok(
332 Self{
333 ensemble,
334 step_count: 0,
335 step_size,
336 hist: histogram,
337 rng,
338 marker_res: PhantomData::<Res>,
339 log_f: 1.0,
340 log_density,
341 log_f_threshold,
342 mode: WangLandauMode::RefineOriginal,
343 recected_steps_current: 0,
344 recected_steps_total: 0,
345 accepted_steps_current: 0,
346 accepted_steps_total: 0,
347 old_bin: usize::MAX,
348 old_energy: None,
349 check_refine_every,
350 steps,
351 }
352 )
353 }
354
355 fn init<F>(
356 &mut self,
357 energy_fn: F,
358 step_limit: Option<u64>
359 ) -> Result<(), WangLandauErrors>
360 where F: Fn(&mut E) -> Option<Energy>
361 {
362 self.old_energy = energy_fn(&mut self.ensemble);
363 if self.old_energy.is_some(){
364 return Ok(());
365 }
366
367 match step_limit {
368 None => {
369 loop {
370 self.ensemble.m_steps_quiet(self.step_size);
371 self.old_energy = energy_fn(&mut self.ensemble);
372
373 if self.old_energy.is_some(){
374 self.count_accepted();
375 return Ok(());
376 }
377 self.count_rejected();
378 }
379 },
380 Some(limit) => {
381 for _ in 0..limit {
382 self.ensemble.m_steps_quiet(self.step_size);
383 self.old_energy = energy_fn(&mut self.ensemble);
384
385 if self.old_energy.is_some(){
386 self.count_accepted();
387 return Ok(());
388 }
389 self.count_rejected();
390 }
391 Err(WangLandauErrors::InitFailed)
392 }
393 }
394 }
395
396 fn greedy_helper<F, H, J>(
397 &mut self,
398 old_distance: &mut J,
399 energy_fn: F,
400 distance_fn: H,
401 ) where F: Fn(&mut E) -> Option<Energy> + Copy,
402 H: Fn(&Hist, &Energy) -> J,
403 J: PartialOrd
404 {
405 self.ensemble
406 .m_steps(self.step_size, &mut self.steps);
407
408
409 if let Some(energy) = energy_fn(&mut self.ensemble) {
410 let distance = distance_fn(&self.hist, &energy);
411 if distance <= *old_distance {
412 self.old_energy = Some(energy);
413 *old_distance = distance;
414 self.count_accepted();
415
416 return;
417 }
418 }
419
420
421 self.count_rejected();
422 self.ensemble
423 .undo_steps_quiet(&self.steps);
424
425 }
426
427 pub fn init_greedy_heuristic<F>(
442 &mut self,
443 energy_fn: F,
444 step_limit: Option<u64>,
445 ) -> Result<(), WangLandauErrors>
446 where F: Fn(&mut E) -> Option<Energy>,
447 {
448 self.init(&energy_fn, step_limit)?;
449 let mut old_distance = self.hist
450 .distance(self.old_energy_ref());
451 let mut step_count = 0;
452 while old_distance != 0.0 {
453 self.greedy_helper(
454 &mut old_distance,
455 &energy_fn,
456 |hist, energy| {
457 hist.distance(energy)
458 }
459 );
460 if let Some(limit) = step_limit {
461 if limit == step_count{
462 return Err(WangLandauErrors::InitFailed);
463 }
464 step_count += 1;
465 }
466 }
467 self.end_init();
468 Ok(())
469 }
470
471 pub fn init_interval_heuristik<F>(
487 &mut self,
488 overlap: NonZeroUsize,
489 energy_fn: F,
490 step_limit: Option<u64>,
491 ) -> Result<(), WangLandauErrors>
492 where F: Fn(&mut E) -> Option<Energy>,
493 Hist: HistogramIntervalDistance<Energy>
494 {
495 self.init(&energy_fn, step_limit)?;
496 let mut old_dist = self.hist
497 .interval_distance_overlap(
498 self.old_energy_ref(),
499 overlap
500 );
501
502 let dist = |h: &Hist, val: &Energy| h.interval_distance_overlap(val, overlap);
503 let mut step_count = 0;
504 while old_dist != 0 {
505 self.greedy_helper(
506 &mut old_dist,
507 &energy_fn,
508 dist
509 );
510 if let Some(limit) = step_limit {
511 if limit == step_count{
512 return Err(WangLandauErrors::InitFailed);
513 }
514 step_count += 1;
515 }
516 }
517 self.end_init();
518 Ok(())
519 }
520
521 pub fn init_mixed_heuristik<F, U>
539 (
540 &mut self,
541 overlap: NonZeroUsize,
542 mid: U,
543 energy_fn: F,
544 step_limit: Option<u64>
545 ) -> Result<(), WangLandauErrors>
546 where F: Fn(&mut E) -> Option<Energy>,
547 Hist: HistogramIntervalDistance<Energy>,
548 U: One + Bounded + WrappingAdd + Eq + PartialOrd
549 {
550 self.init(&energy_fn, step_limit)?;
551 if self.hist
552 .is_inside(
553 self.old_energy_ref()
554 )
555 {
556 self.end_init();
557 return Ok(());
558 }
559
560 let mut old_dist = f64::INFINITY;
561 let mut old_dist_interval = usize::MAX;
562 let mut counter: U = U::min_value();
563 let min_val = U::min_value();
564 let one = U::one();
565 let dist_interval = |h: &Hist, val: &Energy| h.interval_distance_overlap(val, overlap);
566 let mut step_count = 0;
567 loop {
568 if counter == min_val {
569 let current_energy = self.old_energy_ref();
570 old_dist = self.hist.distance(current_energy);
571 }else if counter == mid {
572 let current_energy = self.old_energy_ref();
573 old_dist_interval = dist_interval(&self.hist, current_energy);
574 }
575 if counter < mid {
576 self.greedy_helper(
577 &mut old_dist,
578 &energy_fn,
579 |hist, val| {
580 hist.distance(val)
581 }
582 );
583 if old_dist == 0.0 {
584 break;
585 }
586 } else {
587 self.greedy_helper(
588 &mut old_dist_interval,
589 &energy_fn,
590 dist_interval,
591 );
592 if old_dist_interval == 0 {
593 break;
594 }
595 }
596 counter = counter.wrapping_add(&one);
597 if let Some(limit) = step_limit {
598 if limit == step_count{
599 return Err(WangLandauErrors::InitFailed);
600 }
601 step_count += 1;
602 }
603 }
604 self.end_init();
605 Ok(())
606 }
607
608 fn end_init(&mut self)
609 {
610 self.old_bin = self.hist
611 .get_bin_index(
612 self.old_energy_ref()
613 ).expect("Error in heuristic - old bin invalid");
614 }
615
616 fn old_energy_clone(&self) -> Energy {
617 self.old_energy_ref()
618 .clone()
619 }
620
621 fn old_energy_ref(&self) -> &Energy {
622 self.old_energy
623 .as_ref()
624 .unwrap()
625 }
626
627
628 fn count_accepted(&mut self){
629 self.ensemble.steps_accepted(&self.steps);
630 self.accepted_steps_current += 1;
631 self.accepted_steps_total += 1;
632 }
633
634 fn count_rejected(&mut self){
635 self.ensemble.steps_rejected(&self.steps);
636 self.recected_steps_current += 1;
637 self.recected_steps_total += 1;
638 }
639
640
641 fn check_refine(&mut self)
642 {
643 match self.mode{
644 WangLandauMode::Refine1T => {
645 self.log_f = self.log_f_1_t();
646 },
647 WangLandauMode::RefineOriginal => {
648 if self.step_count % self.check_refine_every == 0
649 && !self.hist.any_bin_zero()
650 {
651 self.recected_steps_current = 0;
652 self.accepted_steps_current = 0;
653 let ref_1_t = self.log_f_1_t();
654 self.log_f *= 0.5;
655 if self.log_f < ref_1_t {
656 self.log_f = ref_1_t;
657 self.mode = WangLandauMode::Refine1T;
658 }
659 self.hist.reset();
660 }
661 }
662 }
663 }
664
665
666 fn wl_step_helper(&mut self, energy: Option<Energy>)
667 {
668 let current_energy = match energy
669 {
670 Some(energy) => energy,
671 None => {
672 self.count_rejected();
673 self.hist.increment_index(self.old_bin).unwrap();
674 self.log_density[self.old_bin] += self.log_f;
675 self.ensemble.undo_steps_quiet(&self.steps);
676 return;
677 }
678 };
679
680 match self.hist.get_bin_index(¤t_energy)
681 {
682 Ok(current_bin) => {
683 let accept_prob = self.metropolis_acception_prob( current_bin);
684
685 if self.rng.random::<f64>() > accept_prob {
686 self.count_rejected();
688 self.ensemble.undo_steps_quiet(&self.steps);
689 } else {
690 self.count_accepted();
692
693 self.old_energy = Some(current_energy);
694 self.old_bin = current_bin;
695 }
696 },
697 _ => {
698 self.count_rejected();
700 self.ensemble.undo_steps_quiet(&self.steps);
701 }
702 };
703
704 self.hist.increment_index(self.old_bin).unwrap();
705 self.log_density[self.old_bin] += self.log_f;
706 }
707
708 pub fn wang_landau_step<F>(
720 &mut self,
721 energy_fn: F,
722 )where F: Fn(&E) -> Option<Energy>
723 {
724 unsafe {
725 self.wang_landau_step_unsafe(|e| energy_fn(e))
726 }
727 }
728
729 pub unsafe fn wang_landau_step_unsafe<F>(
745 &mut self,
746 mut energy_fn: F,
747 )where F: FnMut(&mut E) -> Option<Energy>
748 {
749 debug_assert!(
750 self.old_energy.is_some(),
751 "Error - self.old_energy invalid - Did you forget to call one of the `self.init*` members for initialization?"
752 );
753
754 self.step_count += 1;
755
756
757 self.ensemble.m_steps(self.step_size, &mut self.steps);
758
759 self.check_refine();
760 let current_energy = energy_fn(&mut self.ensemble);
761
762 self.wl_step_helper(current_energy);
763 }
764
765 pub fn wang_landau_step_acc<F>(
774 &mut self,
775 energy_fn: F,
776 )
777 where F: FnMut(&E, &S, &mut Energy)
778 {
779 debug_assert!(
780 self.old_energy.is_some(),
781 "Error - self.old_energy invalid - Did you forget to call one of the `self.init*` members for initialization?"
782 );
783
784 self.step_count += 1;
785
786 let mut new_energy = self.old_energy_clone();
787
788 self.ensemble
789 .m_steps_acc(
790 self.step_size,
791 &mut self.steps,
792 &mut new_energy,
793 energy_fn
794 );
795
796 self.check_refine();
797
798 self.wl_step_helper(Some(new_energy));
799
800 }
801
802 pub fn wang_landau_convergence<F>(
806 &mut self,
807 energy_fn: F,
808 )where F: Fn(&E) -> Option<Energy>,
809 {
810 while !self.is_finished() {
811 self.wang_landau_step(&energy_fn);
812 }
813 }
814
815 pub fn wang_landau_convergence_acc<F>(
819 &mut self,
820 mut energy_fn: F,
821 )where F: FnMut(&E, &S, &mut Energy)
822 {
823 while !self.is_finished() {
824 self.wang_landau_step_acc(&mut energy_fn);
825 }
826 }
827
828 pub unsafe fn wang_landau_convergence_unsafe<F>(
837 &mut self,
838 mut energy_fn: F,
839 )where F: FnMut(&mut E) -> Option<Energy>,
840 {
841 while !self.is_finished() {
842 self.wang_landau_step_unsafe(&mut energy_fn);
843 }
844 }
845
846 pub fn wang_landau_while<F, W>(
851 &mut self,
852 energy_fn: F,
853 mut condition: W
854 ) where F: Fn(&E) -> Option<Energy>,
855 W: FnMut(&Self) -> bool,
856 {
857 while !self.is_finished() && condition(self) {
858 self.wang_landau_step(&energy_fn);
859 }
860 }
861
862 pub fn wang_landau_while_acc<F, W>(
867 &mut self,
868 mut energy_fn: F,
869 mut condition: W
870 ) where F: FnMut(&E, &S, &mut Energy),
871 W: FnMut(&Self) -> bool,
872 {
873 while !self.is_finished() && condition(self) {
874 self.wang_landau_step_acc(&mut energy_fn);
875 }
876 }
877
878 pub unsafe fn wang_landau_while_unsafe<F, W>(
888 &mut self,
889 mut energy_fn: F,
890 mut condition: W
891 ) where F: FnMut(&mut E) -> Option<Energy>,
892 W: FnMut(&Self) -> bool,
893 {
894 while !self.is_finished() && condition(self) {
895 self.wang_landau_step_unsafe(&mut energy_fn);
896 }
897 }
898
899
900 #[inline(always)]
902 fn metropolis_acception_prob(&self, new_bin: usize) -> f64
903 {
904
905 (self.log_density[self.old_bin] - self.log_density[new_bin])
906 .exp()
907
908 }
909}
910
911
912#[cfg(test)]
913mod tests {
914 use super::*;
915 use rand_pcg::Pcg64Mcg;
916 use rand::SeedableRng;
917 use crate::examples::coin_flips::*;
918 #[test]
919 #[cfg_attr(miri,ignore)]
920 fn wl_simulations_equal() {
921 let mut rng = Pcg64Mcg::seed_from_u64(2239790);
922 let ensemble = CoinFlipSequence::new(100, Pcg64Mcg::from_rng(&mut rng));
923 let histogram = HistogramFast::new_inclusive(0, 100).unwrap();
924 let mut wl= WangLandau1T::new(
925 0.0075,
926 ensemble,
927 rng,
928 1,
929 histogram,
930 30
931 ).unwrap();
932
933 wl.init_mixed_heuristik(
934 NonZeroUsize::new(3).unwrap(),
935 6400i16,
936 |e| {
937 Some(e.head_count())
938 },
939 None
940 ).unwrap();
941
942 let mut wl_backup = wl.clone();
943 let start_wl= std::time::Instant::now();
944 wl.wang_landau_convergence(
945 |e| Some(e.head_count())
946 );
947 let dur_1 = start_wl.elapsed();
948 let start_wl_acc = std::time::Instant::now();
949 wl_backup.wang_landau_convergence_acc(
950 CoinFlipSequence::update_head_count
951 );
952 let dur_2 = start_wl_acc.elapsed();
953 println!("WL: {}, WL_ACC: {}, difference: {}", dur_1.as_nanos(), dur_2.as_nanos(), dur_1.as_nanos() - dur_2.as_nanos());
954
955 for (&log_value, &log_value_acc) in wl.log_density().iter().zip(wl_backup.log_density().iter()){
957 assert_eq!(log_value, log_value_acc);
958 }
959 }
960}