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 })),
172 v => v.clone(),
173 }
174}
175
176pub fn fresh(v: &Value, next: &mut u64) -> Value {
179 let mut ids = BTreeSet::new();
180 analytic::collect_ids(v, &mut ids);
181 if ids.is_empty() {
182 return v.clone();
183 }
184 let map: BTreeMap<u64, u64> = ids
185 .into_iter()
186 .map(|id| {
187 *next += 1;
188 (id, *next)
189 })
190 .collect();
191 analytic::renamed(v, &map)
192}
193
194#[cold]
197#[inline(never)]
198pub fn sample(d: &Dist, rng: &mut Rng) -> Value {
199 let i = rng.choose(d.outcomes.iter().map(|(_, p)| *p)).unwrap_or(0);
200 let v = &d.outcomes[i].0;
201 let mut latents: BTreeMap<u64, Drawn> = BTreeMap::new();
202 gather(v, &mut latents);
203 let at: BTreeMap<u64, (f64, f64)> = latents
204 .into_iter()
205 .map(|(id, (family, domain, weight))| {
206 let c = within(&domain, weight.as_deref(), rng);
207 (id, (c, family.quantile(c)))
208 })
209 .collect();
210 concrete(v, &at)
211}
212
213type Drawn = (Family, Domain, Option<Arc<crate::likelihood::Likelihood>>);
215
216fn gather(v: &Value, latents: &mut BTreeMap<u64, Drawn>) {
219 let mut add = |a: &analytic::Analytic| match &a.form {
220 Some(f) => {
221 for t in &f.terms {
222 latents.entry(t.id).or_insert((t.family, Domain::full(), None));
223 }
224 }
225 None => {
226 let entry = latents
227 .entry(a.id)
228 .or_insert((a.family, a.domain.clone(), a.weight.clone()));
229 entry.1 = entry.1.intersect(&a.domain);
230 }
231 };
232 let mut pending = vec![v];
233 while let Some(v) = pending.pop() {
234 match v {
235 Value::Analytic(a) => add(a),
236 Value::Event(e) => e.draws.iter().for_each(&mut add),
237 Value::List(xs) => pending.extend(xs.iter()),
238 Value::Map(xs) => pending.extend(xs.values()),
239 Value::Record(r) => pending.extend(r.fields.iter().map(|(_, x)| x)),
240 Value::Dist(d) => pending.extend(d.outcomes.iter().map(|(x, _)| x)),
241 Value::Closure(c) => pending.extend(c.captured.iter()),
242 _ => {}
243 }
244 }
245}
246
247fn within(domain: &Domain, weight: Option<&crate::likelihood::Likelihood>, rng: &mut Rng) -> f64 {
250 if let Some(l) = weight {
251 return l.inverse(domain, rng.open());
252 }
253 let mut left = rng.open() * domain.mass();
254 for &(a, b) in &domain.0 {
255 if left <= b - a {
256 return (a + left).clamp(a, b);
257 }
258 left -= b - a;
259 }
260 domain.0.last().map_or(0.5, |&(_, b)| b)
261}
262
263fn axis(a: &analytic::Analytic, at: &BTreeMap<u64, (f64, f64)>) -> (f64, f64) {
265 match &a.form {
266 Some(f) => {
267 let t = f.constant + f.terms.iter().map(|t| t.coef * at[&t.id].1).sum::<f64>();
268 (a.family.cdf(t), t)
269 }
270 None => at[&a.id],
271 }
272}
273
274fn concrete(v: &Value, at: &BTreeMap<u64, (f64, f64)>) -> Value {
276 if !analytic::contains(v) {
277 return v.clone();
278 }
279 let child = |x: &Value| concrete(x, at);
280 match v {
281 Value::Analytic(a) => {
282 let (c, t) = axis(a, at);
283 a.at(c, t)
284 }
285 Value::Event(e) => {
286 let coordinates: Vec<f64> = e.draws.iter().map(|d| axis(d, at).0).collect();
287 let inside = |d: &Domain, c: f64| d.0.iter().any(|&(a, b)| a <= c && c <= b);
288 Value::Bool(
289 e.boxes
290 .iter()
291 .any(|part| part.iter().zip(&coordinates).all(|(d, &c)| inside(d, c))),
292 )
293 }
294 Value::List(xs) => Value::list(xs.iter().map(child).collect()),
295 Value::Map(xs) => Value::map(xs.iter().map(|(k, x)| (k.clone(), child(x))).collect()),
296 Value::Record(r) => crate::ops::make_record(
297 r.ty.clone(),
298 r.fields.iter().map(|(k, x)| (k.clone(), child(x))).collect(),
299 ),
300 Value::Dist(d) => {
301 Dist::from_pairs(d.outcomes.iter().map(|(x, p)| (child(x), *p)).collect(), d.missing).into_value()
302 }
303 Value::Closure(c) => Value::Closure(Arc::new(crate::value::Closure {
304 func: c.func,
305 captured: c.captured.iter().map(child).collect(),
306 })),
307 v => v.clone(),
308 }
309}
310
311pub fn mixture(d: &Dist) -> Option<Mixture> {
314 let parts = d
315 .outcomes
316 .iter()
317 .map(|(v, p)| {
318 let part = match v {
319 Value::Analytic(a) => Part::Analytic((**a).clone()),
320 Value::Continuous(f) => Part::Continuous(**f),
321 v => Part::Point(v.as_f64()?),
322 };
323 Some((part, *p))
324 })
325 .collect::<Option<_>>()?;
326 Some(Mixture { parts })
327}
328
329#[cold]
333#[inline(never)]
334pub fn binary(op: BinOp, a: &Value, b: &Value, budget: &mut Budget) -> OpResult<Value> {
335 let (d, other, flipped) = match (a, b) {
336 (Value::Joint(d), other) => (d, other, false),
337 (other, Value::Joint(d)) => (d, other, true),
338 _ => unreachable!("an operand is joint"),
339 };
340 if matches!(other, Value::Joint(_)) || analytic::contains(other) {
341 return Err(OpError::unsupported(format!(
342 "`{}` between a `simulate` distribution of continuous outcomes and another distribution of them, or an outcome of a draw, isn't supported yet",
343 op.symbol()
344 ))
345 .help("draw from it first, with `~`"));
346 }
347 let mut pairs = Vec::with_capacity(d.outcomes.len());
348 for (v, p) in &d.outcomes {
349 let r = if flipped {
350 crate::ops::binary(op, other, v, budget)?
351 } else {
352 crate::ops::binary(op, v, other, budget)?
353 };
354 pairs.push((r, *p));
355 }
356 of(pairs, d.missing, budget)
357}