1use fugue::{addr, plate, sample, Bernoulli, Categorical, Model, ModelExt, Normal, Uniform};
17
18use crate::genome::bit_string::BitString;
19use crate::genome::bounds::MultiBounds;
20use crate::genome::permutation::Permutation;
21use crate::genome::real_vector::RealVector;
22use crate::genome::trace_genome::TraceGenome;
23use crate::genome::traits::{BinaryGenome, PermutationGenome, RealValuedGenome};
24
25pub trait GenomePrior: Clone + Send + Sync + 'static {
35 type Genome: TraceGenome;
37
38 fn model(&self) -> Model<Self::Genome>;
40
41 fn trace_of(&self, genome: &Self::Genome) -> fugue::Trace {
50 genome.to_trace()
51 }
52}
53
54#[derive(Clone, Debug)]
61pub struct UniformBoxPrior {
62 bounds: MultiBounds,
63}
64
65impl UniformBoxPrior {
66 pub fn new(bounds: MultiBounds) -> Self {
68 Self { bounds }
69 }
70
71 pub fn bounds(&self) -> &MultiBounds {
73 &self.bounds
74 }
75}
76
77impl GenomePrior for UniformBoxPrior {
78 type Genome = RealVector;
79
80 fn model(&self) -> Model<RealVector> {
81 let bounds = self.bounds.clone();
82 plate!(i in 0..bounds.dimension().max(1) => {
83 let (lo, hi) = match bounds.get(i) {
84 Some(b) if b.max > b.min => (b.min, b.max),
85 Some(b) => (b.min - 1e-9, b.min + 1e-9),
86 None => (-1.0, 1.0),
87 };
88 sample(addr!("gene", i), Uniform::new(lo, hi).expect("valid uniform prior bounds"))
89 })
90 .map(|genes| RealVector::from_genes(genes).expect("plate produced genes"))
91 }
92}
93
94#[derive(Clone, Debug)]
96pub struct GaussianPrior {
97 mean: f64,
98 std: f64,
99 dim: usize,
100}
101
102impl GaussianPrior {
103 pub fn new(mean: f64, std: f64, dim: usize) -> Self {
106 assert!(std > 0.0, "Gaussian prior std must be > 0");
107 Self { mean, std, dim }
108 }
109}
110
111impl GenomePrior for GaussianPrior {
112 type Genome = RealVector;
113
114 fn model(&self) -> Model<RealVector> {
115 let (mean, std, dim) = (self.mean, self.std, self.dim.max(1));
116 plate!(i in 0..dim => {
117 sample(addr!("gene", i), Normal::new(mean, std).expect("valid Gaussian prior"))
118 })
119 .map(|genes| RealVector::from_genes(genes).expect("plate produced genes"))
120 }
121}
122
123#[derive(Clone, Debug)]
125pub struct BitStringPrior {
126 p_one: f64,
127 len: usize,
128}
129
130impl BitStringPrior {
131 pub fn new(p_one: f64, len: usize) -> Self {
134 assert!(
135 p_one > 0.0 && p_one < 1.0,
136 "BitStringPrior p_one must be in (0, 1)"
137 );
138 Self { p_one, len }
139 }
140
141 pub fn uniform(len: usize) -> Self {
143 Self::new(0.5, len)
144 }
145}
146
147impl GenomePrior for BitStringPrior {
148 type Genome = BitString;
149
150 fn model(&self) -> Model<BitString> {
151 let (p, len) = (self.p_one, self.len.max(1));
152 plate!(i in 0..len => {
153 sample(addr!("bit", i), Bernoulli::new(p).expect("valid Bernoulli prior"))
154 })
155 .map(|bits| BitString::from_bits(bits).expect("plate produced bits"))
156 }
157}
158
159#[derive(Clone, Debug)]
175pub struct PermutationPrior {
176 n: usize,
177}
178
179impl PermutationPrior {
180 pub fn new(n: usize) -> Self {
182 assert!(n > 0, "PermutationPrior needs n > 0");
183 Self { n }
184 }
185}
186
187impl GenomePrior for PermutationPrior {
188 type Genome = Permutation;
189
190 fn model(&self) -> Model<Permutation> {
191 let n = self.n;
192 fn rank_model(n: usize, i: usize, ranks: Vec<usize>) -> Model<Vec<usize>> {
193 if i == n {
194 return fugue::pure(ranks);
195 }
196 let k = n - i;
197 let probs = vec![1.0 / k as f64; k];
198 sample(
199 addr!("perm", i),
200 Categorical::new(probs).expect("valid categorical prior"),
201 )
202 .bind(move |r| {
203 let mut ranks = ranks;
204 ranks.push(r);
205 rank_model(n, i + 1, ranks)
206 })
207 }
208 rank_model(n, 0, Vec::with_capacity(n)).map(move |ranks| {
209 let mut available: Vec<usize> = (0..n).collect();
210 let perm: Vec<usize> = ranks.into_iter().map(|r| available.remove(r)).collect();
211 Permutation::from_permutation(perm).expect("Lehmer decode produced a permutation")
212 })
213 }
214}
215
216#[cfg(test)]
217mod tests {
218 use super::*;
219 use fugue::runtime::handler::run;
220 use fugue::runtime::interpreters::{PriorHandler, ScoreGivenTrace};
221 use fugue::Trace;
222 use rand::rngs::StdRng;
223 use rand::SeedableRng;
224
225 #[test]
226 fn test_gaussian_prior_draws_match_moments() {
227 let prior = GaussianPrior::new(0.0, 2.0, 1);
228 let mut rng = StdRng::seed_from_u64(99);
229 let xs: Vec<f64> = (0..5000)
230 .map(|_| {
231 let (g, _) = run(
232 PriorHandler {
233 rng: &mut rng,
234 trace: Trace::default(),
235 },
236 prior.model(),
237 );
238 g.genes()[0]
239 })
240 .collect();
241 let mean = xs.iter().sum::<f64>() / xs.len() as f64;
242 let var = xs.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / xs.len() as f64;
243 assert!(mean.abs() < 0.2, "prior mean {}", mean);
244 assert!((var.sqrt() - 2.0).abs() < 0.2, "prior std {}", var.sqrt());
245 }
246
247 #[test]
251 fn test_prior_model_log_prior_matches_analytic() {
252 let prior = GaussianPrior::new(1.0, 2.0, 3);
253 let g = RealVector::new(vec![0.5, 1.5, -2.0]);
254 let (_, scored) = run(
255 ScoreGivenTrace {
256 base: g.to_trace(),
257 trace: Trace::default(),
258 },
259 prior.model(),
260 );
261 let normal = Normal::new(1.0, 2.0).unwrap();
262 let analytic: f64 = g
263 .genes()
264 .iter()
265 .map(|x| fugue::Distribution::log_prob(&normal, x))
266 .sum();
267 assert!((scored.log_prior - analytic).abs() < 1e-12);
268 }
269
270 #[test]
271 fn test_uniform_prior_out_of_box_scores_neg_inf() {
272 let prior = UniformBoxPrior::new(MultiBounds::symmetric(1.0, 2));
273 let g = RealVector::new(vec![0.5, 5.0]); let (_, scored) = run(
275 ScoreGivenTrace {
276 base: g.to_trace(),
277 trace: Trace::default(),
278 },
279 prior.model(),
280 );
281 assert_eq!(scored.log_prior, f64::NEG_INFINITY);
282 }
283
284 #[test]
285 fn test_permutation_prior_generates_valid_permutations() {
286 let prior = PermutationPrior::new(6);
287 let mut rng = StdRng::seed_from_u64(7);
288 for _ in 0..50 {
289 let (p, trace) = run(
290 PriorHandler {
291 rng: &mut rng,
292 trace: Trace::default(),
293 },
294 prior.model(),
295 );
296 assert!(p.is_valid_permutation());
297 let expected = -(720.0f64).ln(); assert!((trace.log_prior - expected).abs() < 1e-9);
300 let canonical = p.to_trace();
302 for (addr, choice) in &canonical.choices {
303 assert_eq!(trace.choices[addr].value, choice.value);
304 }
305 }
306 }
307
308 #[test]
309 fn test_bitstring_prior_scores_canonical_trace() {
310 let prior = BitStringPrior::uniform(4);
311 let g = BitString::from_bits(vec![true, false, true, true]).unwrap();
312 let (decoded, scored) = run(
313 ScoreGivenTrace {
314 base: g.to_trace(),
315 trace: Trace::default(),
316 },
317 prior.model(),
318 );
319 assert_eq!(decoded.bits(), g.bits());
320 assert!((scored.log_prior - 4.0 * (0.5f64).ln()).abs() < 1e-12);
321 }
322}