1use crate::analytic::{self, Domain, Event};
13use crate::continuous::{Family, Mixture, Part, Rng};
14use crate::dist::{Budget, Dist};
15use crate::error::{OpError, OpResult};
16use crate::value::Value;
17use probl_syntax::ast::BinOp;
18use std::collections::{BTreeMap, BTreeSet};
19use std::sync::Arc;
20
21pub const OWNED: u64 = 1 << 63;
23
24const MAX_EVENTS: usize = 16;
26
27pub fn of(pairs: Vec<(Value, f64)>, missing: f64, budget: &mut Budget) -> OpResult<Value> {
32 if pairs.is_empty() {
33 return Err(OpError::new("the result would be an empty distribution"));
34 }
35 let mut missing = missing;
36 let mut flat = Vec::with_capacity(pairs.len());
37 for (v, w) in pairs {
38 mix_in(v, w, &mut flat, &mut missing, budget)?;
39 }
40 let mut out = Vec::with_capacity(flat.len());
41 let mut joint = false;
42 for (v, w) in flat {
43 if !analytic::contains(&v) {
44 out.push((v, w));
45 } else if analytic::has_outcomes(&v) {
46 joint = true;
47 out.push((owned(&v), w));
48 } else {
49 for (x, p) in truths(&v, budget)? {
50 out.push((x, w * p));
51 }
52 }
53 }
54 budget.outcomes(out.len() as u128)?;
55 budget.work(out.len() as u64)?;
56 let d = Dist::from_pairs(out, missing.min(1.0));
57 Ok(if joint {
58 Value::Joint(Arc::new(d))
59 } else {
60 d.into_value()
61 })
62}
63
64fn mix_in(v: Value, w: f64, flat: &mut Vec<(Value, f64)>, missing: &mut f64, budget: &mut Budget) -> OpResult<()> {
67 let v = match v {
68 Value::Counts(_) => crate::ops::listed(&v, budget)?.into_owned(),
69 v => v,
70 };
71 match v {
72 Value::Dist(d) | Value::Joint(d) => {
73 *missing += w * d.missing;
74 for (x, p) in &d.outcomes {
75 match x {
76 Value::Joint(inner) => {
77 *missing += w * p * inner.missing;
78 flat.extend(inner.outcomes.iter().map(|(y, q)| (y.clone(), w * p * q)));
79 }
80 x => flat.push((x.clone(), w * p)),
81 }
82 }
83 }
84 other => flat.push((other, w)),
85 }
86 Ok(())
87}
88
89fn owned(v: &Value) -> Value {
91 let mut ids = BTreeSet::new();
92 analytic::collect_ids(v, &mut ids);
93 let map: BTreeMap<u64, u64> = ids.into_iter().zip(OWNED + 1..).collect();
94 analytic::renamed(v, &map)
95}
96
97fn truths(v: &Value, budget: &mut Budget) -> OpResult<Vec<(Value, f64)>> {
100 let mut events: Vec<Arc<Event>> = Vec::new();
101 collect_events(v, &mut events);
102 if events.len() > MAX_EVENTS {
103 return Err(OpError::limit(format!(
104 "a `simulate` value with more than {MAX_EVENTS} conditions on continuous draws is over the limit"
105 )));
106 }
107 budget.work(1u64 << events.len())?;
108 let mut out = Vec::new();
109 for mask in 0..1u32 << events.len() {
110 let mut all: Option<Event> = None;
111 for (i, e) in events.iter().enumerate() {
112 let side = if mask & (1 << i) != 0 {
113 (**e).clone()
114 } else {
115 e.complement()?
116 };
117 all = Some(match all {
118 None => side,
119 Some(all) => all.combine(&side, BinOp::And)?,
120 });
121 }
122 let p = all.map_or(1.0, |e| e.probability());
123 if p > 0.0 {
124 out.push((with_truths(v, &events, mask), p));
125 }
126 }
127 Ok(out)
128}
129
130fn collect_events(v: &Value, events: &mut Vec<Arc<Event>>) {
131 match v {
132 Value::Event(e) => {
133 let key = e.key();
134 if !events.iter().any(|x| x.key() == key) {
135 events.push(e.clone());
136 }
137 }
138 Value::List(xs) => xs.iter().for_each(|x| collect_events(x, events)),
139 Value::Map(xs) => xs.values().for_each(|x| collect_events(x, events)),
140 Value::Record(r) => r.fields.iter().for_each(|(_, x)| collect_events(x, events)),
141 Value::Dist(d) => d.outcomes.iter().for_each(|(x, _)| collect_events(x, events)),
142 Value::Closure(c) => c.captured.iter().for_each(|x| collect_events(x, events)),
143 _ => {}
144 }
145}
146
147fn with_truths(v: &Value, events: &[Arc<Event>], mask: u32) -> Value {
149 if !analytic::contains(v) {
150 return v.clone();
151 }
152 let child = |x: &Value| with_truths(x, events, mask);
153 match v {
154 Value::Event(e) => {
155 let key = e.key();
156 let i = events.iter().position(|x| x.key() == key).expect("a collected event");
157 Value::Bool(mask & (1 << i) != 0)
158 }
159 Value::List(xs) => Value::list(xs.iter().map(child).collect()),
160 Value::Map(xs) => Value::map(xs.iter().map(|(k, x)| (k.clone(), child(x))).collect()),
161 Value::Record(r) => crate::ops::make_record(
162 r.ty.clone(),
163 r.fields.iter().map(|(k, x)| (k.clone(), child(x))).collect(),
164 ),
165 Value::Dist(d) => {
166 Dist::from_pairs(d.outcomes.iter().map(|(x, p)| (child(x), *p)).collect(), d.missing).into_value()
167 }
168 Value::Closure(c) => Value::Closure(Arc::new(crate::value::Closure {
169 func: c.func,
170 captured: c.captured.iter().map(child).collect(),
171 named: c.named,
172 })),
173 v => v.clone(),
174 }
175}
176
177pub fn fresh(v: &Value, next: &mut u64) -> Value {
180 let mut ids = BTreeSet::new();
181 analytic::collect_ids(v, &mut ids);
182 if ids.is_empty() {
183 return v.clone();
184 }
185 let map: BTreeMap<u64, u64> = ids
186 .into_iter()
187 .map(|id| {
188 *next += 1;
189 (id, *next)
190 })
191 .collect();
192 analytic::renamed(v, &map)
193}
194
195#[cold]
198#[inline(never)]
199pub fn sample(d: &Dist, rng: &mut Rng) -> Value {
200 let i = rng.choose(d.outcomes.iter().map(|(_, p)| *p)).unwrap_or(0);
201 let v = &d.outcomes[i].0;
202 let mut latents: BTreeMap<u64, Drawn> = BTreeMap::new();
203 gather(v, &mut latents);
204 let at: BTreeMap<u64, (f64, f64)> = latents
205 .into_iter()
206 .map(|(id, (family, domain, weight))| {
207 let c = within(&domain, weight.as_deref(), rng);
208 (id, (c, family.quantile(c)))
209 })
210 .collect();
211 concrete(v, &at)
212}
213
214type Drawn = (Family, Domain, Option<Arc<crate::likelihood::Likelihood>>);
216
217fn gather(v: &Value, latents: &mut BTreeMap<u64, Drawn>) {
220 let mut add = |a: &analytic::Analytic| match &a.form {
221 Some(f) => {
222 for t in &f.terms {
223 latents.entry(t.id).or_insert((t.family, Domain::full(), None));
224 }
225 }
226 None => {
227 let entry = latents
228 .entry(a.id)
229 .or_insert((a.family, a.domain.clone(), a.weight.clone()));
230 entry.1 = entry.1.intersect(&a.domain);
231 }
232 };
233 let mut pending = vec![v];
234 while let Some(v) = pending.pop() {
235 match v {
236 Value::Analytic(a) => add(a),
237 Value::Event(e) => e.draws.iter().for_each(&mut add),
238 Value::List(xs) => pending.extend(xs.iter()),
239 Value::Map(xs) => pending.extend(xs.values()),
240 Value::Record(r) => pending.extend(r.fields.iter().map(|(_, x)| x)),
241 Value::Dist(d) => pending.extend(d.outcomes.iter().map(|(x, _)| x)),
242 Value::Closure(c) => pending.extend(c.captured.iter()),
243 _ => {}
244 }
245 }
246}
247
248fn within(domain: &Domain, weight: Option<&crate::likelihood::Likelihood>, rng: &mut Rng) -> f64 {
251 if let Some(l) = weight {
252 return l.inverse(domain, rng.open());
253 }
254 let mut left = rng.open() * domain.mass();
255 for &(a, b) in &domain.0 {
256 if left <= b - a {
257 return (a + left).clamp(a, b);
258 }
259 left -= b - a;
260 }
261 domain.0.last().map_or(0.5, |&(_, b)| b)
262}
263
264fn axis(a: &analytic::Analytic, at: &BTreeMap<u64, (f64, f64)>) -> (f64, f64) {
266 match &a.form {
267 Some(f) => {
268 let t = f.constant + f.terms.iter().map(|t| t.coef * at[&t.id].1).sum::<f64>();
269 (a.family.cdf(t), t)
270 }
271 None => at[&a.id],
272 }
273}
274
275fn concrete(v: &Value, at: &BTreeMap<u64, (f64, f64)>) -> Value {
277 if !analytic::contains(v) {
278 return v.clone();
279 }
280 let child = |x: &Value| concrete(x, at);
281 match v {
282 Value::Analytic(a) => {
283 let (c, t) = axis(a, at);
284 a.at(c, t)
285 }
286 Value::Event(e) => {
287 let coordinates: Vec<f64> = e.draws.iter().map(|d| axis(d, at).0).collect();
288 let inside = |d: &Domain, c: f64| d.0.iter().any(|&(a, b)| a <= c && c <= b);
289 Value::Bool(
290 e.boxes
291 .iter()
292 .any(|part| part.iter().zip(&coordinates).all(|(d, &c)| inside(d, c))),
293 )
294 }
295 Value::List(xs) => Value::list(xs.iter().map(child).collect()),
296 Value::Map(xs) => Value::map(xs.iter().map(|(k, x)| (k.clone(), child(x))).collect()),
297 Value::Record(r) => crate::ops::make_record(
298 r.ty.clone(),
299 r.fields.iter().map(|(k, x)| (k.clone(), child(x))).collect(),
300 ),
301 Value::Dist(d) => {
302 Dist::from_pairs(d.outcomes.iter().map(|(x, p)| (child(x), *p)).collect(), d.missing).into_value()
303 }
304 Value::Closure(c) => Value::Closure(Arc::new(crate::value::Closure {
305 func: c.func,
306 captured: c.captured.iter().map(child).collect(),
307 named: c.named,
308 })),
309 v => v.clone(),
310 }
311}
312
313pub fn mixture(d: &Dist) -> Option<Mixture> {
316 let parts = d
317 .outcomes
318 .iter()
319 .map(|(v, p)| {
320 let part = match v {
321 Value::Analytic(a) => Part::Analytic((**a).clone()),
322 Value::Continuous(f) => Part::Continuous(**f),
323 v => Part::Point(v.as_f64()?),
324 };
325 Some((part, *p))
326 })
327 .collect::<Option<_>>()?;
328 Some(Mixture { parts })
329}
330
331#[cold]
335#[inline(never)]
336pub fn binary(op: BinOp, a: &Value, b: &Value, budget: &mut Budget) -> OpResult<Value> {
337 let (d, other, flipped) = match (a, b) {
338 (Value::Joint(d), other) => (d, other, false),
339 (other, Value::Joint(d)) => (d, other, true),
340 _ => unreachable!("an operand is joint"),
341 };
342 if matches!(other, Value::Joint(_)) || analytic::contains(other) {
343 return Err(OpError::unsupported(format!(
344 "`{}` between a `simulate` distribution of continuous outcomes and another distribution of them, or an outcome of a draw, isn't supported yet",
345 op.symbol()
346 ))
347 .help("draw from it first, with `~`"));
348 }
349 let mut pairs = Vec::with_capacity(d.outcomes.len());
350 for (v, p) in &d.outcomes {
351 let r = if flipped {
352 crate::ops::binary(op, other, v, budget)?
353 } else {
354 crate::ops::binary(op, v, other, budget)?
355 };
356 pairs.push((r, *p));
357 }
358 of(pairs, d.missing, budget)
359}