nmr_schedule/pdf/
presets.rs1use alloc::{borrow::ToOwned, vec::Vec};
2
3use crate::{InitialState, Monotonicity, find_zero};
4
5use super::Pdf;
6
7pub fn unweighted(len: usize) -> Pdf {
19 Pdf::from_integral(move |pos| pos / len as f64, len)
20}
21
22pub fn linear(len: usize) -> Pdf {
34 let inv_len = 1. / len as f64;
35 Pdf::from_integral(move |pos| inv_len * pos * (2. - inv_len * pos), len)
36}
37
38pub fn exponential(len: usize, scale: f64) -> Pdf {
44 let c = -scale / len as f64;
45 let normalize = 1. / ((-scale).exp() - 1.);
46
47 Pdf::from_integral(move |pos| normalize * (c * pos).exp(), len)
48}
49
50#[derive(Clone, Copy, Debug)]
52pub enum QSinBias {
53 Low,
56 Med,
59 High,
62}
63
64impl core::fmt::Display for QSinBias {
65 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
66 f.write_str(match self {
67 QSinBias::Low => "Low",
68 QSinBias::Med => "Med",
69 QSinBias::High => "High",
70 })
71 }
72}
73
74pub fn qsin(len: usize, bias: QSinBias, time: f64) -> Pdf {
78 let half_time = time / 2.;
79 let len_f = len as f64;
80
81 let s = half_time / len_f;
82
83 match bias {
84 QSinBias::Low => {
85 let f = |x: f64| x + x.cos();
86
87 let normalize = (f(half_time) - f(0.)).recip();
88
89 Pdf::from_integral(move |pos| f(pos * s) * normalize, len)
90 }
91 QSinBias::Med => {
92 let bars = 16;
93 let bars_f = bars as f64;
94
95 let mut discrete = alloc::vec![0.; len];
96
97 let mut sum = 0.;
98
99 for (i, v) in discrete.iter_mut().enumerate() {
100 let mut approximation = 0.;
101
102 for bar in 0..=bars {
103 let x = i as f64 + bar as f64 / bars_f;
104 approximation += (1. - (x * s).sin()).powf(1.8)
105 * if bar == 0 || bar == bars { 0.5 } else { 1. };
106 }
107
108 *v = approximation;
109 sum += *v;
110 }
111
112 let scale = sum.recip();
113
114 discrete.iter_mut().for_each(|v| *v *= scale);
115
116 Pdf::from_discrete(discrete)
117 }
118 QSinBias::High => {
119 let f = |x: f64| 1.2225 * x + 1.7 * x.cos() - 0.25 * (2. * x).sin();
120
121 let normalize = (f(half_time) - f(0.)).recip();
122
123 Pdf::from_integral(move |pos| f(pos * s) * normalize, len)
124 }
125 }
126}
127
128#[allow(clippy::missing_panics_doc)] pub fn sin_weighted_poisson_gap(len: usize, count: usize) -> Pdf {
133 let (_, mk_probabilities) = find_zero(
138 Monotonicity::Decreasing,
139 InitialState::new(len as f64 / count as f64, 0., f64::INFINITY),
140 crate::Precision::Image {
141 amount: 0.001,
142 debug: (&|_, _| -> () { panic!("Convergence failed") }) as &_,
143 },
144 |arbitrary| {
145 let probabilities = (0..len).map(move |i| {
146 1. / (arbitrary
147 * ((i as f64 + 0.5) / (len as f64 + 1.) * core::f64::consts::FRAC_PI_2).sin()
148 + 1.)
149 });
150
151 (
152 probabilities.to_owned().sum::<f64>() - count as f64,
153 move || {
154 probabilities.collect() },
156 )
157 },
158 );
159
160 let probabilities: Vec<f64> = mk_probabilities();
161 let probabilities_ref = &probabilities;
162
163 let (_, mk_pdf) = find_zero(
166 Monotonicity::Increasing,
167 InitialState::new(count as f64, 0., f64::INFINITY),
168 crate::Precision::Image {
169 amount: 0.00001,
170 debug: (&|_, _| -> () { panic!("Convergence failed") }) as &_,
171 },
172 |inverse_count| {
173 let pdf_gen = move |i| {
176 let complement: f64 = 1. - probabilities_ref[i];
177 1. - complement.powf(inverse_count)
178 };
179
180 ((0..len).map(pdf_gen).sum::<f64>() - 1., move || {
181 Pdf::from_discrete((0..len).map(pdf_gen).collect())
182 })
183 },
184 );
185
186 mk_pdf()
187}