Skip to main content

probl_engine/
interp.rs

1//! The world-set interpreter.
2//!
3//! Every statement maps a set of worlds to a set of worlds. All the worlds in
4//! a set are at the same point of the program, so control flow is shared and
5//! only the data differs. Splitting happens in statements; expressions are
6//! evaluated in one world at a time. See docs/semantics.md for the rules this
7//! implements.
8
9use crate::analytic::{self, Analytic};
10use crate::builtins;
11use crate::chain::{Chain, Solution};
12use crate::conjugate::{self, Seen};
13use crate::continuous::{Family, Rng};
14use crate::dist::{Budget, Counts, Dist};
15use crate::error::{ErrorKind, Fault, OpError, OpResult, Result, RuntimeError};
16use crate::failure::Failures;
17use crate::ops::{self, Truth};
18use crate::report::Sink;
19use crate::value::{Closure, Delayed, Value, fmt_prob};
20use crate::weight::Weight;
21use crate::world::Returned;
22use crate::world::{Flow, World, clear, clear_dead, live_slots, merge, merge_values, state_hash, total_weight};
23use probl_sema::builtins::Lifting;
24use probl_sema::conjugate::{Conjugacy, Likelihood, Update};
25use probl_sema::effects::EvidenceOrder;
26use probl_sema::ir::*;
27use probl_sema::{Builtin, Liveness};
28use probl_syntax::Span;
29use probl_syntax::ast::BinOp;
30use rustc_hash::{FxHashMap, FxHashSet};
31use std::collections::BTreeMap;
32use std::sync::Arc;
33use std::sync::atomic::{AtomicBool, Ordering};
34
35#[derive(Clone, Debug, Default)]
36pub struct Stats {
37    /// Largest number of worlds any statement ran on.
38    pub peak_worlds: usize,
39    /// Statement executions, counting each world separately.
40    pub world_steps: u64,
41    pub calls: u64,
42    pub memo_hits: u64,
43    /// Loops solved as Markov chains, and their states (section 10).
44    pub solved_loops: u64,
45    pub chain_states: u64,
46    /// Calls that came back to themselves, solved by iteration, and the
47    /// rounds that took (section 6).
48    pub solved_calls: u64,
49    pub call_rounds: u64,
50    /// When sampling: what happened to each variable whose draws may be
51    /// delayed (`Conjugacy::variables`).
52    pub updates: Vec<Updates>,
53    /// When the program was answered with decision diagrams instead of
54    /// worlds (`Options::symbolic`): their size.
55    pub diagrams: Option<crate::symbolic::Diagrams>,
56}
57
58/// How often a variable's draws were delayed and updated exactly
59/// (docs/semantics.md, section 14), counted over all runs.
60#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
61pub struct Updates {
62    /// Draws delayed.
63    pub delayed: u64,
64    /// Observations that updated it exactly.
65    pub exact: u64,
66    /// Times it was drawn when first needed, from its updated distribution.
67    pub drawn: u64,
68}
69
70impl Stats {
71    /// Add up another batch's statistics.
72    pub fn absorb(&mut self, other: &Stats) {
73        self.peak_worlds = self.peak_worlds.max(other.peak_worlds);
74        self.world_steps += other.world_steps;
75        self.calls += other.calls;
76        self.memo_hits += other.memo_hits;
77        self.solved_loops += other.solved_loops;
78        self.chain_states += other.chain_states;
79        self.solved_calls += other.solved_calls;
80        self.call_rounds += other.call_rounds;
81        if self.updates.len() < other.updates.len() {
82            self.updates.resize(other.updates.len(), Updates::default());
83        }
84        for (mine, theirs) in self.updates.iter_mut().zip(&other.updates) {
85            mine.delayed += theirs.delayed;
86            mine.exact += theirs.exact;
87            mine.drawn += theirs.drawn;
88        }
89    }
90}
91
92/// How the engine runs, with the host's limits already applied.
93#[derive(Clone, Debug)]
94pub struct Config {
95    pub today: Option<i32>,
96    pub epsilon: f64,
97    pub merging: bool,
98    pub memoizing: bool,
99    pub max_worlds: usize,
100    pub max_iterations: u64,
101    pub max_call_depth: usize,
102    pub max_cached_calls: usize,
103    pub max_output: usize,
104    pub budget: Budget,
105    pub cancel: Option<Arc<AtomicBool>>,
106    /// Sample with this seed, instead of enumerating (docs/semantics.md,
107    /// section 14).
108    pub sample_seed: Option<u64>,
109    /// When sampling, delay the draws of conjugate priors and update them
110    /// exactly (section 14).
111    pub conjugate: bool,
112    /// When enumerating, solve loops that cycle as Markov chains (section 10).
113    pub solving: bool,
114    /// The most states a loop's chain may have to be solved.
115    pub max_chain_states: usize,
116    /// A fault ends only the world it happens in (docs/semantics.md,
117    /// section 11), instead of the run.
118    pub partial: bool,
119    /// Compute what has no formula by numerical integration, to this
120    /// relative tolerance (section 13).
121    pub integrate: Option<f64>,
122}
123
124/// The most steps eliminating one loop's chain may take before the loop is
125/// unrolled instead.
126const MAX_ELIMINATION: u64 = 200_000_000;
127
128/// Runs are executed this many at a time, and each batch has a random
129/// stream of its own. The size is fixed: changing it changes the output for
130/// a given seed.
131pub const BATCH: u64 = 1000;
132
133/// What a batch of sampled runs produced, to be combined with the other
134/// batches in order.
135#[derive(Debug)]
136pub struct Batch {
137    pub sinks: Vec<Sink>,
138    pub totals: SampleTotals,
139    pub observed: bool,
140    pub densities: bool,
141    pub unresolved: Weight,
142    pub last_ruling_out: Option<Span>,
143    pub stats: Stats,
144    pub failures: Failures,
145}
146
147/// Lines printed by `print(…)`, with where.
148pub type Printed = Vec<(Span, String)>;
149
150/// The final weights of all the runs, when sampling.
151#[derive(Clone, Copy, Debug)]
152pub struct SampleTotals {
153    pub weight: Weight,
154    /// Sum of the squared weights, for the effective sample size.
155    pub squares: Weight,
156}
157
158/// The distribution of a function's return value, unnormalized.
159#[derive(Clone, Debug)]
160pub struct CallResult {
161    pub outcomes: Vec<Returned>,
162    /// Weight the call left unresolved.
163    pub unresolved: Weight,
164    /// Weight its observations ruled out, when enumerating.
165    pub lost: Weight,
166    /// Whether the call ran `observe` (outside any `simulate`).
167    pub observed: bool,
168    /// The part of `unresolved` waiting on calls that came back to
169    /// themselves and are still being solved (section 6), for each by its
170    /// depth in the stack. A result waiting on one isn't final.
171    pub pending: Vec<(usize, Weight)>,
172    /// In partial mode: its worlds that failed.
173    pub failures: Failures,
174}
175
176impl CallResult {
177    /// The result of a call that came back to itself, before the first
178    /// round: nothing resolved, all of it waiting on that call.
179    fn waiting(depth: usize) -> CallResult {
180        CallResult {
181            outcomes: Vec::new(),
182            unresolved: Weight::ONE,
183            lost: Weight::ZERO,
184            observed: false,
185            pending: vec![(depth, Weight::ONE)],
186            failures: Failures::default(),
187        }
188    }
189
190    /// Take out the weight waiting on the call at `depth`.
191    fn take_pending(&mut self, depth: usize) -> Weight {
192        match self.pending.iter().position(|&(d, _)| d == depth) {
193            Some(i) => self.pending.remove(i).1,
194            None => Weight::ZERO,
195        }
196    }
197}
198
199/// A call's result so far, while the call it waits on is being solved
200/// (section 6).
201struct Approx {
202    result: Arc<CallResult>,
203    /// The depth of that call, and its frame and round when this was worked
204    /// out. It stands for the call as long as that frame runs, and can be
205    /// reused as it is until that frame's next round.
206    head: usize,
207    frame: u64,
208    round: u64,
209}
210
211/// Add `weight` times what's waiting in `from` to `into`.
212fn add_pending(into: &mut Vec<(usize, Weight)>, from: &[(usize, Weight)], weight: Weight) {
213    for &(depth, w) in from {
214        match into.iter_mut().find(|(d, _)| *d == depth) {
215            Some((_, total)) => *total += weight * w,
216            None => into.push((depth, weight * w)),
217        }
218    }
219}
220
221type CallKey = (FnId, Vec<Value>);
222
223pub struct Engine<'p> {
224    prog: &'p Program,
225    live: &'p Liveness,
226    /// Which draws may be delayed, and what must draw them.
227    conj: &'p Conjugacy,
228    config: Config,
229    budget: Budget,
230    memo: FxHashMap<CallKey, Arc<CallResult>>,
231    /// `simulate` results that are distributions whose outcomes own their
232    /// draws: pure values of the block and its captures, which a call's
233    /// result, holding analytic outcomes, can't be cached as.
234    simulated: FxHashMap<CallKey, Value>,
235    /// The calls running, with their depth in the stack.
236    active: FxHashMap<CallKey, usize>,
237    /// Results so far of calls that wait on a call being solved by
238    /// iteration (section 6).
239    approx: FxHashMap<CallKey, Approx>,
240    /// For each depth in the stack: the frame running there, and its round,
241    /// each a number never used before.
242    frames_at: Vec<u64>,
243    rounds_at: Vec<u64>,
244    last_number: u64,
245    /// Weight waiting on calls being solved, in the current call.
246    pending: Vec<(usize, Weight)>,
247    depth: usize,
248    /// Weight left unaccounted for (loops cut short, truncated tails).
249    pub unresolved: Weight,
250    /// When enumerating: weight ruled out by observations, which solving a
251    /// loop counts as a way out of it.
252    lost: Weight,
253    /// For each statement: whether it's an unbounded loop that may be
254    /// solved as a Markov chain (section 10).
255    solvable: Vec<bool>,
256    /// For each loop: the variables it only adds to, which the chain carries
257    /// as rewards rather than in its states.
258    rewards: FxHashMap<StmtId, Vec<SlotId>>,
259    /// Loops whose chains were found too large to solve: they're unrolled
260    /// from then on.
261    too_large: FxHashSet<StmtId>,
262    /// Whether the current inference scope has run `observe`.
263    pub observed: bool,
264    /// Whether it has observed a value with a density, which makes the
265    /// evidence a density too (docs/semantics.md, section 14).
266    pub densities: bool,
267    /// The last observation that ruled out a world, for impossible evidence.
268    pub last_ruling_out: Option<Span>,
269    /// When enumerating, the first observation of a density.
270    pub density_at: Option<Span>,
271    /// How many densities the weights of the worlds that reported or
272    /// finished include. It must be as many for each.
273    pub reported_densities: Option<u32>,
274    /// In the innermost `simulate` running: its first observation of a
275    /// density, and how many its returning worlds' weights include, which
276    /// must be as many for each, since its result is normalized.
277    local_density_at: Option<Span>,
278    returned_densities: Option<u32>,
279    /// The tolerance, if a result is computed by numerical integration.
280    pub integrated: Option<f64>,
281    pub sinks: Vec<Sink>,
282    dice: FxHashMap<(u32, u32), Value>,
283    pools: FxHashMap<(u32, Value), Value>,
284    record_names: Vec<Arc<str>>,
285    enum_values: Vec<Vec<Value>>,
286    print: &'p mut (dyn FnMut(&str) + Send),
287    /// The values of the program's inputs (`read`).
288    inputs: &'p [Value],
289    /// Bytes printed, counting the batches printed before this one.
290    printed: usize,
291    /// When sampling a batch: what it printed, kept to be printed in batch
292    /// order.
293    lines: Option<Printed>,
294    pub stats: Stats,
295    /// When sampling: the random numbers. `None` when enumerating, including
296    /// inside `simulate` while sampling.
297    sampler: Option<Rng>,
298    /// Nesting depth of local `simulate` inference (in either outer mode).
299    nested: usize,
300    next_latent: u64,
301    /// Dynamic effect boundary for collection callbacks. Effects inside an
302    /// explicit `simulate` are local to that computation instead.
303    callback: Option<&'static str>,
304    /// In partial mode: the worlds that failed, in the current call.
305    pub failures: Failures,
306    /// The `try`s running, innermost last, each with the depth of the call
307    /// it's in, and its catches.
308    handlers: Vec<(usize, &'p [Catch])>,
309    /// Worlds of the statement running that faulted inside a `try` of this
310    /// call that catches the fault, on their way to it.
311    faulted: Vec<(World, RuntimeError)>,
312    /// Where evidence can still be applied, to tell whether a failed
313    /// world's weight can be added to the finished worlds'.
314    evidence: EvidenceOrder,
315}
316
317/// Where a world was when it failed.
318#[derive(Clone, Copy)]
319struct At {
320    f: FnId,
321    stmt: StmtId,
322    weight: Weight,
323    run: u32,
324    densities: u32,
325}
326
327impl At {
328    fn new(f: FnId, stmt: &Stmt, w: &World) -> At {
329        At {
330            f,
331            stmt: stmt.id,
332            weight: w.weight,
333            run: w.run,
334            densities: w.densities,
335        }
336    }
337}
338
339/// The value of `$e` in one world. If it fails with a fault that a `catch`
340/// or partial mode takes, the world leaves the statement: `$undo` runs, and
341/// the statement goes on with the next world (see `Engine::fail`). A catch
342/// gets the world as its statement began: `world w` when `$e` leaves `w` as
343/// it was, or `saved s` with a copy `s` taken before `$e` changed it.
344macro_rules! each {
345    ($self:ident, $at:expr, world $w:expr, $e:expr $(, $undo:block)?) => {
346        match $e {
347            Ok(v) => v,
348            Err(e) => {
349                let saved = $self.snapshot(&$w);
350                $self.fail(e, $at, saved)?;
351                $($undo)?
352                continue;
353            }
354        }
355    };
356    ($self:ident, $at:expr, saved $saved:ident, $e:expr $(, $undo:block)?) => {
357        match $e {
358            Ok(v) => v,
359            Err(e) => {
360                $self.fail(e, $at, $saved.take())?;
361                $($undo)?
362                continue;
363            }
364        }
365    };
366}
367
368impl<'p> Engine<'p> {
369    pub fn new(
370        prog: &'p Program,
371        live: &'p Liveness,
372        conj: &'p Conjugacy,
373        config: Config,
374        inputs: &'p [Value],
375        print: &'p mut (dyn FnMut(&str) + Send),
376    ) -> Engine<'p> {
377        let sampler = config.sample_seed.map(Rng::new);
378        Engine {
379            prog,
380            live,
381            conj,
382            budget: config.budget.clone(),
383            config,
384            memo: FxHashMap::default(),
385            simulated: FxHashMap::default(),
386            active: FxHashMap::default(),
387            approx: FxHashMap::default(),
388            frames_at: Vec::new(),
389            rounds_at: Vec::new(),
390            last_number: 0,
391            pending: Vec::new(),
392            depth: 0,
393            unresolved: Weight::ZERO,
394            lost: Weight::ZERO,
395            solvable: probl_sema::effects::solvable_loops(prog),
396            rewards: probl_sema::rewards::loop_rewards(prog),
397            too_large: FxHashSet::default(),
398            observed: false,
399            densities: false,
400            last_ruling_out: None,
401            density_at: None,
402            reported_densities: None,
403            local_density_at: None,
404            returned_densities: None,
405            integrated: None,
406            sinks: vec![Sink::default(); prog.reports.len()],
407            dice: FxHashMap::default(),
408            pools: FxHashMap::default(),
409            record_names: prog.records.iter().map(|r| Arc::from(r.name.as_str())).collect(),
410            enum_values: prog
411                .enums
412                .iter()
413                .enumerate()
414                .map(|(t, e)| {
415                    e.variants
416                        .iter()
417                        .enumerate()
418                        .map(|(v, name)| ops::enum_value(t as u32, v as u32, name))
419                        .collect()
420                })
421                .collect(),
422            print,
423            inputs,
424            printed: 0,
425            lines: None,
426            stats: Stats::default(),
427            sampler,
428            nested: 0,
429            next_latent: 0,
430            callback: None,
431            failures: Failures::default(),
432            evidence: probl_sema::effects::evidence_order(prog),
433            handlers: Vec::new(),
434            faulted: Vec::new(),
435        }
436    }
437
438    /// Run the top level. Returns the total weight of the worlds that finish.
439    pub fn run_main(&mut self) -> Result<Weight> {
440        let prog = self.prog;
441        let main = prog.main();
442        let world = World {
443            slots: vec![Value::Dead; main.n_slots()],
444            constraints: Default::default(),
445            inherited: Default::default(),
446            weight: Weight::ONE,
447            run: 0,
448            densities: 0,
449        };
450        let flow = self.exec_block(MAIN, &main.body, vec![world])?;
451        uncaught(&flow)?;
452        for w in &flow.next {
453            self.same_units(w, main.span)?;
454        }
455        Ok(total_weight(&flow.next))
456    }
457
458    /// Weights with different numbers of densities aren't in the same
459    /// units, so they can't be added up or compared.
460    fn same_units(&mut self, w: &World, span: Span) -> Result<()> {
461        match self.reported_densities {
462            Some(n) if n != w.densities => Err(OpError::unsupported(
463                "worlds that observe different numbers of continuous values aren't supported when enumerating",
464            )
465            .help("a density is per unit of what it observes, so worlds with more of them can't be weighed against the others; observe the same values in every world")
466            .at(self.density_at.unwrap_or(span))),
467            _ => {
468                self.reported_densities = Some(w.densities);
469                Ok(())
470            }
471        }
472    }
473
474    /// Sample the runs `first..first + n` with the random numbers `rng`
475    /// (docs/semantics.md, section 14), after earlier batches printed
476    /// `printed` bytes. A batch starts afresh: its reports and counters are
477    /// its own, so batches can run on any thread; only caches carry over.
478    /// What it prints comes back with its result, even when it fails.
479    pub fn run_batch(&mut self, rng: Rng, first: u64, n: u64, printed: usize) -> (Result<Batch>, Printed) {
480        self.sampler = Some(rng);
481        self.sinks = vec![Sink::default(); self.prog.reports.len()];
482        self.observed = false;
483        self.densities = false;
484        self.unresolved = Weight::ZERO;
485        self.last_ruling_out = None;
486        self.stats = Stats::default();
487        self.failures = Failures::default();
488        self.printed = printed;
489        self.lines = Some(Vec::new());
490        self.budget = self.config.budget.clone();
491        let result = self.sample_runs(first, n);
492        self.budget.give_back();
493        (result, self.lines.take().unwrap_or_default())
494    }
495
496    fn sample_runs(&mut self, first: u64, n: u64) -> Result<Batch> {
497        let main = self.prog.main();
498        let worlds = (first..first + n)
499            .map(|run| World {
500                slots: vec![Value::Dead; main.n_slots()],
501                constraints: Default::default(),
502                inherited: Default::default(),
503                weight: Weight::ONE,
504                run: run as u32,
505                densities: 0,
506            })
507            .collect();
508        let flow = self.exec_block(MAIN, &main.body, worlds)?;
509        uncaught(&flow)?;
510        let mut totals = SampleTotals {
511            weight: Weight::ZERO,
512            squares: Weight::ZERO,
513        };
514        for w in &flow.next {
515            totals.weight += w.weight;
516            totals.squares += w.weight * w.weight;
517        }
518        for sink in &mut self.sinks {
519            sink.end_batch();
520        }
521        Ok(Batch {
522            sinks: std::mem::take(&mut self.sinks),
523            totals,
524            observed: self.observed,
525            densities: self.densities,
526            unresolved: self.unresolved,
527            last_ruling_out: self.last_ruling_out,
528            stats: std::mem::take(&mut self.stats),
529            failures: std::mem::take(&mut self.failures),
530        })
531    }
532
533    /// When sampling: one draw from a distribution. A finite distribution
534    /// gives one of its outcomes, chosen with its probability (missing mass
535    /// is ignored); a continuous one, a number.
536    fn sample(&mut self, v: &Value) -> Value {
537        let rng = self.sampler.as_mut().expect("only called when sampling");
538        match v {
539            Value::Dist(d) if !d.outcomes.is_empty() => {
540                let i = rng.choose(d.outcomes.iter().map(|(_, p)| *p)).unwrap_or(0);
541                match &d.outcomes[i].0 {
542                    Value::Continuous(f) => Value::Float(f.sample(rng)),
543                    Value::Joint(j) => crate::joint::sample(j, rng),
544                    x => x.clone(),
545                }
546            }
547            Value::Continuous(f) => Value::Float(f.sample(rng)),
548            Value::Counts(c) => Value::Int(c.sample(rng).into()),
549            Value::Joint(j) => crate::joint::sample(j, rng),
550            other => other.clone(),
551        }
552    }
553
554    /// When sampling, a value to report: continuous parts are drawn; a
555    /// finite distribution is kept whole, so each outcome counts with its
556    /// probability.
557    #[inline(never)]
558    fn sample_continuous(&mut self, v: Value) -> Value {
559        match &v {
560            Value::Continuous(_) | Value::Joint(_) => self.sample(&v),
561            Value::Dist(d)
562                if d.outcomes
563                    .iter()
564                    .any(|(x, _)| matches!(x, Value::Continuous(_) | Value::Joint(_))) =>
565            {
566                let pairs = d.outcomes.iter().map(|(x, p)| (self.sample(x), *p)).collect();
567                Dist::from_pairs(pairs, d.missing).into_value()
568            }
569            _ => v,
570        }
571    }
572
573    /// When sampling, `binomial(…)`, `poisson(…)` or `geometric(…)` with plain
574    /// arguments is drawn from, or observed, without listing its outcomes:
575    /// the distribution is the same, and it's much faster.
576    fn direct_counts(&mut self, f: FnId, e: &'p Expr, w: &World) -> Result<Option<Counts>> {
577        let ExprKind::Builtin {
578            func: b @ (Builtin::Binomial | Builtin::Poisson | Builtin::Geometric),
579            args,
580            ..
581        } = &e.kind
582        else {
583            return Ok(None);
584        };
585        if self.sampler.is_none() {
586            return Ok(None);
587        }
588        let mut values = Vec::with_capacity(args.len());
589        for a in args {
590            values.push(self.eval(f, a, w)?);
591        }
592        let counts = builtins::counts(*b, &values, &mut self.budget).map_err(|err| err.at(e.span))?;
593        Ok(counts.filter(Counts::direct))
594    }
595
596    /// Whether draws of conjugate priors are delayed now: when sampling,
597    /// but not inside `simulate` (section 14).
598    fn delaying(&self) -> bool {
599        self.config.conjugate && self.sampler.is_some()
600    }
601
602    fn updates(&mut self, variable: u32) -> &mut Updates {
603        let i = variable as usize;
604        if self.stats.updates.len() <= i {
605            let n = self.conj.variables.len().max(i + 1);
606            self.stats.updates.resize(n, Updates::default());
607        }
608        &mut self.stats.updates[i]
609    }
610
611    /// Draw a delayed variable from its distribution, updated by the
612    /// observations so far, now that its value is needed.
613    fn draw_delayed(&mut self, w: &mut World, slot: SlotId) {
614        if let Value::Delayed(d) = &w.slots[slot as usize] {
615            let d = **d;
616            let x = d
617                .family
618                .sample(self.sampler.as_mut().expect("delayed only when sampling"));
619            w.slots[slot as usize] = Value::Float(x);
620            self.updates(d.variable).drawn += 1;
621        }
622    }
623
624    /// `observe` of a delayed variable through a conjugate form: the log of
625    /// the observation's probability (or density), with the variable
626    /// updated to its distribution after it. `None` when it isn't an exact
627    /// update: the variable isn't delayed, or doesn't have the right family,
628    /// or the rest of the observation isn't plain. Then the variable is
629    /// drawn, and the observation is made as usual. `from` is where
630    /// problems with the observed distribution's parameters are reported,
631    /// as when it's evaluated.
632    fn observe_exactly(&mut self, f: FnId, u: &Update<'p>, w: &mut World, from: Span) -> Result<Option<f64>> {
633        let Value::Delayed(d) = &w.slots[u.slot as usize] else {
634            return Ok(None);
635        };
636        let d = **d;
637        let pair = matches!(
638            (d.family, u.likelihood),
639            (
640                Family::Beta { .. },
641                Likelihood::Binomial { .. } | Likelihood::Bernoulli { .. }
642            ) | (Family::Gamma { .. }, Likelihood::Poisson { .. })
643                | (Family::Normal { .. }, Likelihood::Normal { .. })
644        );
645        let seen = if pair { self.seen(f, u, w, from)? } else { None };
646        let Some(seen) = seen else {
647            self.draw_delayed(w, u.slot);
648            return Ok(None);
649        };
650        let (ln, posterior) = conjugate::update(&d.family, seen).expect("a conjugate pair");
651        w.slots[u.slot as usize] = Value::Delayed(Arc::new(Delayed {
652            family: posterior,
653            variable: d.variable,
654        }));
655        self.updates(d.variable).exact += 1;
656        self.densities |= matches!(seen, Seen::Normal { .. });
657        Ok(Some(ln))
658    }
659
660    /// Assigning a discrete outcome, like `floor(x)`, splits its world: one
661    /// for each of its values, with the draw restricted to where it's that
662    /// value, so that the variable is a plain number. The split worlds go
663    /// to `failed`, to be dropped, and theirs to the end. (A place with a
664    /// path keeps the outcome.)
665    #[cold]
666    #[inline(never)]
667    fn split_discrete(
668        &mut self,
669        f: FnId,
670        stmt: &'p Stmt,
671        place: &Place,
672        worlds: &mut Vec<World>,
673        failed: &mut Vec<usize>,
674    ) -> Result<()> {
675        if !place.path.is_empty() {
676            return Ok(());
677        }
678        let mut split = Vec::new();
679        let mut gone = Vec::new();
680        for (i, w) in worlds.iter().enumerate() {
681            let Value::Analytic(a) = &w.slots[place.slot as usize] else {
682                continue;
683            };
684            // `failed` is in order: the worlds that failed to assign.
685            if !a.is_discrete() || failed.binary_search(&i).is_ok() {
686                continue;
687            }
688            if a.form.is_some() {
689                return Err(
690                    analytic::joint_condition("assigning a rounded outcome of several normal draws").at(stmt.span),
691                );
692            }
693            for (v, region, share) in a.atoms() {
694                if share <= 0.0 {
695                    continue;
696                }
697                let mut nw = w.clone().scaled(share);
698                let latent = analytic::Latent {
699                    family: a.family,
700                    domain: a.domain.intersect(&region),
701                    form: None,
702                    weight: a.weight.clone(),
703                };
704                Arc::make_mut(&mut nw.constraints).insert(a.id, latent);
705                if let Err(e) = self.assign(f, place, v, &mut nw, stmt.span) {
706                    self.fail(e, At::new(f, stmt, &nw), None)?;
707                    continue;
708                }
709                split.push(nw);
710            }
711            gone.push(i);
712        }
713        failed.extend(gone);
714        failed.sort_unstable();
715        worlds.extend(split);
716        self.check_worlds(worlds.len() - failed.len(), stmt.span)
717    }
718
719    /// `if p` for a probability `p` drawn from a beta: each branch updates
720    /// it, as observing that side from `bernoulli(p)` would. Whether it was
721    /// such a `p`. (Out of `exec_stmt_kind`, like the next two, which every
722    /// statement runs: inlined, they'd make its frame larger.)
723    #[inline(never)]
724    fn branch_on_draw(
725        &mut self,
726        x: &Analytic,
727        w: &World,
728        yes: &mut Vec<World>,
729        no: &mut Vec<World>,
730        span: Span,
731    ) -> Result<bool> {
732        let sides = [Seen::Bernoulli(true), Seen::Bernoulli(false)];
733        let mut sides = self.outcomes_of(x, w, &sides, span)?.into_iter();
734        let (Some(y), Some(n)) = (sides.next(), sides.next()) else {
735            return Ok(false);
736        };
737        self.check_callback_effect(span)?;
738        yes.extend(y);
739        no.extend(n);
740        Ok(true)
741    }
742
743    /// A draw whose distribution depends on an analytic draw, into `out`:
744    /// from `normal(mean, sd)` with an outcome of normal draws as its mean
745    /// (see `gaussian_draw`), or from `bernoulli(p)` or `binomial(n, p)`
746    /// with a probability drawn from a beta (see `dependent_draw`). Whether
747    /// it was one.
748    #[inline(never)]
749    fn draw_dependent(
750        &mut self,
751        f: FnId,
752        place: &Place,
753        dist: &'p Expr,
754        w: &World,
755        out: &mut Vec<World>,
756    ) -> Result<bool> {
757        if let Some(v) = self.gaussian_draw(f, dist, w)? {
758            let mut w = w.clone();
759            self.assign(f, place, v, &mut w, dist.span)?;
760            out.push(w);
761            return Ok(true);
762        }
763        let Some(outcomes) = self.dependent_draw(f, dist, w)? else {
764            return Ok(false);
765        };
766        let mark = out.len();
767        for (v, mut w) in outcomes {
768            if let Err(e) = self.assign(f, place, v, &mut w, dist.span) {
769                out.truncate(mark);
770                return Err(e);
771            }
772            out.push(w);
773        }
774        Ok(true)
775    }
776
777    /// Update an analytic draw exactly when enumerating: the observation's
778    /// probability, as a logarithm, or `None` if it isn't an update this
779    /// can make.
780    #[inline(never)]
781    fn observe_posterior(&mut self, f: FnId, u: &Update<'p>, w: &mut World, from: Span) -> Result<Option<f64>> {
782        let Some(x) = self.draw_in(w, u.slot, from)? else {
783            return Ok(None);
784        };
785        let Some(seen) = self.seen(f, u, w, from)? else {
786            return Ok(None);
787        };
788        let Some((ln, latent)) = analytic::update(&x, seen).map_err(|e| e.at(from))? else {
789            return Ok(None);
790        };
791        if matches!(seen, Seen::Normal { .. }) {
792            self.check_density(f, from)?;
793            self.densities = true;
794            w.densities += 1;
795        }
796        self.know(w, x.id, latent, from)?;
797        Ok(Some(ln))
798    }
799
800    /// A draw from `normal(mean, sd)` when enumerating, with `mean` an
801    /// outcome of normal draws: the mean plus a fresh normal draw. `None`
802    /// for another draw.
803    fn gaussian_draw(&mut self, f: FnId, dist: &'p Expr, w: &World) -> Result<Option<Value>> {
804        let ExprKind::Builtin {
805            func: Builtin::Normal,
806            args,
807            named,
808        } = &dist.kind
809        else {
810            return Ok(None);
811        };
812        let ([mean, sd], true) = (args.as_slice(), named.is_empty()) else {
813            return Ok(None);
814        };
815        let Value::Analytic(m) = self.eval(f, mean, w)? else {
816            return Ok(None);
817        };
818        let Some(sd) = self.plain_sd(f, sd, w, dist.span)? else {
819            return Ok(None);
820        };
821        self.next_latent += 1;
822        match analytic::normal_draw(&m, sd, self.next_latent).map_err(|e| e.at(dist.span))? {
823            Some(v) => Ok(Some(v)),
824            None => Err(
825                analytic::unsupported("drawing from a normal distribution whose mean is this outcome").at(mean.span),
826            ),
827        }
828    }
829
830    /// `observe y from normal(mean, sd)` when enumerating, with `mean` an
831    /// outcome of normal draws: the observation's density, as a logarithm,
832    /// with the world updated exactly. `None` if the mean isn't an outcome.
833    #[inline(never)]
834    fn observe_gaussian(&mut self, f: FnId, v: &Value, d: &'p Expr, w: &mut World, span: Span) -> Result<Option<f64>> {
835        let ExprKind::Builtin {
836            func: Builtin::Normal,
837            args,
838            named,
839        } = &d.kind
840        else {
841            return Ok(None);
842        };
843        let ([mean, sd], true) = (args.as_slice(), named.is_empty()) else {
844            return Ok(None);
845        };
846        let Value::Analytic(m) = self.eval(f, mean, w)? else {
847            return Ok(None);
848        };
849        let Some(sd) = self.plain_sd(f, sd, w, d.span)? else {
850            return Ok(None);
851        };
852        let y = match v {
853            Value::Int(_) | Value::Float(_) | Value::Prob(_) => v.as_f64(),
854            _ => None,
855        };
856        let Some(y) = y else {
857            return Ok(None);
858        };
859        self.check_density(f, span)?;
860        let observed = analytic::observe_normal(&m, y, sd, &mut self.next_latent).map_err(|e| e.at(span))?;
861        let Some((ln, known)) = observed else {
862            if self.config.integrate.is_some() {
863                return Ok(None);
864            }
865            return Err(analytic::unsupported(
866                "observing a value from a normal distribution whose mean is this outcome",
867            )
868            .at(mean.span));
869        };
870        self.densities = true;
871        w.densities += 1;
872        for (id, latent) in known {
873            Arc::make_mut(&mut w.constraints).insert(id, latent);
874        }
875        self.reread(w, span)?;
876        Ok(Some(ln))
877    }
878
879    /// `observe v from D(…)` with `@integrate`, when parameters of `D` are
880    /// outcomes of one draw: what it says about the draw, as a factor of
881    /// its likelihood, and its probability or density, as a logarithm.
882    /// `None` if it isn't such an observation.
883    #[inline(never)]
884    fn observe_numerically(
885        &mut self,
886        f: FnId,
887        v: &Value,
888        d: &'p Expr,
889        w: &mut World,
890        span: Span,
891    ) -> Result<Option<f64>> {
892        use crate::likelihood::{Factor, Law, Likelihood, Param};
893        let Some(tolerance) = self.config.integrate else {
894            return Ok(None);
895        };
896        let ExprKind::Builtin { func, args, named } = &d.kind else {
897            return Ok(None);
898        };
899        // Each law's parameters, with where each is valid: from, to, and
900        // whether each end is included.
901        type Range = (Option<(f64, bool)>, Option<(f64, bool)>);
902        let positive: Range = (Some((0.0, false)), None);
903        let any: Range = (None, None);
904        let probability: Range = (Some((0.0, true)), Some((1.0, true)));
905        let (law, ranges): (Law, Vec<Range>) = match func {
906            Builtin::Normal => (Law::Normal, vec![any, positive]),
907            Builtin::Lognormal => (Law::Lognormal, vec![any, positive]),
908            Builtin::Uniform => (Law::Uniform, vec![any, any]),
909            Builtin::Beta => (Law::Beta, vec![positive, positive]),
910            Builtin::Gamma => (Law::Gamma, vec![positive, positive]),
911            Builtin::Exponential => (Law::Exponential, vec![positive]),
912            Builtin::Binomial => (Law::Binomial, vec![any, probability]),
913            Builtin::Bernoulli => (Law::Bernoulli, vec![probability]),
914            Builtin::Poisson => (Law::Poisson, vec![(Some((0.0, true)), None)]),
915            Builtin::Geometric => (Law::Geometric, vec![(Some((0.0, false)), Some((1.0, true)))]),
916            _ => return Ok(None),
917        };
918        if !named.is_empty() || args.len() != ranges.len() {
919            return Ok(None);
920        }
921        let mut values = Vec::with_capacity(args.len());
922        for a in args {
923            values.push(self.eval(f, a, w)?);
924        }
925        let mut draw: Option<Arc<Analytic>> = None;
926        for value in &values {
927            match value {
928                Value::Analytic(a) => match &draw {
929                    None => draw = Some(a.clone()),
930                    Some(x) if x.same_axis(a) => {}
931                    Some(_) => {
932                        return Err(analytic::unsupported(
933                            "an observation whose parameters are outcomes of different draws",
934                        )
935                        .at(d.span));
936                    }
937                },
938                v if v.as_f64().is_some() => {}
939                _ => return Ok(None),
940            }
941        }
942        let Some(x) = draw else {
943            return Ok(None);
944        };
945        if x.form.is_some() {
946            return Err(
947                analytic::unsupported("integrating over a sum of draws, which has several dimensions").at(d.span),
948            );
949        }
950        let value = match (law, v) {
951            (Law::Bernoulli, Value::Bool(b)) => f64::from(u8::from(*b)),
952            (_, Value::Int(_) | Value::Float(_) | Value::Prob(_)) => match v.as_f64() {
953                Some(y) => y,
954                None => return Ok(None),
955            },
956            _ => return Ok(None),
957        };
958        // A whole number of trials, as `binomial` takes it.
959        if law == Law::Binomial {
960            let n = &values[0];
961            if !matches!(n, Value::Int(_)) {
962                return Err(analytic::unsupported("a number of trials that's an outcome of a draw").at(d.span));
963            }
964            builtins::call_plain(Builtin::Binomial, &[n.clone(), Value::Prob(0.5)], &mut self.budget)
965                .map_err(|e| e.at(d.span))?;
966        }
967        // Parameters outside their range: everywhere, the fault a number
968        // makes; on part of the draw's range, not supported yet.
969        let mut params = Vec::with_capacity(values.len());
970        for (value, &(lo, hi)) in values.iter().zip(&ranges) {
971            match value {
972                Value::Analytic(a) => {
973                    let bad = analytic::outside(&x, &a.pieces, lo, hi).map_err(|e| e.at(d.span))?;
974                    let mass = bad.mass();
975                    if mass > 0.0 {
976                        let name = law.name();
977                        return Err(if mass >= x.domain.mass() {
978                            RuntimeError::new(d.span, format!("`{name}` isn't defined for these parameters"))
979                                .as_fault(Fault::DomainError)
980                        } else {
981                            analytic::unsupported(&format!(
982                                "`{name}` with a parameter outside its range on part of the draw's range"
983                            ))
984                            .at(d.span)
985                        });
986                    }
987                    params.push(Param::Outcome(a.pieces.clone()));
988                }
989                v => params.push(Param::Plain(v.as_f64().expect("a number"))),
990            }
991        }
992        if law.is_density() {
993            self.check_density(f, span)?;
994        }
995        let mut factors = x.weight.as_ref().map(|l| l.factors.clone()).unwrap_or_default();
996        factors.push(Factor { law, params, value });
997        let before = match &x.weight {
998            None => libm::log(x.domain.mass()),
999            Some(l) => l.shift + libm::log(l.mass(&x.domain)),
1000        };
1001        let likelihood = Likelihood::new(x.family, factors, tolerance, &mut self.budget).map_err(|e| e.at(span))?;
1002        self.integrated = Some(tolerance);
1003        if law.is_density() {
1004            self.densities = true;
1005            w.densities += 1;
1006        }
1007        let Some(likelihood) = likelihood else {
1008            return Ok(Some(f64::NEG_INFINITY));
1009        };
1010        let after = likelihood.shift + libm::log(likelihood.mass(&x.domain));
1011        let latent = analytic::Latent {
1012            family: x.family,
1013            domain: x.domain.clone(),
1014            form: None,
1015            weight: Some(Arc::new(likelihood)),
1016        };
1017        self.know(w, x.id, latent, span)?;
1018        Ok(Some(after - before))
1019    }
1020
1021    /// An observation's distribution that takes outcomes of draws as
1022    /// parameters, without `@integrate`: its error says that `@integrate`
1023    /// would compute it.
1024    #[cold]
1025    fn integration_hint(&self, d: &Expr, e: RuntimeError) -> RuntimeError {
1026        let law = matches!(
1027            &d.kind,
1028            ExprKind::Builtin {
1029                func: Builtin::Normal
1030                    | Builtin::Lognormal
1031                    | Builtin::Uniform
1032                    | Builtin::Beta
1033                    | Builtin::Gamma
1034                    | Builtin::Exponential
1035                    | Builtin::Binomial
1036                    | Builtin::Bernoulli
1037                    | Builtin::Poisson
1038                    | Builtin::Geometric,
1039                ..
1040            }
1041        );
1042        if e.kind != ErrorKind::Unsupported || !law || self.config.integrate.is_some() || self.sampler.is_some() {
1043            return e;
1044        }
1045        e.with_help(
1046            "`@integrate` computes such an observation of one draw by numerical integration, approximately; or use `@mode sample(runs: 10_000)`",
1047        )
1048    }
1049
1050    /// A normal distribution's standard deviation, checked as `normal`
1051    /// checks it; `None` if it isn't a plain value.
1052    fn plain_sd(&mut self, f: FnId, sd: &Expr, w: &World, span: Span) -> Result<Option<f64>> {
1053        let sd = self.eval(f, sd, w)?;
1054        if analytic::contains(&sd) || sd.is_uncertain() {
1055            return Ok(None);
1056        }
1057        let checked = builtins::call_plain(Builtin::Normal, &[Value::Float(0.0), sd], &mut self.budget)
1058            .map_err(|e| e.at(span))?;
1059        let Value::Continuous(family) = checked else {
1060            unreachable!("`normal` gives a continuous distribution")
1061        };
1062        let Family::Normal { sd, .. } = *family else {
1063            unreachable!("`normal` gives a normal distribution")
1064        };
1065        Ok(Some(sd))
1066    }
1067
1068    /// Drawing from `bernoulli(p)` or `binomial(n, p)` when enumerating, with
1069    /// `p` a variable drawn from a beta: each outcome, in a world where `p`
1070    /// is updated exactly by it. `None` for another draw.
1071    fn dependent_draw(&mut self, f: FnId, dist: &'p Expr, w: &World) -> Result<Option<Vec<(Value, World)>>> {
1072        let ExprKind::Builtin { func, args, named } = &dist.kind else {
1073            return Ok(None);
1074        };
1075        if self.sampler.is_some() || !named.is_empty() {
1076            return Ok(None);
1077        }
1078        let (p, trials) = match (func, args.as_slice()) {
1079            (Builtin::Bernoulli, [p]) => (p, None),
1080            (Builtin::Binomial, [n, p]) => (p, Some(n)),
1081            _ => return Ok(None),
1082        };
1083        // The variable, or `prob` of it.
1084        let slot = match &p.kind {
1085            ExprKind::Slot(s) => *s,
1086            ExprKind::Builtin {
1087                func: Builtin::Prob,
1088                args,
1089                ..
1090            } => match args.as_slice() {
1091                [
1092                    Expr {
1093                        kind: ExprKind::Slot(s),
1094                        ..
1095                    },
1096                ] => *s,
1097                _ => return Ok(None),
1098            },
1099            _ => return Ok(None),
1100        };
1101        let Some(x) = self.draw_in(w, slot, p.span)? else {
1102            return Ok(None);
1103        };
1104        let outcomes: Vec<(Value, Seen)> = match trials {
1105            None => [false, true].map(|b| (Value::Bool(b), Seen::Bernoulli(b))).into(),
1106            Some(n) => {
1107                let n = self.eval(f, n, w)?;
1108                if analytic::contains(&n) {
1109                    return Ok(None);
1110                }
1111                let counts = builtins::counts(Builtin::Binomial, &[n, Value::Prob(0.5)], &mut self.budget)
1112                    .map_err(|e| e.at(dist.span))?;
1113                let Some(Counts::Binomial { n, .. }) = counts else {
1114                    return Ok(None);
1115                };
1116                self.budget.outcomes(n as u128 + 1).map_err(|e| e.at(dist.span))?;
1117                (0..=n)
1118                    .map(|k| (Value::Int(k.into()), Seen::Binomial { trials: n, k: k as f64 }))
1119                    .collect()
1120            }
1121        };
1122        let seen: Vec<Seen> = outcomes.iter().map(|(_, s)| *s).collect();
1123        let worlds = self.outcomes_of(&x, w, &seen, dist.span)?;
1124        if worlds.is_empty() {
1125            return Ok(None);
1126        }
1127        Ok(Some(
1128            outcomes
1129                .into_iter()
1130                .zip(worlds)
1131                .filter_map(|((v, _), w)| Some((v, w?)))
1132                .collect(),
1133        ))
1134    }
1135
1136    /// The worlds where a draw `x`, the parameter of a likelihood, saw each
1137    /// of `seen`, updated exactly: an empty list if `x` isn't the prior of
1138    /// that likelihood, and `None` for what `x` can't have seen.
1139    fn outcomes_of(&mut self, x: &Analytic, w: &World, seen: &[Seen], span: Span) -> Result<Vec<Option<World>>> {
1140        let mut out = Vec::with_capacity(seen.len());
1141        for &s in seen {
1142            let Some((ln, latent)) = analytic::update(x, s).map_err(|e| e.at(span))? else {
1143                return Ok(Vec::new());
1144            };
1145            if ln == f64::NEG_INFINITY {
1146                out.push(None);
1147                continue;
1148            }
1149            let mut w = w.clone();
1150            w.weight = w.weight * Weight::from_ln(ln);
1151            self.know(&mut w, x.id, latent, span)?;
1152            out.push(Some(w));
1153        }
1154        Ok(out)
1155    }
1156
1157    /// The analytic outcome in `slot`, as the world reads it.
1158    fn draw_in(&mut self, w: &World, slot: SlotId, span: Span) -> Result<Option<Analytic>> {
1159        let v = &w.slots[slot as usize];
1160        if !matches!(v, Value::Analytic(_)) {
1161            return Ok(None);
1162        }
1163        Ok(
1164            match analytic::resolve(v, &w.constraints, &mut self.budget).map_err(|e| e.at(span))? {
1165                Value::Analytic(x) => Some((*x).clone()),
1166                _ => None,
1167            },
1168        )
1169    }
1170
1171    /// Record what a world now knows about a latent, and read the values
1172    /// that hold it again, as they would be read.
1173    fn know(&mut self, w: &mut World, id: u64, latent: analytic::Latent, span: Span) -> Result<()> {
1174        Arc::make_mut(&mut w.constraints).insert(id, latent);
1175        self.reread(w, span)
1176    }
1177
1178    /// Read the values that hold latents again, as the world reads them.
1179    fn reread(&mut self, w: &mut World, span: Span) -> Result<()> {
1180        for slot in &mut w.slots {
1181            if analytic::contains(slot) {
1182                *slot = analytic::resolve(slot, &w.constraints, &mut self.budget).map_err(|e| e.at(span))?;
1183            }
1184        }
1185        Ok(())
1186    }
1187
1188    /// What a conjugate observation saw, checked as when its variable is
1189    /// drawn; `None` if a part of it isn't a plain value.
1190    fn seen(&mut self, f: FnId, u: &Update<'p>, w: &World, from: Span) -> Result<Option<Seen>> {
1191        if self.sampler.is_none() {
1192            for e in u.others() {
1193                if analytic::contains(&self.eval(f, e, w)?) {
1194                    return Ok(None);
1195                }
1196            }
1197        }
1198        // A count, as observing one from a drawn distribution reads it:
1199        // anything but a whole number of 0 or more is impossible.
1200        let count = |v: &Value| match v {
1201            Value::Bool(_) => f64::NAN,
1202            _ => v.as_f64().unwrap_or(f64::NAN),
1203        };
1204        Ok(match u.likelihood {
1205            Likelihood::Binomial { value, trials } => {
1206                let v = self.eval(f, value, w)?;
1207                let n = self.eval(f, trials, w)?;
1208                match builtins::counts(Builtin::Binomial, &[n, Value::Prob(0.5)], &mut self.budget)
1209                    .map_err(|e| e.at(from))?
1210                {
1211                    Some(Counts::Binomial { n, .. }) => Some(Seen::Binomial {
1212                        trials: n,
1213                        k: count(&v),
1214                    }),
1215                    _ => None,
1216                }
1217            }
1218            Likelihood::Bernoulli { value } => match self.eval(f, value, w)? {
1219                Value::Bool(b) => Some(Seen::Bernoulli(b)),
1220                _ => None,
1221            },
1222            Likelihood::Poisson { value } => {
1223                let v = self.eval(f, value, w)?;
1224                Some(Seen::Poisson { k: count(&v) })
1225            }
1226            Likelihood::Normal { value, sd } => {
1227                let v = self.eval(f, value, w)?;
1228                let sd = self.eval(f, sd, w)?;
1229                if sd.is_uncertain() {
1230                    return Ok(None);
1231                }
1232                let checked = builtins::call_plain(Builtin::Normal, &[Value::Float(0.0), sd], &mut self.budget)
1233                    .map_err(|e| e.at(from))?;
1234                let Value::Continuous(family) = checked else {
1235                    unreachable!("`normal` gives a continuous distribution")
1236                };
1237                let Family::Normal { sd, .. } = *family else {
1238                    unreachable!("`normal` gives a normal distribution")
1239                };
1240                match v {
1241                    Value::Int(_) | Value::Float(_) | Value::Prob(_) if v.as_f64().is_some() => Some(Seen::Normal {
1242                        y: v.as_f64().expect("a number"),
1243                        sd,
1244                    }),
1245                    _ => None,
1246                }
1247            }
1248        })
1249    }
1250
1251    fn merge(&self, worlds: Vec<World>, stmt: StmtId) -> Vec<World> {
1252        merge(worlds, &self.live.after[stmt as usize], self.merging())
1253    }
1254
1255    /// Whether equal worlds merge: never when sampling, since every run
1256    /// counts separately (section 14), but inside `simulate` they do.
1257    fn merging(&self) -> bool {
1258        self.config.merging && self.sampler.is_none()
1259    }
1260
1261    /// Whether a fault ends only its world: in partial mode, except inside
1262    /// `simulate` and collection callbacks, which fail as a whole, in the
1263    /// world that runs them (docs/semantics.md, section 11).
1264    fn partial(&self) -> bool {
1265        self.config.partial && self.nested == 0 && self.callback.is_none()
1266    }
1267
1268    /// Whether a `try` running in this call catches `fault`.
1269    fn catches_here(&self, fault: Option<Fault>) -> bool {
1270        self.handlers
1271            .iter()
1272            .any(|(depth, catches)| *depth == self.depth && catches.iter().any(|c| c.catches(fault)))
1273    }
1274
1275    /// Whether a `try` running in a call further up catches `fault`.
1276    fn catches_above(&self, fault: Option<Fault>) -> bool {
1277        self.handlers
1278            .iter()
1279            .any(|(depth, catches)| *depth < self.depth && catches.iter().any(|c| c.catches(fault)))
1280    }
1281
1282    /// Inside a `try` of this call: a copy of `w`, for the catch it may
1283    /// fault to. Elsewhere nothing is copied.
1284    #[inline]
1285    fn snapshot(&self, w: &World) -> Option<World> {
1286        if self.handlers.is_empty() {
1287            return None;
1288        }
1289        self.handlers
1290            .iter()
1291            .any(|(depth, _)| *depth == self.depth)
1292            .then(|| w.clone())
1293    }
1294
1295    /// A world at `at` failed with `e`. A fault that a `try` of this call
1296    /// catches sends `saved`, the world as its statement began, on to the
1297    /// catch. One that a `try` further up catches, or any fault in partial
1298    /// mode, ends the world here: it's recorded, for the call's caller to
1299    /// catch, or as a failure. Either way the statement goes on with the
1300    /// other worlds. Anything else stops the run.
1301    fn fail(&mut self, e: RuntimeError, at: At, saved: Option<World>) -> Result<()> {
1302        if e.fault.is_none() {
1303            return Err(e);
1304        }
1305        if let Some(w) = saved.filter(|_| self.catches_here(e.fault)) {
1306            self.faulted.push((w, e));
1307            return Ok(());
1308        }
1309        if !self.partial() && !self.catches_above(e.fault) {
1310            return Err(e);
1311        }
1312        let fun = &self.prog.functions[at.f as usize];
1313        let e = match fun.kind {
1314            FnKind::Named => e.with_note(format!("in a call to `{}`", fun.name)),
1315            FnKind::Lambda => e.with_note("inside a lambda"),
1316            FnKind::Simulate => e.with_note("inside a `simulate` block"),
1317            FnKind::Main => e,
1318        };
1319        let stmt = at.stmt as usize;
1320        let before_evidence = self.evidence.at[stmt] || self.evidence.after[stmt];
1321        let run = self.sampler.is_some().then_some(at.run);
1322        self.failures.record(e, at.weight, run, before_evidence, at.densities);
1323        Ok(())
1324    }
1325
1326    /// Stop if a statement produced more worlds than allowed. (When sampling,
1327    /// worlds never multiply: there's one per run in the batch.)
1328    fn check_worlds(&self, n: usize, span: Span) -> Result<()> {
1329        if n > self.config.max_worlds && self.sampler.is_none() {
1330            return Err(RuntimeError::limit(
1331                span,
1332                format!("more than {} worlds are too many to follow", self.config.max_worlds),
1333            )
1334            .with_help("simplify the model, raise the limit, or sample it with `@mode sample(runs: 10_000)`"));
1335        }
1336        Ok(())
1337    }
1338
1339    fn spend(&mut self, n: u64, span: Span) -> Result<()> {
1340        self.budget.work(n).map_err(|e| e.at(span))?;
1341        if let Some(cancel) = &self.config.cancel {
1342            if cancel.load(Ordering::Relaxed) {
1343                return Err(RuntimeError::limit(span, "the run was cancelled"));
1344            }
1345        }
1346        Ok(())
1347    }
1348
1349    // ── Statements ───────────────────────────────────────────────────────
1350
1351    fn exec_block(&mut self, f: FnId, block: &'p Block, worlds: Vec<World>) -> Result<Flow> {
1352        let mut flow = Flow::next(worlds);
1353        for stmt in &block.stmts {
1354            if flow.next.is_empty() {
1355                break;
1356            }
1357            let input = std::mem::take(&mut flow.next);
1358            let out = self.exec_stmt(f, stmt, input)?;
1359            flow.next = out.next;
1360            clear(&mut flow.next, &self.live.dies[stmt.id as usize]);
1361            flow.broke.extend(out.broke);
1362            flow.continued.extend(out.continued);
1363            flow.returned.extend(out.returned);
1364            flow.faulted.extend(out.faulted);
1365        }
1366        Ok(flow)
1367    }
1368
1369    /// Weighs an enumerated world by the density `exp(ln)` of what an
1370    /// observation at `span` saw.
1371    fn observe_density(&mut self, f: FnId, ln: f64, w: &mut World, span: Span) -> Result<()> {
1372        self.check_density(f, span)?;
1373        if ln == f64::INFINITY {
1374            return Err(RuntimeError::new(span, "the density at the observed value is infinite"));
1375        }
1376        if ln == f64::NEG_INFINITY {
1377            self.lost += w.weight;
1378            w.weight = Weight::ZERO;
1379            self.last_ruling_out = Some(span);
1380        } else {
1381            w.weight = w.weight * Weight::from_ln(ln);
1382            w.densities += 1;
1383        }
1384        Ok(())
1385    }
1386
1387    /// An enumerated world's weight can include a density only where the
1388    /// world counts it (see `World::densities`).
1389    /// A `simulate` block's own observations can be densities too: its
1390    /// result is normalized, as long as its worlds include as many.
1391    #[inline(never)]
1392    fn check_density(&mut self, f: FnId, span: Span) -> Result<()> {
1393        if self.depth == 0 {
1394            self.density_at.get_or_insert(span);
1395            return Ok(());
1396        }
1397        if self.prog.functions[f as usize].kind == FnKind::Simulate {
1398            self.local_density_at.get_or_insert(span);
1399            return Ok(());
1400        }
1401        Err(OpError::unsupported(
1402            "observing a value from a continuous distribution inside a function isn't supported when enumerating yet",
1403        )
1404        .help("observe it in the main program or directly inside `simulate`, or sample the model with `@mode sample(runs: 10_000)`")
1405        .at(span))
1406    }
1407
1408    /// A world returning from a `simulate` block: its weight must include as
1409    /// many densities as the others'.
1410    #[inline(never)]
1411    fn simulate_returns(&mut self, w: &World, span: Span) -> Result<()> {
1412        match self.returned_densities {
1413            Some(n) if n != w.densities => Err(OpError::unsupported(
1414                "a `simulate` whose worlds observe different numbers of continuous values isn't supported when enumerating",
1415            )
1416            .help("a density is per unit of what it observes, so worlds with more of them can't be weighed against the others; observe the same values in every world")
1417            .at(self.local_density_at.unwrap_or(span))),
1418            _ => {
1419                self.returned_densities = Some(w.densities);
1420                Ok(())
1421            }
1422        }
1423    }
1424
1425    /// The parts of a world where an event holds, or doesn't: each with its
1426    /// probability given what the world knew, and the restrictions there.
1427    /// An event of several independent draws has a part for each box.
1428    fn event_parts(
1429        &mut self,
1430        event: &analytic::Event,
1431        yes: bool,
1432        constraints: &analytic::Constraints,
1433        span: Span,
1434    ) -> Result<Vec<(f64, analytic::Constraints)>> {
1435        if !event.restrictable() {
1436            return Err(analytic::joint_condition("a condition on several normal draws together").at(span));
1437        }
1438        let boxes = event.side(yes).map_err(|e| e.at(span))?;
1439        let n = boxes.len().max(1);
1440        self.budget
1441            .collection(((constraints.len() + event.draws.len()) * n) as u128)
1442            .map_err(|e| e.at(span))?;
1443        self.budget
1444            .work(((constraints.len() + event.size()) * n) as u64)
1445            .map_err(|e| e.at(span))?;
1446        let mut parts = Vec::with_capacity(boxes.len());
1447        for part in &boxes {
1448            let mut c = constraints.clone();
1449            let p = event.restrict(part, &mut c);
1450            parts.push((p, c));
1451        }
1452        Ok(parts)
1453    }
1454
1455    /// `if` on an event: each part of the world where it holds goes to
1456    /// `yes`, and each where it doesn't to `no`, restricted to that part.
1457    #[inline(never)]
1458    fn branch_on_event(
1459        &mut self,
1460        event: &analytic::Event,
1461        w: World,
1462        yes: &mut Vec<World>,
1463        no: &mut Vec<World>,
1464        span: Span,
1465    ) -> Result<()> {
1466        let holds = self.event_parts(event, true, &w.constraints, span)?;
1467        let fails = self.event_parts(event, false, &w.constraints, span)?;
1468        let parts: Vec<_> = (holds.into_iter().map(|(p, c)| (true, p, c)))
1469            .chain(fails.into_iter().map(|(p, c)| (false, p, c)))
1470            .filter(|(_, p, _)| *p > 0.0)
1471            .collect();
1472        let last = parts.len().saturating_sub(1);
1473        let mut w = Some(w);
1474        for (i, (side, p, c)) in parts.into_iter().enumerate() {
1475            // The last part takes the world itself.
1476            let mut nw = if i == last { w.take() } else { w.clone() }.expect("the world");
1477            nw.constraints = c;
1478            let out = if side { &mut *yes } else { &mut *no };
1479            out.push(nw.scaled(p));
1480        }
1481        Ok(())
1482    }
1483
1484    /// `observe` of an event, or of one side of it: each part of the world
1485    /// where it holds goes on, restricted to that part.
1486    #[inline(never)]
1487    fn observe_event(
1488        &mut self,
1489        event: &analytic::Event,
1490        yes: bool,
1491        w: World,
1492        out: &mut Vec<World>,
1493        span: Span,
1494    ) -> Result<()> {
1495        let parts = self.event_parts(event, yes, &w.constraints, span)?;
1496        let p: f64 = parts.iter().map(|(p, _)| p).sum();
1497        if p < 1.0 && self.sampler.is_none() {
1498            self.lost += w.weight.scale(1.0 - p);
1499        }
1500        if p <= 0.0 {
1501            self.last_ruling_out = Some(span);
1502        }
1503        let parts: Vec<_> = parts.into_iter().filter(|(p, _)| *p > 0.0).collect();
1504        let last = parts.len().saturating_sub(1);
1505        let mut w = Some(w);
1506        for (i, (p, c)) in parts.into_iter().enumerate() {
1507            let mut nw = if i == last { w.take() } else { w.clone() }.expect("the world");
1508            nw.constraints = c;
1509            out.push(nw.scaled(p));
1510        }
1511        Ok(())
1512    }
1513
1514    fn exec_stmt(&mut self, f: FnId, stmt: &'p Stmt, worlds: Vec<World>) -> Result<Flow> {
1515        // Outside every `try`, no world can fault to a catch.
1516        if self.handlers.is_empty() {
1517            return self.exec_stmt_kind(f, stmt, worlds);
1518        }
1519        // The worlds that fault in this statement, on their way to a catch,
1520        // leave with its flow.
1521        let outer = std::mem::take(&mut self.faulted);
1522        let flow = self.exec_stmt_kind(f, stmt, worlds);
1523        let faulted = std::mem::replace(&mut self.faulted, outer);
1524        let mut flow = flow?;
1525        flow.faulted.extend(faulted);
1526        Ok(flow)
1527    }
1528
1529    fn exec_stmt_kind(&mut self, f: FnId, stmt: &'p Stmt, worlds: Vec<World>) -> Result<Flow> {
1530        let span = stmt.span;
1531        if matches!(
1532            stmt.kind,
1533            StmtKind::Draw { .. } | StmtKind::Take { .. } | StmtKind::Observe { .. } | StmtKind::Chance { .. }
1534        ) {
1535            self.check_callback_effect(span)?;
1536        }
1537        let n = worlds.len();
1538        self.stats.world_steps += n as u64;
1539        self.stats.peak_worlds = self.stats.peak_worlds.max(n);
1540        self.spend(n as u64, span)?;
1541        self.check_worlds(n, span)?;
1542        let mut worlds = worlds;
1543        if self.delaying() {
1544            // Delayed variables this statement reads are drawn first.
1545            let conj = self.conj;
1546            let first = &conj.draws_first[stmt.id as usize];
1547            if !first.is_empty() {
1548                for w in &mut worlds {
1549                    for &slot in first {
1550                        self.draw_delayed(w, slot);
1551                    }
1552                }
1553            }
1554        }
1555        match &stmt.kind {
1556            StmtKind::Set { place, value } | StmtKind::Update { place, value, .. } => {
1557                let update = match &stmt.kind {
1558                    StmtKind::Update { check, .. } => Some(check.as_ref()),
1559                    _ => None,
1560                };
1561                let mut worlds = worlds;
1562                let mut failed = Vec::new();
1563                let mut discrete = false;
1564                for (i, w) in worlds.iter_mut().enumerate() {
1565                    let mut saved = self.snapshot(w);
1566                    if let Some(check) = update {
1567                        let updated = self.update(f, place, value, check, w, span);
1568                        each!(self, At::new(f, stmt, w), saved saved, updated, { failed.push(i) });
1569                        continue;
1570                    }
1571                    let v = match self.record_place_type(place, w) {
1572                        Some(ty) => self.eval_expected(f, value, w, &ty),
1573                        None => self.eval(f, value, w),
1574                    };
1575                    let v = each!(self, At::new(f, stmt, w), saved saved, v, { failed.push(i) });
1576                    if let Value::Analytic(_) = v {
1577                        discrete |= is_discrete(&v);
1578                    }
1579                    each!(self, At::new(f, stmt, w), saved saved, self.assign(f, place, v, w, span), {
1580                        failed.push(i)
1581                    });
1582                }
1583                if discrete {
1584                    self.split_discrete(f, stmt, place, &mut worlds, &mut failed)?;
1585                }
1586                drop_failed(&mut worlds, &failed);
1587                // When the live values say less than before, as when a
1588                // call's result is used up (`f = f - $5`), worlds may now be
1589                // the same: merging spares the statements after from
1590                // following each of them (a call next would multiply them).
1591                if self.live.narrows[stmt.id as usize] && self.merging() {
1592                    return Ok(Flow::next(self.merge(worlds, stmt.id)));
1593                }
1594                Ok(Flow::next(worlds))
1595            }
1596            StmtKind::Draw { place, dist } => {
1597                // A conjugate prior's draw may be delayed (section 14).
1598                let delay = if self.delaying() {
1599                    self.conj.delays[stmt.id as usize]
1600                } else {
1601                    None
1602                };
1603                let mut out = Vec::with_capacity(worlds.len());
1604                for w in worlds {
1605                    let at = At::new(f, stmt, &w);
1606                    let mut saved = self.snapshot(&w);
1607                    if self.sampler.is_some() {
1608                        if let Some(counts) = each!(self, at, saved saved, self.direct_counts(f, dist, &w)) {
1609                            let k = counts.sample(self.sampler.as_mut().expect("sampling"));
1610                            let mut w = w;
1611                            each!(self, at, saved saved, self.assign(f, place, Value::Int(k.into()), &mut w, dist.span));
1612                            out.push(w);
1613                            continue;
1614                        }
1615                    }
1616                    if self.sampler.is_none()
1617                        && each!(self, at, saved saved, self.draw_dependent(f, place, dist, &w, &mut out))
1618                    {
1619                        self.check_worlds(out.len(), span)?;
1620                        continue;
1621                    }
1622                    let d = each!(self, at, saved saved, self.eval(f, dist, &w));
1623                    if let (Some(variable), Value::Continuous(family)) = (delay, &d) {
1624                        if conjugate::is_prior(family) {
1625                            let mut w = w;
1626                            w.slots[place.slot as usize] = Value::Delayed(Arc::new(Delayed {
1627                                family: **family,
1628                                variable,
1629                            }));
1630                            self.updates(variable).delayed += 1;
1631                            out.push(w);
1632                            continue;
1633                        }
1634                    }
1635                    let mark = out.len();
1636                    each!(self, at, saved saved, self.split_by(f, place, d, w, dist.span, &mut out), {
1637                        out.truncate(mark)
1638                    });
1639                    self.check_worlds(out.len(), span)?;
1640                }
1641                Ok(Flow::next(self.merge(out, stmt.id)))
1642            }
1643            StmtKind::Take { place, bag } => {
1644                let mut out = Vec::new();
1645                for w in worlds {
1646                    let at = At::new(f, stmt, &w);
1647                    let current = each!(self, at, world w, self.read_place(f, bag, &w, span));
1648                    let Value::Bag(cards) = &current else {
1649                        return Err(RuntimeError::new(
1650                            span,
1651                            format!("`take` needs a bag, found {}", ops::article(&current.kind())),
1652                        )
1653                        .with_help("make one with `bag([card: count, …])`"));
1654                    };
1655                    let total: u128 = cards.values().map(|n| *n as u128).sum();
1656                    if total == 0 {
1657                        let empty = RuntimeError::new(span, "can't take a card from an empty bag")
1658                            .as_fault(Fault::EmptyCollection);
1659                        each!(self, at, world w, Err::<(), _>(empty));
1660                    }
1661                    let chosen = match &mut self.sampler {
1662                        Some(rng) => rng.choose(cards.values().map(|n| *n as f64)),
1663                        None => None,
1664                    };
1665                    for (i, (card, count)) in cards.iter().enumerate() {
1666                        if self.sampler.is_some() && chosen != Some(i) {
1667                            continue;
1668                        }
1669                        let rest = cards.without_nth(i);
1670                        let mut nw = if self.sampler.is_some() {
1671                            w.clone()
1672                        } else {
1673                            w.clone().scaled(*count as f64 / total as f64)
1674                        };
1675                        self.assign(f, bag, Value::multiset(rest), &mut nw, span)?;
1676                        self.assign(f, place, card.clone(), &mut nw, span)?;
1677                        out.push(nw);
1678                    }
1679                    self.check_worlds(out.len(), span)?;
1680                }
1681                Ok(Flow::next(self.merge(out, stmt.id)))
1682            }
1683            StmtKind::Call { dest, callee, args } => {
1684                let mut out = Vec::with_capacity(worlds.len());
1685                for w in worlds {
1686                    let at = At::new(f, stmt, &w);
1687                    let mut key = Vec::with_capacity(args.len() + 4);
1688                    let evaluated: Result<()> = args.iter().try_for_each(|a| {
1689                        key.push(self.eval(f, a, &w)?);
1690                        Ok(())
1691                    });
1692                    each!(self, at, world w, evaluated);
1693                    let func = match callee {
1694                        Callee::Fn { func, capture_args } => {
1695                            self.capture(&mut key, f, *func, capture_args, &w, span, false)?;
1696                            *func
1697                        }
1698                        Callee::Value(e, named) => match each!(self, at, world w, self.eval(f, e, &w)) {
1699                            Value::Builtin(b) => {
1700                                let value =
1701                                    each!(self, at, world w, self.builtin_values(b, &key, named, w.weight, span));
1702                                let mut saved = self.snapshot(&w);
1703                                let mut nw = w;
1704                                each!(self, at, saved saved, self.assign(f, dest, value, &mut nw, span));
1705                                out.push(nw);
1706                                continue;
1707                            }
1708                            Value::Closure(c) => {
1709                                if !named.is_empty() {
1710                                    return Err(RuntimeError::new(
1711                                        span,
1712                                        "only builtin minimum/maximum accept named defaults",
1713                                    ));
1714                                }
1715                                self.check_arity(&c, args.len(), span)?;
1716                                key.extend(c.captured.iter().cloned());
1717                                c.func
1718                            }
1719                            other => {
1720                                return Err(RuntimeError::new(
1721                                    e.span,
1722                                    format!("can't call {}", ops::article(&other.kind())),
1723                                ));
1724                            }
1725                        },
1726                    };
1727                    let result = each!(self, at, world w, self.call(func, key, span));
1728                    self.unresolved += w.weight * result.unresolved;
1729                    self.lost += w.weight * result.lost;
1730                    // What faulted in the call faulted in this world: a catch
1731                    // here takes it with the world as it was, and otherwise
1732                    // the world fails, for a `try` further up or as a
1733                    // failure, and may still meet evidence after the call.
1734                    let run = self.sampler.is_some().then_some(w.run);
1735                    let after = self.evidence.after[stmt.id as usize];
1736                    for g in &result.failures.groups {
1737                        if self.catches_here(g.error.fault) {
1738                            let mut caught = w.clone();
1739                            caught.weight = caught.weight * g.weight;
1740                            self.faulted.push((caught, g.error.clone()));
1741                        } else if self.partial() || self.catches_above(g.error.fault) {
1742                            self.failures.absorb_group(g, w.weight, run, after, w.densities);
1743                        } else {
1744                            return Err(g.error.clone());
1745                        }
1746                    }
1747                    add_pending(&mut self.pending, &result.pending, w.weight);
1748                    let last = result.outcomes.len().saturating_sub(1);
1749                    let mut w = Some(w);
1750                    for (i, (v, p, restrictions)) in result.outcomes.iter().enumerate() {
1751                        let mut nw = if i == last {
1752                            w.take().unwrap()
1753                        } else {
1754                            w.clone().unwrap()
1755                        };
1756                        nw.weight = nw.weight * *p;
1757                        // Only copy the constraints (shared with the other
1758                        // outcomes' worlds) when the call added some.
1759                        if !restrictions.is_empty() {
1760                            Arc::make_mut(&mut nw.constraints)
1761                                .extend(restrictions.iter().map(|(id, d)| (*id, d.clone())));
1762                        }
1763                        self.assign(f, dest, v.clone(), &mut nw, span)?;
1764                        out.push(nw);
1765                    }
1766                    self.check_worlds(out.len(), span)?;
1767                }
1768                Ok(Flow::next(self.merge(out, stmt.id)))
1769            }
1770            StmtKind::If { cond, then, otherwise } => {
1771                let (mut yes, mut no) = (Vec::new(), Vec::new());
1772                for w in worlds {
1773                    let condition = each!(self, At::new(f, stmt, &w), world w, self.eval(f, cond, &w));
1774                    if let Value::Event(event) = condition {
1775                        self.check_callback_effect(cond.span)?;
1776                        self.branch_on_event(&event, w, &mut yes, &mut no, cond.span)?;
1777                        continue;
1778                    }
1779                    if let Value::Analytic(x) = &condition {
1780                        if self.branch_on_draw(x, &w, &mut yes, &mut no, cond.span)? {
1781                            continue;
1782                        }
1783                    }
1784                    let c = ops::condition(&condition).map_err(|e| e.at(cond.span))?;
1785                    if (c.yes > 0.0 && c.no > 0.0) || c.missing > 0.0 {
1786                        self.check_callback_effect(cond.span)?;
1787                    }
1788                    if let Some(rng) = &mut self.sampler {
1789                        // One branch, chosen with its probability.
1790                        match rng.choose([c.yes, c.no].into_iter()) {
1791                            Some(0) => yes.push(w),
1792                            Some(_) => no.push(w),
1793                            None => self.unresolved += w.weight,
1794                        }
1795                        continue;
1796                    }
1797                    let (p, q) = (c.yes, c.no);
1798                    if c.missing > 0.0 {
1799                        self.unresolved += w.weight.scale(c.missing);
1800                    }
1801                    if q <= 0.0 {
1802                        yes.push(w.scaled(p));
1803                    } else if p <= 0.0 {
1804                        no.push(w.scaled(q));
1805                    } else {
1806                        no.push(w.clone().scaled(q));
1807                        yes.push(w.scaled(p));
1808                    }
1809                }
1810                self.check_worlds(yes.len() + no.len(), span)?;
1811                let mut flow = Flow::default();
1812                if !yes.is_empty() {
1813                    flow.join(self.exec_block(f, then, yes)?);
1814                }
1815                if !no.is_empty() {
1816                    flow.join(self.exec_block(f, otherwise, no)?);
1817                }
1818                flow.next = self.merge(flow.next, stmt.id);
1819                Ok(flow)
1820            }
1821            StmtKind::Chance {
1822                arms,
1823                otherwise,
1824                exhaustive,
1825            } => {
1826                let mut buckets: Vec<Vec<World>> = vec![Vec::new(); arms.len()];
1827                let mut rest = Vec::new();
1828                for w in worlds {
1829                    let at = At::new(f, stmt, &w);
1830                    // Chance weights are explicit probabilities.
1831                    let chances: Result<Vec<f64>> = arms
1832                        .iter()
1833                        .map(|(weight, _)| {
1834                            let value = self.eval(f, weight, &w)?;
1835                            ops::to_prob(&value).map_err(|e| e.at(weight.span))
1836                        })
1837                        .collect();
1838                    let chances = each!(self, at, world w, chances);
1839                    let sum: f64 = chances.iter().sum();
1840                    if sum > 1.0 + 1e-9 {
1841                        let over = RuntimeError::new(
1842                            span,
1843                            format!(
1844                                "the chances in this `chance` add up to {}, more than 100%",
1845                                fmt_prob(sum)
1846                            ),
1847                        );
1848                        each!(self, at, world w, Err::<(), _>(over.as_fault(Fault::DomainError)));
1849                    }
1850                    let remainder = (1.0 - sum).max(0.0);
1851                    if remainder > 1e-9 && *exhaustive && otherwise.is_none() {
1852                        let short = RuntimeError::new(
1853                            span,
1854                            format!("the chances add up to {} and there's no `else` arm", fmt_prob(sum)),
1855                        )
1856                        .with_help("a `chance` used as a value needs its chances to add up to 100%, or an `else`");
1857                        each!(self, at, world w, Err::<(), _>(short.as_fault(Fault::DomainError)));
1858                    }
1859                    if let Some(rng) = &mut self.sampler {
1860                        // One arm, chosen with its probability.
1861                        let options = chances.iter().copied().chain(std::iter::once(remainder));
1862                        match rng.choose(options) {
1863                            Some(i) if i < arms.len() => buckets[i].push(w),
1864                            Some(_) => rest.push(w),
1865                            None => self.unresolved += w.weight,
1866                        }
1867                        continue;
1868                    }
1869                    for (bucket, p) in buckets.iter_mut().zip(&chances) {
1870                        if *p > 0.0 {
1871                            bucket.push(w.clone().scaled(*p));
1872                        }
1873                    }
1874                    if remainder > 1e-12 {
1875                        rest.push(w.scaled(remainder));
1876                    }
1877                }
1878                self.check_worlds(buckets.iter().map(Vec::len).sum::<usize>() + rest.len(), span)?;
1879                let mut flow = Flow::default();
1880                for ((_, body), bucket) in arms.iter().zip(buckets) {
1881                    if !bucket.is_empty() {
1882                        flow.join(self.exec_block(f, body, bucket)?);
1883                    }
1884                }
1885                if !rest.is_empty() {
1886                    match otherwise {
1887                        Some(body) => flow.join(self.exec_block(f, body, rest)?),
1888                        None if *exhaustive => {}
1889                        None => flow.next.extend(rest),
1890                    }
1891                }
1892                flow.next = self.merge(flow.next, stmt.id);
1893                Ok(flow)
1894            }
1895            StmtKind::Loop { body, bounded } => self.exec_loop(f, stmt, body, *bounded, worlds),
1896            StmtKind::Break => Ok(Flow {
1897                broke: worlds,
1898                ..Flow::default()
1899            }),
1900            StmtKind::Continue => Ok(Flow {
1901                continued: worlds,
1902                ..Flow::default()
1903            }),
1904            StmtKind::Return(value) => self.exec_return(f, stmt, value, worlds),
1905            StmtKind::Observe { value, from } => {
1906                self.observed = true;
1907                // When sampling, an update of a delayed draw; when
1908                // enumerating, of an analytic one.
1909                let exact = if self.delaying() || self.sampler.is_none() {
1910                    probl_sema::conjugate::update(value, from.as_ref())
1911                } else {
1912                    None
1913                };
1914                let mut out = Vec::with_capacity(worlds.len());
1915                for mut w in worlds {
1916                    let here = At::new(f, stmt, &w);
1917                    let mut saved = self.snapshot(&w);
1918                    if let Some(u) = &exact {
1919                        let at = from.as_ref().map_or(span, |d| d.span);
1920                        let ln = if self.sampler.is_some() {
1921                            self.observe_exactly(f, u, &mut w, at)
1922                        } else {
1923                            self.observe_posterior(f, u, &mut w, at)
1924                        };
1925                        if let Some(ln) = each!(self, here, saved saved, ln) {
1926                            w.weight = w.weight * Weight::from_ln(ln);
1927                            if w.weight.is_zero() {
1928                                self.last_ruling_out = Some(span);
1929                            } else {
1930                                out.push(w);
1931                            }
1932                            continue;
1933                        }
1934                    }
1935                    let (factor, missing, ruled_out) = match from {
1936                        None => {
1937                            let v = each!(self, here, saved saved, self.eval(f, value, &w));
1938                            if let Value::Event(event) = v {
1939                                self.observe_event(&event, true, w, &mut out, value.span)?;
1940                                continue;
1941                            } else {
1942                                let b = ops::fact(&v, "observe").map_err(|e| e.at(value.span))?;
1943                                if b { (1.0, 0.0, 0.0) } else { (0.0, 0.0, 1.0) }
1944                            }
1945                        }
1946                        Some(d) => {
1947                            let v = each!(self, here, saved saved, self.eval(f, value, &w));
1948                            match each!(self, here, saved saved, self.direct_counts(f, d, &w)) {
1949                                Some(counts) if self.sampler.is_some() => {
1950                                    let x = match v {
1951                                        Value::Bool(_) => None,
1952                                        _ => v.as_f64(),
1953                                    };
1954                                    (x.map_or(0.0, |x| counts.pmf(x)), 0.0, 0.0)
1955                                }
1956                                _ => {
1957                                    if self.sampler.is_none() {
1958                                        let mut exact = self.observe_gaussian(f, &v, d, &mut w, span);
1959                                        if matches!(exact, Ok(None)) {
1960                                            exact = self.observe_numerically(f, &v, d, &mut w, span);
1961                                        }
1962                                        if let Some(ln) = each!(self, here, saved saved, exact) {
1963                                            w.weight = w.weight * Weight::from_ln(ln);
1964                                            if w.weight.is_zero() {
1965                                                self.last_ruling_out = Some(span);
1966                                            } else {
1967                                                out.push(w);
1968                                            }
1969                                            continue;
1970                                        }
1971                                    }
1972                                    let dist = self.eval(f, d, &w).map_err(|e| self.integration_hint(d, e));
1973                                    let dist = each!(self, here, saved saved, dist);
1974                                    if let (Value::Bool(b), Value::Event(event)) = (&v, &dist) {
1975                                        self.observe_event(event, *b, w, &mut out, span)?;
1976                                        continue;
1977                                    } else {
1978                                        if analytic::contains(&v) || analytic::contains(&dist) {
1979                                            return Err(analytic::unsupported("this likelihood observation").at(span));
1980                                        }
1981                                        self.densities |= is_density(&dist);
1982                                        match each!(
1983                                            self,
1984                                            here,
1985                                            saved saved,
1986                                            likelihood(&dist, &v, self.sampler.is_some()).map_err(|e| e.at(span))
1987                                        ) {
1988                                            Weighs::Probability(p, missing, other) => (p, missing, other),
1989                                            Weighs::LogDensity(ln) => {
1990                                                self.observe_density(f, ln, &mut w, span)?;
1991                                                if !w.weight.is_zero() {
1992                                                    out.push(w);
1993                                                }
1994                                                continue;
1995                                            }
1996                                        }
1997                                    }
1998                                }
1999                            }
2000                        }
2001                    };
2002                    if missing > 0.0 && self.sampler.is_none() {
2003                        self.unresolved += w.weight.scale(missing);
2004                    }
2005                    if ruled_out > 0.0 && self.sampler.is_none() {
2006                        self.lost += w.weight.scale(ruled_out);
2007                    }
2008                    if factor > 0.0 {
2009                        w.weight = w.weight.scale(factor);
2010                        out.push(w);
2011                    } else {
2012                        self.last_ruling_out = Some(span);
2013                    }
2014                }
2015                Ok(Flow::next(out))
2016            }
2017            StmtKind::Report { site, value, key } => {
2018                let mut failed = Vec::new();
2019                for (i, w) in worlds.iter().enumerate() {
2020                    let v = each!(self, At::new(f, stmt, w), world w, self.eval(f, value, w), { failed.push(i) });
2021                    // A report of a count law lists it.
2022                    let v = match v {
2023                        Value::Counts(_) => ops::listed(&v, &mut self.budget)
2024                            .map_err(|e| e.at(value.span))?
2025                            .into_owned(),
2026                        v => v,
2027                    };
2028                    let k = match key {
2029                        Some(k) => {
2030                            let k_value =
2031                                each!(self, At::new(f, stmt, w), world w, self.eval(f, k, w), { failed.push(i) });
2032                            if analytic::contains(&k_value) && !matches!(k_value, Value::Event(_)) {
2033                                return Err(analytic::unsupported("grouping by a continuous outcome").at(k.span));
2034                            }
2035                            if k_value.is_uncertain() {
2036                                return Err(RuntimeError::new(
2037                                    k.span,
2038                                    "a `by` key must be a plain value, not a distribution",
2039                                )
2040                                .with_help("draw a value first with `~`"));
2041                            }
2042                            k_value
2043                        }
2044                        None => Value::Unit,
2045                    };
2046                    // A distribution of outcomes, from `max(x, d6)` say, reports
2047                    // as their mixture, and a record or list of them field by
2048                    // field.
2049                    let (v, run) = if self.sampler.is_some() {
2050                        (self.sample_continuous(v), Some(w.run))
2051                    } else {
2052                        (v, None)
2053                    };
2054                    if let Value::Event(event) = &k {
2055                        // An event of a draw is a key with two values: each
2056                        // group has its part of the world.
2057                        let span = key.as_ref().map_or(span, |k| k.span);
2058                        for yes in [true, false] {
2059                            for (p, constraints) in self.event_parts(event, yes, &w.constraints, span)? {
2060                                if p <= 0.0 {
2061                                    continue;
2062                                }
2063                                let v = analytic::resolve(&v, &constraints, &mut self.budget)
2064                                    .map_err(|e| e.at(value.span))?;
2065                                let k = Value::Bool(yes);
2066                                self.same_units(w, value.span)?;
2067                                let sink = &self.sinks[*site as usize];
2068                                sink.validate_analytic(&k, &v).map_err(|e| e.at(value.span))?;
2069                                sink.validate_structure(&k, &v).map_err(|e| e.at(value.span))?;
2070                                self.sinks[*site as usize].add(k, &v, w.weight.scale(p), run);
2071                            }
2072                        }
2073                        continue;
2074                    }
2075                    if run.is_none() {
2076                        self.same_units(w, value.span)?;
2077                        self.sinks[*site as usize]
2078                            .validate_structure(&k, &v)
2079                            .map_err(|e| e.at(value.span))?;
2080                    }
2081                    self.sinks[*site as usize]
2082                        .validate_analytic(&k, &v)
2083                        .map_err(|e| e.at(value.span))?;
2084                    self.sinks[*site as usize].add(k, &v, w.weight, run);
2085                }
2086                let mut worlds = worlds;
2087                drop_failed(&mut worlds, &failed);
2088                Ok(Flow::next(worlds))
2089            }
2090            StmtKind::Fail { message } => Err(RuntimeError::new(span, message.clone())),
2091            StmtKind::Try { body, catches } => {
2092                self.handlers.push((self.depth, catches.as_slice()));
2093                let flow = self.exec_block(f, body, worlds);
2094                self.handlers.pop();
2095                let mut flow = flow?;
2096                // Each faulted world goes on in the first catch that takes
2097                // its fault; the others are for a `try` around this one.
2098                let mut caught: Vec<Vec<World>> = vec![Vec::new(); catches.len()];
2099                for (w, e) in std::mem::take(&mut flow.faulted) {
2100                    match catches.iter().position(|c| c.catches(e.fault)) {
2101                        Some(i) => caught[i].push(w),
2102                        None => flow.faulted.push((w, e)),
2103                    }
2104                }
2105                for (c, worlds) in catches.iter().zip(caught) {
2106                    if !worlds.is_empty() {
2107                        flow.join(self.exec_block(f, &c.body, worlds)?);
2108                    }
2109                }
2110                flow.next = self.merge(flow.next, stmt.id);
2111                Ok(flow)
2112            }
2113            StmtKind::Check { slot, ty } => {
2114                for w in &mut worlds {
2115                    // A delayed variable is drawn only if some of its values
2116                    // could fail the check.
2117                    if let Value::Delayed(_) = &w.slots[*slot as usize] {
2118                        let always = *ty == TypeSpec::Float;
2119                        if always {
2120                            continue;
2121                        }
2122                        self.draw_delayed(w, *slot);
2123                    }
2124                    self.check_slot(f, *slot, ty, w, span)?;
2125                }
2126                Ok(Flow::next(worlds))
2127            }
2128        }
2129    }
2130
2131    /// `return`: each world's value, with the restrictions its caller can
2132    /// see. (Out of `exec_stmt_kind`, whose frame every statement pays for.)
2133    #[inline(never)]
2134    fn exec_return(&mut self, f: FnId, stmt: &'p Stmt, value: &'p Expr, worlds: Vec<World>) -> Result<Flow> {
2135        let mut flow = Flow::default();
2136        let simulate = self.prog.functions[f as usize].kind == FnKind::Simulate;
2137        for w in worlds {
2138            let v = each!(self, At::new(f, stmt, &w), world w, self.eval(f, value, &w));
2139            if simulate {
2140                self.simulate_returns(&w, stmt.span)?;
2141            }
2142            let mut constraints = w.constraints;
2143            if constraints.keys().any(|id| !w.inherited.contains(id)) {
2144                Arc::make_mut(&mut constraints).retain(|id, _| w.inherited.contains(id));
2145            }
2146            flow.returned.push((v, w.weight, constraints));
2147        }
2148        Ok(flow)
2149    }
2150
2151    fn exec_loop(
2152        &mut self,
2153        f: FnId,
2154        stmt: &'p Stmt,
2155        body: &'p Block,
2156        bounded: bool,
2157        worlds: Vec<World>,
2158    ) -> Result<Flow> {
2159        let live = self.live;
2160        let span = stmt.span;
2161        let entered = total_weight(&worlds);
2162        let cutoff = entered.scale(self.config.epsilon);
2163        let mut inside = worlds;
2164        let mut out = Flow::default();
2165        let mut iterations: u64 = 0;
2166        // The states met at the loop's start in earlier iterations, while it
2167        // may be solved. Once one comes back, the loop cycles, and it's
2168        // solved as a Markov chain from there (section 10).
2169        let mut met = self.may_solve(stmt, bounded).then(FxHashSet::<u64>::default);
2170        while !inside.is_empty() {
2171            let cycles = match &mut met {
2172                Some(met) => {
2173                    // A loop's rewards don't make its states new.
2174                    let rewards = self.rewards.get(&stmt.id).map_or(&[][..], Vec::as_slice);
2175                    let mut head = live_slots(&live.loop_head[stmt.id as usize], inside[0].slots.len());
2176                    head.retain(|&i| !rewards.contains(&(i as SlotId)));
2177                    let hashes: Vec<u64> = inside.iter().map(|w| state_hash(w, &head)).collect();
2178                    let again = hashes.iter().any(|h| met.contains(h));
2179                    met.extend(hashes);
2180                    again
2181                }
2182                None => false,
2183            };
2184            if cycles {
2185                met = None;
2186                if let Some(solved) = self.solve_loop(f, stmt, body, &inside, cutoff, &mut iterations)? {
2187                    out.join(solved);
2188                    break;
2189                }
2190            }
2191            let mass = total_weight(&inside);
2192            if !bounded && mass < cutoff && self.sampler.is_none() {
2193                self.unresolved += mass;
2194                break;
2195            }
2196            iterations += 1;
2197            if iterations > self.config.max_iterations {
2198                return Err(RuntimeError::limit(
2199                    span,
2200                    format!("this loop ran {} times without finishing", self.config.max_iterations),
2201                )
2202                .with_note(format!(
2203                    "worlds still inside the loop weigh {} of what entered it",
2204                    fmt_prob(mass.ratio(entered))
2205                ))
2206                .with_help("check that every world can leave the loop"));
2207            }
2208            let mut flow = self.exec_block(f, body, inside)?;
2209            clear_dead(&mut flow.broke, &live.after[stmt.id as usize]);
2210            clear_dead(&mut flow.continued, &live.loop_head[stmt.id as usize]);
2211            out.next.extend(flow.broke);
2212            out.returned.extend(flow.returned);
2213            out.faulted.extend(flow.faulted);
2214            let mut again = flow.next;
2215            again.extend(flow.continued);
2216            inside = merge(again, &live.loop_head[stmt.id as usize], self.merging());
2217        }
2218        out.next = self.merge(out.next, stmt.id);
2219        Ok(out)
2220    }
2221
2222    /// Whether a loop may be solved as a Markov chain: an unbounded loop
2223    /// whose body doesn't report or print, when enumerating, unless its
2224    /// chain was already found too large.
2225    fn may_solve(&self, stmt: &Stmt, bounded: bool) -> bool {
2226        !bounded
2227            && self.config.solving
2228            && self.sampler.is_none()
2229            && self.solvable[stmt.id as usize]
2230            && !self.too_large.contains(&stmt.id)
2231    }
2232
2233    /// Solve a loop as an absorbing Markov chain, from the worlds `inside`
2234    /// at its start (section 10). Its states are the worlds at its start,
2235    /// told apart by the variables read later. Running the body once from
2236    /// each state reachable from `inside` gives the chance of going on to
2237    /// each state, and the worlds that leave. The expected number of visits
2238    /// to each state then says how much of each leaves. `None` if the chain
2239    /// is too large, and the loop should be unrolled instead.
2240    fn solve_loop(
2241        &mut self,
2242        f: FnId,
2243        stmt: &'p Stmt,
2244        body: &'p Block,
2245        inside: &[World],
2246        cutoff: Weight,
2247        iterations: &mut u64,
2248    ) -> Result<Option<Flow>> {
2249        // The chain's transitions are probabilities: a density isn't one.
2250        if inside
2251            .iter()
2252            .any(|w| w.densities > 0 || !w.constraints.is_empty() || w.slots.iter().any(analytic::contains))
2253        {
2254            self.too_large.insert(stmt.id);
2255            return Ok(None);
2256        }
2257        let head_set = &self.live.loop_head[stmt.id as usize];
2258        let after = &self.live.after[stmt.id as usize];
2259        let n = inside[0].slots.len();
2260        let rewards: Vec<usize> = self
2261            .rewards
2262            .get(&stmt.id)
2263            .map_or(&[][..], Vec::as_slice)
2264            .iter()
2265            .map(|&r| r as usize)
2266            .collect();
2267        let mut head = live_slots(head_set, n);
2268        head.retain(|i| !rewards.contains(i));
2269        let dead: Vec<SlotId> = head_set.iter_missing(n).collect();
2270        if rewards.len() > MAX_REWARDS {
2271            self.too_large.insert(stmt.id);
2272            return Ok(None);
2273        }
2274        // A world's rewards, where they're plain numbers: whole numbers or
2275        // floats, by their bits, and which are floats.
2276        let reward_of = |slots: &[Value]| -> Option<Raw> {
2277            let (mut bits, mut floats) = ([0u64; MAX_REWARDS], 0u8);
2278            for (i, &r) in rewards.iter().enumerate() {
2279                bits[i] = match &slots[r] {
2280                    Value::Int(n) => n.to_i64()? as u64,
2281                    Value::Float(x) => {
2282                        floats |= 1 << i;
2283                        x.to_bits()
2284                    }
2285                    _ => return None,
2286                };
2287            }
2288            Some((bits, floats))
2289        };
2290        // Each state: its world, with dead slots cleared and rewards at 0,
2291        // found by its other live slots.
2292        let mut states: Vec<Vec<Value>> = Vec::new();
2293        let mut index: FxHashMap<Vec<Value>, usize> = FxHashMap::default();
2294        let mut intern = |mut slots: Vec<Value>, states: &mut Vec<Vec<Value>>| -> usize {
2295            for &d in &dead {
2296                slots[d as usize] = Value::Dead;
2297            }
2298            for &r in &rewards {
2299                slots[r] = Value::Int(0.into());
2300            }
2301            let key: Vec<Value> = head.iter().map(|&i| slots[i].clone()).collect();
2302            *index.entry(key).or_insert_with(|| {
2303                states.push(slots);
2304                states.len() - 1
2305            })
2306        };
2307        let total = total_weight(inside);
2308        let mut start: Vec<f64> = Vec::new();
2309        // With rewards: each state and reward the worlds start with.
2310        let mut starts: Vec<((usize, Raw), f64)> = Vec::new();
2311        for w in inside {
2312            let reward = if rewards.is_empty() {
2313                ([0; MAX_REWARDS], 0)
2314            } else {
2315                match reward_of(&w.slots) {
2316                    Some(r) => r,
2317                    None => return Ok(None),
2318                }
2319            };
2320            let i = intern(w.slots.clone(), &mut states);
2321            if start.len() <= i {
2322                start.resize(i + 1, 0.0);
2323            }
2324            start[i] += w.weight.ratio(total);
2325            starts.push(((i, reward), w.weight.ratio(total)));
2326        }
2327        // With rewards: each state's steps, to each state with how much the
2328        // rewards grow.
2329        let mut moves: Vec<Vec<(u32, Raw, f64)>> = Vec::new();
2330        let mut chain = Chain::default();
2331        let mut exits: Vec<Vec<World>> = Vec::new();
2332        let mut returns: Vec<Vec<Returned>> = Vec::new();
2333        let mut unresolved: Vec<Weight> = Vec::new();
2334        let mut failed: Vec<Failures> = Vec::new();
2335        let mut faulted: Vec<Vec<(World, RuntimeError)>> = Vec::new();
2336        // A body that uses a call's result so far can't be solved as a chain:
2337        // that result changes from round to round.
2338        let saved_pending = std::mem::take(&mut self.pending);
2339        let mut k = 0;
2340        while k < states.len() {
2341            if !self.pending.is_empty() {
2342                self.pending = saved_pending;
2343                return Ok(None);
2344            }
2345            if states.len() > self.config.max_chain_states {
2346                self.pending = saved_pending;
2347                self.too_large.insert(stmt.id);
2348                return Ok(None);
2349            }
2350            let world = World {
2351                slots: states[k].clone(),
2352                constraints: Default::default(),
2353                inherited: Default::default(),
2354                weight: Weight::ONE,
2355                run: 0,
2356                densities: 0,
2357            };
2358            let saved_unresolved = std::mem::replace(&mut self.unresolved, Weight::ZERO);
2359            let saved_lost = std::mem::replace(&mut self.lost, Weight::ZERO);
2360            let saved_failures = std::mem::take(&mut self.failures);
2361            let flow = self.exec_block(f, body, vec![world]);
2362            let left = std::mem::replace(&mut self.unresolved, saved_unresolved);
2363            let lost = std::mem::replace(&mut self.lost, saved_lost);
2364            let failures = std::mem::replace(&mut self.failures, saved_failures);
2365            let mut flow = flow?;
2366            if flow
2367                .next
2368                .iter()
2369                .chain(&flow.continued)
2370                .chain(&flow.broke)
2371                .chain(flow.faulted.iter().map(|(w, _)| w))
2372                .any(|w| w.densities > 0 || !w.constraints.is_empty() || w.slots.iter().any(analytic::contains))
2373                || flow
2374                    .returned
2375                    .iter()
2376                    .any(|(v, _, c)| !c.is_empty() || analytic::contains(v))
2377            {
2378                self.pending = saved_pending;
2379                self.too_large.insert(stmt.id);
2380                return Ok(None);
2381            }
2382            clear_dead(&mut flow.broke, after);
2383            // Leaving: by `break` or `return`, unresolved, ruled out,
2384            // failed, or faulted to a catch outside the loop.
2385            let mut leave = left + lost + failures.weight;
2386            let faults = std::mem::take(&mut flow.faulted);
2387            for (w, _) in &faults {
2388                leave += w.weight;
2389            }
2390            for w in &flow.broke {
2391                leave += w.weight;
2392            }
2393            for (_, w, _) in &flow.returned {
2394                leave += *w;
2395            }
2396            let mut next: FxHashMap<usize, f64> = FxHashMap::default();
2397            let mut steps: Vec<(u32, Raw, f64)> = Vec::new();
2398            for w in flow.next.into_iter().chain(flow.continued) {
2399                let p = w.weight.to_f64();
2400                if p == 0.0 {
2401                    // Too small for the chain's arithmetic.
2402                    self.too_large.insert(stmt.id);
2403                    self.pending = saved_pending;
2404                    return Ok(None);
2405                }
2406                let grows = if rewards.is_empty() {
2407                    ([0; MAX_REWARDS], 0)
2408                } else {
2409                    match reward_of(&w.slots) {
2410                        Some(r) => r,
2411                        None => {
2412                            self.too_large.insert(stmt.id);
2413                            self.pending = saved_pending;
2414                            return Ok(None);
2415                        }
2416                    }
2417                };
2418                let j = intern(w.slots, &mut states);
2419                *next.entry(j).or_insert(0.0) += p;
2420                if !rewards.is_empty() {
2421                    steps.push((j as u32, grows, p));
2422                }
2423            }
2424            moves.push(steps);
2425            let mut next: Vec<(usize, f64)> = next.into_iter().collect();
2426            next.sort_by_key(|&(j, _)| j);
2427            chain.next.push(next);
2428            chain.leave.push(leave.to_f64());
2429            exits.push(flow.broke);
2430            returns.push(flow.returned);
2431            unresolved.push(left);
2432            failed.push(failures);
2433            faulted.push(faults);
2434            k += 1;
2435        }
2436        let waiting = !self.pending.is_empty();
2437        self.pending = saved_pending;
2438        if waiting {
2439            return Ok(None);
2440        }
2441        start.resize(states.len(), 0.0);
2442        // Rewards are iterated, which needs no elimination: only states that
2443        // can't be left are found first.
2444        if !rewards.is_empty() {
2445            if let Some(s) = chain.stuck() {
2446                return Err(self.never_leaves(f, stmt, &states[s], &head));
2447            }
2448            drop(index);
2449            drop(states);
2450            let chain = Rewarded {
2451                rewards: &rewards,
2452                moves: &moves,
2453                exits,
2454                returns,
2455                unresolved,
2456                failed,
2457                faulted,
2458            };
2459            let solved = self.iterate_rewards(stmt, chain, starts, total, cutoff, iterations)?;
2460            if solved.is_none() {
2461                self.too_large.insert(stmt.id);
2462            }
2463            return Ok(solved);
2464        }
2465        let mut steps = MAX_ELIMINATION;
2466        let solution = chain.visits(&start, &mut steps);
2467        self.spend(MAX_ELIMINATION - steps, stmt.span)?;
2468        let visits = match solution {
2469            Solution::Visits(v) => v,
2470            Solution::TooBig => {
2471                self.too_large.insert(stmt.id);
2472                return Ok(None);
2473            }
2474            Solution::Stuck(s) => return Err(self.never_leaves(f, stmt, &states[s], &head)),
2475        };
2476        self.stats.solved_loops += 1;
2477        self.stats.chain_states += states.len() as u64;
2478        let mut flow = Flow::default();
2479        let mut left = Weight::ZERO;
2480        for (k, v) in visits.into_iter().enumerate() {
2481            if v == 0.0 {
2482                continue;
2483            }
2484            let times = total.scale(v);
2485            for mut w in std::mem::take(&mut exits[k]) {
2486                w.weight = w.weight * times;
2487                flow.next.push(w);
2488            }
2489            for (value, w, c) in std::mem::take(&mut returns[k]) {
2490                flow.returned.push((value, w * times, c));
2491            }
2492            left += unresolved[k] * times;
2493            self.failures.absorb(&failed[k], times, None, false);
2494            for (mut w, e) in std::mem::take(&mut faulted[k]) {
2495                w.weight = w.weight * times;
2496                flow.faulted.push((w, e));
2497            }
2498        }
2499        self.unresolved += left;
2500        Ok(Some(flow))
2501    }
2502
2503    /// A loop solved as a chain whose steps carry rewards: the law of each
2504    /// state and reward, step after step, from the worlds that start it, as
2505    /// unrolling would follow them, but without running the body again.
2506    /// Like unrolling, it stops once what's still inside weighs less than
2507    /// `cutoff`, which is then unresolved, or after the most iterations.
2508    /// `None` if a reward doesn't fit the numbers it's followed in.
2509    fn iterate_rewards(
2510        &mut self,
2511        stmt: &Stmt,
2512        chain: Rewarded,
2513        starts: Vec<((usize, Raw), f64)>,
2514        total: Weight,
2515        cutoff: Weight,
2516        iterations: &mut u64,
2517    ) -> Result<Option<Flow>> {
2518        let span = stmt.span;
2519        let count = chain.rewards.len();
2520        // Each reward is followed as a whole number while every value it
2521        // takes is an int, and as a float once one is a float.
2522        let floats = starts
2523            .iter()
2524            .map(|((_, (_, f)), _)| *f)
2525            .chain(chain.moves.iter().flatten().map(|(_, (_, f), _)| *f))
2526            .fold(0u8, |a, f| a | f);
2527        let float: [bool; MAX_REWARDS] = std::array::from_fn(|i| floats & (1 << i) != 0);
2528        let compact = |(bits, floats): &Raw| -> Option<Reward> {
2529            let mut out = *bits;
2530            for i in 0..count {
2531                if float[i] && floats & (1 << i) == 0 {
2532                    let n = bits[i] as i64;
2533                    if n.unsigned_abs() > 1 << 53 {
2534                        return None;
2535                    }
2536                    out[i] = (n as f64).to_bits();
2537                }
2538            }
2539            Some(out)
2540        };
2541        let plus = |a: &Reward, b: &Reward| -> Option<Reward> {
2542            let mut out = [0u64; MAX_REWARDS];
2543            for i in 0..count {
2544                out[i] = if float[i] {
2545                    (f64::from_bits(a[i]) + f64::from_bits(b[i])).to_bits()
2546                } else {
2547                    (a[i] as i64).checked_add(b[i] as i64)? as u64
2548                };
2549            }
2550            Some(out)
2551        };
2552        let value = |r: &Reward, i: usize| -> Value {
2553            if float[i] {
2554                Value::Float(f64::from_bits(r[i]))
2555            } else {
2556                Value::Int((r[i] as i64).into())
2557            }
2558        };
2559        let mut moves: Vec<Vec<(u32, Reward, f64)>> = Vec::with_capacity(chain.moves.len());
2560        for steps in chain.moves {
2561            let mut out = Vec::with_capacity(steps.len());
2562            for (j, d, p) in steps {
2563                let Some(d) = compact(d) else { return Ok(None) };
2564                out.push((*j, d, *p));
2565            }
2566            moves.push(out);
2567        }
2568        let mut current: FxHashMap<(u32, Reward), f64> = FxHashMap::default();
2569        for ((s, r), p) in &starts {
2570            let Some(r) = compact(r) else { return Ok(None) };
2571            *current.entry((*s as u32, r)).or_insert(0.0) += p;
2572        }
2573        // How much weight ran the body from each state; leaves by each of
2574        // the distinct worlds that leave, with each reward, as unrolling's
2575        // merging would add it up; and faulted, by state and reward.
2576        let mut visits = vec![0.0; moves.len()];
2577        let mut templates: Vec<&World> = Vec::new();
2578        let mut template_index: FxHashMap<&[Value], u32> = FxHashMap::default();
2579        let exits: Vec<Vec<(u32, f64)>> = chain
2580            .exits
2581            .iter()
2582            .map(|worlds| {
2583                worlds
2584                    .iter()
2585                    .map(|w| {
2586                        let t = *template_index.entry(&w.slots[..]).or_insert_with(|| {
2587                            templates.push(w);
2588                            (templates.len() - 1) as u32
2589                        });
2590                        (t, w.weight.to_f64())
2591                    })
2592                    .collect()
2593            })
2594            .collect();
2595        drop(template_index);
2596        let mut leaving: FxHashMap<(u32, Reward), f64> = FxHashMap::default();
2597        let mut ran: FxHashMap<(u32, Reward), f64> = FxHashMap::default();
2598        let mut next: FxHashMap<(u32, Reward), f64> = FxHashMap::default();
2599        // When every step adds the same, and every world starts with the
2600        // same reward, as a counter of rounds does, all the worlds have the
2601        // same reward at each step: the weights are a vector by state.
2602        let mut increments = moves.iter().flatten().map(|(_, d, _)| d);
2603        let same = increments.next().copied().filter(|d| increments.all(|e| e == d));
2604        let mut rewards = current.keys().map(|(_, r)| r);
2605        let start = rewards.next().copied().filter(|r| rewards.all(|q| q == r));
2606        if let (Some(d), Some(mut reward)) = (same, start) {
2607            let n = moves.len();
2608            let mut weights = vec![0.0; n];
2609            for (&(s, _), &m) in &current {
2610                weights[s as usize] += m;
2611            }
2612            let mut after = vec![0.0; n];
2613            loop {
2614                let mass: f64 = weights.iter().sum();
2615                if mass == 0.0 {
2616                    break;
2617                }
2618                if total.scale(mass) < cutoff {
2619                    self.unresolved += total.scale(mass);
2620                    break;
2621                }
2622                *iterations += 1;
2623                if *iterations > self.config.max_iterations {
2624                    return Err(RuntimeError::limit(
2625                        span,
2626                        format!("this loop ran {} times without finishing", self.config.max_iterations),
2627                    )
2628                    .with_note(format!(
2629                        "worlds still inside the loop weigh {} of what entered it",
2630                        fmt_prob(mass)
2631                    ))
2632                    .with_help("check that every world can leave the loop"));
2633                }
2634                self.spend(n as u64, span)?;
2635                for (s, &m) in weights.iter().enumerate() {
2636                    if m == 0.0 {
2637                        continue;
2638                    }
2639                    visits[s] += m;
2640                    for &(j, _, p) in &moves[s] {
2641                        after[j as usize] += m * p;
2642                    }
2643                    for &(t, p) in &exits[s] {
2644                        *leaving.entry((t, reward)).or_insert(0.0) += m * p;
2645                    }
2646                    if !chain.faulted[s].is_empty() {
2647                        *ran.entry((s as u32, reward)).or_insert(0.0) += m;
2648                    }
2649                }
2650                std::mem::swap(&mut weights, &mut after);
2651                after.iter_mut().for_each(|w| *w = 0.0);
2652                let Some(r) = plus(&reward, &d) else { return Ok(None) };
2653                reward = r;
2654            }
2655            current.clear();
2656        }
2657        while !current.is_empty() {
2658            let mass: f64 = current.values().sum();
2659            if total.scale(mass) < cutoff {
2660                self.unresolved += total.scale(mass);
2661                break;
2662            }
2663            *iterations += 1;
2664            if *iterations > self.config.max_iterations {
2665                return Err(RuntimeError::limit(
2666                    span,
2667                    format!("this loop ran {} times without finishing", self.config.max_iterations),
2668                )
2669                .with_note(format!(
2670                    "worlds still inside the loop weigh {} of what entered it",
2671                    fmt_prob(mass)
2672                ))
2673                .with_help("check that every world can leave the loop"));
2674            }
2675            self.spend(current.len() as u64, span)?;
2676            for (&(s, r), &m) in &current {
2677                visits[s as usize] += m;
2678                for (j, d, p) in &moves[s as usize] {
2679                    let Some(r) = plus(&r, d) else { return Ok(None) };
2680                    *next.entry((*j, r)).or_insert(0.0) += m * p;
2681                }
2682                for &(t, p) in &exits[s as usize] {
2683                    *leaving.entry((t, r)).or_insert(0.0) += m * p;
2684                }
2685                if !chain.faulted[s as usize].is_empty() {
2686                    *ran.entry((s, r)).or_insert(0.0) += m;
2687                }
2688            }
2689            self.check_worlds(next.len(), span)?;
2690            std::mem::swap(&mut current, &mut next);
2691            next.clear();
2692        }
2693        self.stats.solved_loops += 1;
2694        self.stats.chain_states += moves.len() as u64;
2695        // A world leaving from a state with a reward: what it gained in
2696        // its last step, added to the reward.
2697        let add = |a: Value, b: &Value, budget: &mut crate::dist::Budget| -> Result<Value> {
2698            ops::binary(probl_syntax::ast::BinOp::Add, &a, b, budget).map_err(|e| e.at(span))
2699        };
2700        let mut flow = Flow::default();
2701        let rewarded = |w: &World, r: &Reward, budget: &mut crate::dist::Budget| -> Result<World> {
2702            let mut w = w.clone();
2703            for (i, &slot) in chain.rewards.iter().enumerate() {
2704                if !matches!(w.slots[slot], Value::Dead) {
2705                    w.slots[slot] = add(value(r, i), &w.slots[slot], budget)?;
2706                }
2707            }
2708            Ok(w)
2709        };
2710        let mut leaving: Vec<((u32, Reward), f64)> = leaving.into_iter().collect();
2711        leaving.sort_by_key(|a| a.0);
2712        for ((t, r), m) in leaving {
2713            let mut w = rewarded(templates[t as usize], &r, &mut self.budget)?;
2714            w.weight = total.scale(m);
2715            flow.next.push(w);
2716        }
2717        let mut ran: Vec<((u32, Reward), f64)> = ran.into_iter().collect();
2718        ran.sort_by_key(|a| a.0);
2719        for ((s, r), m) in ran {
2720            let times = total.scale(m);
2721            for (w, e) in &chain.faulted[s as usize] {
2722                let mut w = rewarded(w, &r, &mut self.budget)?;
2723                w.weight = w.weight * times;
2724                flow.faulted.push((w, e.clone()));
2725            }
2726        }
2727        let mut left = Weight::ZERO;
2728        for (s, v) in visits.into_iter().enumerate() {
2729            if v == 0.0 {
2730                continue;
2731            }
2732            let times = total.scale(v);
2733            for (value, w, c) in &chain.returns[s] {
2734                flow.returned.push((value.clone(), *w * times, c.clone()));
2735            }
2736            left += chain.unresolved[s] * times;
2737            self.failures.absorb(&chain.failed[s], times, None, false);
2738        }
2739        self.unresolved += left;
2740        Ok(Some(flow))
2741    }
2742
2743    /// The error for a loop that some worlds can never leave, with one of
2744    /// them.
2745    fn never_leaves(&self, f: FnId, stmt: &Stmt, slots: &[Value], head: &[usize]) -> RuntimeError {
2746        let names = &self.prog.functions[f as usize].slots;
2747        let parts: Vec<String> = head
2748            .iter()
2749            .filter(|&&i| names[i].name != TEMP)
2750            .map(|&i| format!("`{}` is {:?}", names[i].name, slots[i]))
2751            .collect();
2752        let err = RuntimeError::new(stmt.span, "some worlds can never leave this loop")
2753            .with_help("check that every world can leave the loop");
2754        if parts.is_empty() {
2755            err
2756        } else {
2757            err.with_note(format!("for example, the worlds where {}", parts.join(" and ")))
2758        }
2759    }
2760
2761    /// Store each possible value of `d` into `place`, one world per outcome.
2762    fn split_by(&mut self, f: FnId, place: &Place, d: Value, w: World, span: Span, out: &mut Vec<World>) -> Result<()> {
2763        match d {
2764            Value::Dist(_) | Value::Continuous(_) | Value::Counts(_) | Value::Joint(_) if self.sampler.is_some() => {
2765                let v = self.sample(&d);
2766                let mut w = w;
2767                self.assign(f, place, v, &mut w, span)?;
2768                out.push(w);
2769            }
2770            Value::Continuous(family) => {
2771                self.next_latent += 1;
2772                let v = Analytic::new(self.next_latent, *family)
2773                    .value()
2774                    .map_err(|e| e.at(span))?;
2775                let mut w = w;
2776                self.assign(f, place, v, &mut w, span)?;
2777                out.push(w);
2778            }
2779            Value::Dist(dist) => {
2780                self.unresolved += w.weight.scale(dist.missing);
2781                let last = dist.outcomes.len().saturating_sub(1);
2782                let mut w = Some(w);
2783                for (i, (v, p)) in dist.outcomes.iter().enumerate() {
2784                    let base = if i == last {
2785                        w.take().unwrap()
2786                    } else {
2787                        w.clone().unwrap()
2788                    };
2789                    let mut nw = base.scaled(*p);
2790                    if matches!(v, Value::Continuous(_) | Value::Joint(_)) {
2791                        self.split_by(f, place, v.clone(), nw, span, out)?;
2792                    } else {
2793                        self.assign(f, place, v.clone(), &mut nw, span)?;
2794                        out.push(nw);
2795                    }
2796                }
2797            }
2798            Value::Prob(p) => {
2799                return self.split_by(f, place, Dist::bernoulli(p).into_value(), w, span, out);
2800            }
2801            Value::Joint(dist) => self.draw_joint(f, place, &dist, w, span, out)?,
2802            // Enumerating needs its outcomes.
2803            Value::Counts(_) => {
2804                let d = ops::listed(&d, &mut self.budget).map_err(|e| e.at(span))?.into_owned();
2805                return self.split_by(f, place, d, w, span, out);
2806            }
2807            other => {
2808                let mut w = w;
2809                self.assign(f, place, other, &mut w, span)?;
2810                out.push(w);
2811            }
2812        }
2813        Ok(())
2814    }
2815
2816    /// A draw from a distribution whose outcomes own their draws: a world
2817    /// for each outcome, with its draws fresh there.
2818    #[inline(never)]
2819    fn draw_joint(
2820        &mut self,
2821        f: FnId,
2822        place: &Place,
2823        dist: &Dist,
2824        w: World,
2825        span: Span,
2826        out: &mut Vec<World>,
2827    ) -> Result<()> {
2828        self.unresolved += w.weight.scale(dist.missing);
2829        for (v, p) in &dist.outcomes {
2830            let v = crate::joint::fresh(v, &mut self.next_latent);
2831            let mut nw = w.clone().scaled(*p);
2832            self.assign(f, place, v, &mut nw, span)?;
2833            out.push(nw);
2834        }
2835        Ok(())
2836    }
2837
2838    fn check_arity(&self, c: &Closure, given: usize, span: Span) -> Result<()> {
2839        let expected = self.prog.functions[c.func as usize].n_params as usize;
2840        if expected != given {
2841            return Err(RuntimeError::new(
2842                span,
2843                format!("this function takes {expected} argument(s), but {given} were given"),
2844            ));
2845        }
2846        Ok(())
2847    }
2848
2849    /// Convert values at declared type boundaries, recursively through
2850    /// containers. Numeric probability conversions are checked, never clamped.
2851    fn coerce(&mut self, v: Value, ty: &TypeSpec, span: Span) -> Result<Value> {
2852        self.coerce_at(v, ty, span, 0)
2853    }
2854
2855    fn coerce_at(&mut self, v: Value, ty: &TypeSpec, span: Span, depth: usize) -> Result<Value> {
2856        self.budget.work(1).map_err(|e| e.at(span))?;
2857        if depth > 64 {
2858            return Err(RuntimeError::new(
2859                span,
2860                "type conversion nesting exceeds the limit of 64",
2861            ));
2862        }
2863        Ok(match (v, ty) {
2864            (Value::Analytic(_) | Value::Event(_), TypeSpec::Prob) => {
2865                return Err(analytic::unsupported("converting this outcome to prob").at(span));
2866            }
2867            (v @ Value::Float(_), TypeSpec::Int) => Value::Int(
2868                ops::integer(&v, "int conversion", &mut self.budget)
2869                    .map_err(|e| e.at(span))?
2870                    .into_owned(),
2871            ),
2872            // An outcome like `floor(x)` is an int; another is never rounded
2873            // into one.
2874            (Value::Analytic(a), TypeSpec::Int) if a.int => Value::Analytic(a),
2875            (Value::Analytic(_), TypeSpec::Int) => {
2876                return Err(analytic::unsupported("converting this outcome to int").at(span));
2877            }
2878            (Value::Counts(c), TypeSpec::Dist(t)) if **t == TypeSpec::Int => Value::Counts(c),
2879            (Value::Prob(p), TypeSpec::Float) => Value::Float(p),
2880            // A declared type is a contract: failing it isn't a fault of the
2881            // world's values, even in partial mode.
2882            (v @ (Value::Int(_) | Value::Float(_)), TypeSpec::Prob) => {
2883                ops::make_prob(&v).map_err(|e| OpError { fault: None, ..e }.at(span))?
2884            }
2885            (Value::List(xs), TypeSpec::List(t)) => {
2886                self.budget.collection(xs.len() as u128).map_err(|e| e.at(span))?;
2887                let mut out = Vec::with_capacity(xs.len());
2888                for x in xs.iter() {
2889                    out.push(self.coerce_at(x.clone(), t, span, depth + 1)?);
2890                }
2891                Value::list(out)
2892            }
2893            (Value::Map(xs), TypeSpec::Map(kt, vt)) => {
2894                let mut out = BTreeMap::new();
2895                for (k, v) in xs.iter() {
2896                    let key = self.coerce_at(k.clone(), kt, span, depth + 1)?;
2897                    let value = self.coerce_at(v.clone(), vt, span, depth + 1)?;
2898                    if out.insert(key, value).is_some() {
2899                        return Err(RuntimeError::new(span, "map key collision during type conversion"));
2900                    }
2901                }
2902                Value::map(out)
2903            }
2904            (Value::Bag(xs), TypeSpec::Bag(t)) => {
2905                let mut out = BTreeMap::<Value, u64>::new();
2906                for (x, n) in xs.iter() {
2907                    let key = self.coerce_at(x.clone(), t, span, depth + 1)?;
2908                    let count = out.entry(key).or_default();
2909                    *count = count
2910                        .checked_add(*n)
2911                        .ok_or_else(|| RuntimeError::new(span, "bag count overflow during type conversion"))?;
2912                }
2913                Value::multiset(crate::value::Multiset::new(out))
2914            }
2915            (Value::Dist(d), TypeSpec::Dist(t)) => {
2916                let mut out = Vec::with_capacity(d.outcomes.len());
2917                for (x, p) in &d.outcomes {
2918                    out.push((self.coerce_at(x.clone(), t, span, depth + 1)?, *p));
2919                }
2920                ops::combine(out, d.missing, &mut self.budget).map_err(|e| e.at(span))?
2921            }
2922            (Value::Joint(d), TypeSpec::Dist(t)) => {
2923                let mut out = Vec::with_capacity(d.outcomes.len());
2924                for (x, p) in &d.outcomes {
2925                    out.push((self.coerce_at(x.clone(), t, span, depth + 1)?, *p));
2926                }
2927                crate::joint::of(out, d.missing, &mut self.budget).map_err(|e| e.at(span))?
2928            }
2929            (Value::Record(r), TypeSpec::Record(t)) => {
2930                let types: Vec<_> = self.prog.records[*t as usize]
2931                    .fields
2932                    .iter()
2933                    .map(|f| (f.name.clone(), f.ty.clone()))
2934                    .collect();
2935                let mut fields = Vec::with_capacity(r.fields.len());
2936                for (name, value) in &r.fields {
2937                    let value = match types.iter().find(|(n, _)| n == &**name) {
2938                        Some((_, t)) => self.coerce_at(value.clone(), t, span, depth + 1)?,
2939                        None => value.clone(),
2940                    };
2941                    fields.push((name.clone(), value));
2942                }
2943                ops::make_record(r.ty.clone(), fields)
2944            }
2945            (Value::Record(r), TypeSpec::AnonRecord(types)) => {
2946                let mut fields = Vec::with_capacity(r.fields.len());
2947                for (name, value) in &r.fields {
2948                    let value = match types.iter().find(|(n, _)| n == &**name) {
2949                        Some((_, t)) => self.coerce_at(value.clone(), t, span, depth + 1)?,
2950                        None => value.clone(),
2951                    };
2952                    fields.push((name.clone(), value));
2953                }
2954                ops::make_record(r.ty.clone(), fields)
2955            }
2956            (v, _) => v,
2957        })
2958    }
2959
2960    /// Does `v` have the declared type?
2961    fn conforms(&self, v: &Value, ty: &TypeSpec) -> bool {
2962        match (ty, v) {
2963            (TypeSpec::Int, Value::Int(_)) => true,
2964            (TypeSpec::Float, Value::Float(_) | Value::Int(_) | Value::Analytic(_)) => true,
2965            (TypeSpec::Complex, Value::Complex(_)) => true,
2966            (TypeSpec::Prob, Value::Prob(_)) => true,
2967            (TypeSpec::Bool, Value::Bool(_) | Value::Event(_)) => true,
2968            (TypeSpec::Str, Value::Str(_)) => true,
2969            (TypeSpec::Date, Value::Date(_)) => true,
2970            (TypeSpec::Unit, Value::Unit) => true,
2971            (TypeSpec::Function, Value::Closure(_) | Value::Builtin(_)) => true,
2972            (TypeSpec::List(t), Value::List(items)) => items.iter().all(|x| self.conforms(x, t)),
2973            (TypeSpec::List(t), Value::Range(..)) => **t == TypeSpec::Int,
2974            (TypeSpec::Map(k, t), Value::Map(m)) => m.iter().all(|(a, b)| self.conforms(a, k) && self.conforms(b, t)),
2975            (TypeSpec::Bag(t), Value::Bag(b)) => b.keys().all(|x| self.conforms(x, t)),
2976            (TypeSpec::Dist(t), Value::Dist(d) | Value::Joint(d)) => d.outcomes.iter().all(|(x, _)| match x {
2977                Value::Continuous(_) => **t == TypeSpec::Float,
2978                _ => self.conforms(x, t),
2979            }),
2980            (TypeSpec::Dist(t), Value::Continuous(_)) => **t == TypeSpec::Float,
2981            (TypeSpec::Dist(t), Value::Counts(_)) => **t == TypeSpec::Int,
2982            (TypeSpec::Record(r), Value::Record(rec)) => {
2983                let declared = &self.prog.records[*r as usize];
2984                rec.ty.as_deref() == Some(declared.name.as_str())
2985                    && rec.fields.len() == declared.fields.len()
2986                    && declared
2987                        .fields
2988                        .iter()
2989                        .all(|f| rec.get(&f.name).is_some_and(|x| self.conforms(x, &f.ty)))
2990            }
2991            (TypeSpec::Enum(e), Value::Enum(x)) => x.ty == *e,
2992            (TypeSpec::AnonRecord(fields), Value::Record(rec)) => {
2993                rec.fields.len() == fields.len()
2994                    && rec
2995                        .fields
2996                        .iter()
2997                        .zip(fields)
2998                        .all(|((n, x), (m, t))| &**n == m.as_str() && self.conforms(x, t))
2999            }
3000            _ => false,
3001        }
3002    }
3003
3004    // ── Calls ────────────────────────────────────────────────────────────
3005
3006    /// Run a function as a sub-simulation and return the unnormalized
3007    /// distribution of its result. Results are memoized unless the function
3008    /// prints: its behaviour depends only on its arguments and the outside
3009    /// values it reads, which are all in `key`.
3010    ///
3011    /// When enumerating, a call can come back to itself: the same function,
3012    /// with the same arguments, while it's still running. Its result is then
3013    /// solved by iteration (section 6). The call that comes back gets the
3014    /// result so far, which starts with nothing resolved, and the call it
3015    /// came back to runs again with each new result, until the weight still
3016    /// waiting on it is below ε (`run_call`).
3017    fn call(&mut self, func: FnId, key: Vec<Value>, span: Span) -> Result<Arc<CallResult>> {
3018        self.stats.calls += 1;
3019        let prog = self.prog;
3020        let fun = &prog.functions[func as usize];
3021        // When sampling, every call makes its own choices (section 14).
3022        let sampling = self.sampler.is_some();
3023        // A cached result does not prove a callback's effects were permitted:
3024        // a draw or observation can collapse to one outcome of weight one.
3025        let memoizable = self.config.memoizing && !sampling && !fun.effects.prints && self.callback.is_none();
3026        let call_key = (func, key);
3027        if memoizable {
3028            if let Some(r) = self.memo.get(&call_key) {
3029                self.stats.memo_hits += 1;
3030                self.observed |= r.observed;
3031                return Ok(r.clone());
3032            }
3033        }
3034        if !sampling {
3035            if let Some(&depth) = self.active.get(&call_key) {
3036                if fun.effects.prints {
3037                    return Err(RuntimeError::new(
3038                        span,
3039                        format!("`{}` comes back to itself, and prints", describe_call(fun, &call_key.1)),
3040                    )
3041                    .with_note("a call that comes back to itself runs several times, until its result settles, and would print each time")
3042                    .with_help("remove the `print`, or print the result where the call is made"));
3043                }
3044                let so_far = match self.approx.get(&call_key) {
3045                    Some(a) if self.frames_at.get(a.head) == Some(&a.frame) => a.result.clone(),
3046                    _ => {
3047                        let result = Arc::new(CallResult::waiting(depth));
3048                        let approx = Approx {
3049                            result: result.clone(),
3050                            head: depth,
3051                            frame: self.frames_at[depth],
3052                            round: self.rounds_at[depth],
3053                        };
3054                        self.approx.insert(call_key, approx);
3055                        result
3056                    }
3057                };
3058                return Ok(so_far);
3059            }
3060            // Worked out already, in this round of the call it waits on.
3061            if let Some(a) = self.approx.get(&call_key) {
3062                if self.frames_at.get(a.head) == Some(&a.frame) && self.rounds_at.get(a.head) == Some(&a.round) {
3063                    self.stats.memo_hits += 1;
3064                    return Ok(a.result.clone());
3065                }
3066            }
3067        }
3068        if self.depth >= self.config.max_call_depth {
3069            return Err(RuntimeError::limit(
3070                span,
3071                format!("calls are nested more than {} deep", self.config.max_call_depth),
3072            )
3073            .with_help("check for recursion that doesn't stop, or write it as a loop"));
3074        }
3075        let mut slots = vec![Value::Dead; fun.n_slots()];
3076        let n = fun.n_params as usize;
3077        for (i, v) in call_key.1[..n].iter().enumerate() {
3078            slots[i] = v.clone();
3079        }
3080        for (cap, v) in fun.captures.iter().zip(&call_key.1[n..]) {
3081            slots[cap.slot as usize] = v.clone();
3082        }
3083        let depth = self.depth + 1;
3084        if !sampling {
3085            self.active.insert(call_key.clone(), depth);
3086            if self.frames_at.len() <= depth {
3087                self.frames_at.resize(depth + 1, 0);
3088                self.rounds_at.resize(depth + 1, 0);
3089            }
3090            self.last_number += 1;
3091            self.frames_at[depth] = self.last_number;
3092        }
3093        let result = self.run_call(func, &call_key, slots, depth, span);
3094        if !sampling {
3095            self.active.remove(&call_key);
3096            // Results that waited on this frame no longer stand for anything.
3097            self.frames_at[depth] = 0;
3098        }
3099        let result = result?;
3100        // A result still waiting on a call being solved isn't final.
3101        if memoizable
3102            && result.pending.is_empty()
3103            && result
3104                .outcomes
3105                .iter()
3106                .all(|(v, _, c)| c.is_empty() && !analytic::contains(v))
3107        {
3108            if self.memo.len() >= self.config.max_cached_calls {
3109                self.memo.clear();
3110            }
3111            self.memo.insert(call_key, result.clone());
3112        }
3113        Ok(result)
3114    }
3115
3116    /// Run a call's body from `slots`, at `depth` in the stack, and return
3117    /// its result (section 6).
3118    ///
3119    /// If the call came back to itself, and waits on no call further down
3120    /// the stack, it's solved here: round after round, each using the result
3121    /// of the round before, until the weight still waiting on it is below ε.
3122    /// That weight stays unresolved. If it waits on a call further down,
3123    /// it's part of that call's solving instead: what waits on it waits on
3124    /// that call, and its result stands for it until that call's next round.
3125    fn run_call(
3126        &mut self,
3127        func: FnId,
3128        call_key: &CallKey,
3129        slots: Vec<Value>,
3130        depth: usize,
3131        span: Span,
3132    ) -> Result<Arc<CallResult>> {
3133        let fun = &self.prog.functions[func as usize];
3134        let sampling = self.sampler.is_some();
3135        let mut inherited = std::collections::BTreeSet::new();
3136        for v in &slots {
3137            analytic::collect_ids(v, &mut inherited);
3138        }
3139        let inherited = Arc::new(inherited);
3140        let mut rounds: u64 = 0;
3141        let mut before = f64::INFINITY;
3142        loop {
3143            if !sampling {
3144                self.last_number += 1;
3145                self.rounds_at[depth] = self.last_number;
3146            }
3147            let saved_unresolved = std::mem::replace(&mut self.unresolved, Weight::ZERO);
3148            let saved_lost = std::mem::replace(&mut self.lost, Weight::ZERO);
3149            let saved_observed = std::mem::replace(&mut self.observed, false);
3150            let saved_pending = std::mem::take(&mut self.pending);
3151            let saved_failures = std::mem::take(&mut self.failures);
3152            self.depth += 1;
3153            let flow = self.exec_block(
3154                func,
3155                &fun.body,
3156                vec![World {
3157                    slots: slots.clone(),
3158                    constraints: Default::default(),
3159                    inherited: inherited.clone(),
3160                    weight: Weight::ONE,
3161                    run: 0,
3162                    densities: 0,
3163                }],
3164            );
3165            self.depth -= 1;
3166            let unresolved = std::mem::replace(&mut self.unresolved, saved_unresolved);
3167            let lost = std::mem::replace(&mut self.lost, saved_lost);
3168            let observed = std::mem::replace(&mut self.observed, saved_observed);
3169            let pending = std::mem::replace(&mut self.pending, saved_pending);
3170            let failures = std::mem::replace(&mut self.failures, saved_failures);
3171            self.observed |= observed;
3172            let flow = flow.map_err(|e| match fun.kind {
3173                FnKind::Named => e.with_note(format!("in a call to `{}`", fun.name)),
3174                FnKind::Simulate => e.with_note("inside a `simulate` block"),
3175                FnKind::Lambda => e.with_note("inside a lambda"),
3176                FnKind::Main => e,
3177            })?;
3178            uncaught(&flow)?;
3179            let mut result = CallResult {
3180                outcomes: merge_values(flow.returned),
3181                unresolved,
3182                lost,
3183                observed,
3184                pending,
3185                failures,
3186            };
3187            if let Some(head) = result.pending.iter().map(|&(d, _)| d).filter(|&d| d < depth).min() {
3188                // Part of the solving of the call at `head`.
3189                let waiting = Weight::sum(result.pending.iter().map(|&(_, w)| w));
3190                result.pending = vec![(head, waiting)];
3191                let result = Arc::new(result);
3192                let approx = Approx {
3193                    result: result.clone(),
3194                    head,
3195                    frame: self.frames_at[head],
3196                    round: self.rounds_at[head],
3197                };
3198                self.approx.insert(call_key.clone(), approx);
3199                return Ok(result);
3200            }
3201            let waiting = result.take_pending(depth);
3202            if waiting.is_zero() {
3203                return Ok(Arc::new(result));
3204            }
3205            // It came back to itself: solved here.
3206            if rounds == 0 {
3207                self.stats.solved_calls += 1;
3208            }
3209            let w = waiting.to_f64();
3210            if w <= self.config.epsilon {
3211                self.settle(depth);
3212                return Ok(Arc::new(result));
3213            }
3214            rounds += 1;
3215            self.stats.call_rounds += 1;
3216            if w >= before {
3217                return Err(RuntimeError::new(
3218                    span,
3219                    format!(
3220                        "`{}` never returns for some of its worlds",
3221                        describe_call(fun, &call_key.1)
3222                    ),
3223                )
3224                .with_note(format!("{} of its weight keeps coming back to it", fmt_prob(w)))
3225                .with_help("check that the recursion can end"));
3226            }
3227            if rounds >= self.config.max_iterations {
3228                return Err(RuntimeError::limit(
3229                    span,
3230                    format!(
3231                        "`{}` came back to itself {} times without settling",
3232                        describe_call(fun, &call_key.1),
3233                        self.config.max_iterations
3234                    ),
3235                )
3236                .with_note(format!("{} of its weight is still waiting on it", fmt_prob(w)))
3237                .with_help("check that the recursion can end, or raise `@epsilon`"));
3238            }
3239            before = w;
3240            result.pending.push((depth, waiting));
3241            let approx = Approx {
3242                result: Arc::new(result),
3243                head: depth,
3244                frame: self.frames_at[depth],
3245                round: self.rounds_at[depth],
3246            };
3247            self.approx.insert(call_key.clone(), approx);
3248        }
3249    }
3250
3251    /// The call at `depth` is solved: the results that waited on it, from
3252    /// its last round, are final too if the weight still waiting in them is
3253    /// below ε. The others are dropped.
3254    fn settle(&mut self, depth: usize) {
3255        let frame = self.frames_at[depth];
3256        let epsilon = self.config.epsilon;
3257        let settled: Vec<(CallKey, CallResult)> = self
3258            .approx
3259            .iter()
3260            .filter(|(_, a)| a.head == depth && a.frame == frame)
3261            .filter(|(_, a)| a.result.pending.iter().all(|&(_, w)| w.to_f64() <= epsilon))
3262            .map(|(key, a)| {
3263                let mut result = (*a.result).clone();
3264                // What's still waiting stays unresolved.
3265                result.pending.clear();
3266                (key.clone(), result)
3267            })
3268            .collect();
3269        self.approx.retain(|_, a| a.head != depth);
3270        if !self.config.memoizing {
3271            return;
3272        }
3273        for (key, result) in settled {
3274            if !self.active.contains_key(&key)
3275                && !self.prog.functions[key.0 as usize].effects.prints
3276                && result
3277                    .outcomes
3278                    .iter()
3279                    .all(|(v, _, c)| c.is_empty() && !analytic::contains(v))
3280            {
3281                self.memo.insert(key, Arc::new(result));
3282            }
3283        }
3284    }
3285
3286    /// `simulate { … }`: run a block as a separate model and return its
3287    /// normalized distribution (docs/semantics.md, section 8).
3288    fn simulate(&mut self, func: FnId, key: Vec<Value>, span: Span) -> Result<Value> {
3289        if key.iter().any(analytic::contains) {
3290            return Err(analytic::unsupported("capturing an analytic draw inside `simulate`").at(span));
3291        }
3292        if !self.simulated.is_empty() {
3293            if let Some(v) = self.simulated.get(&(func, key.clone())) {
3294                self.stats.memo_hits += 1;
3295                return Ok(v.clone());
3296            }
3297        }
3298        let call_key = self.config.memoizing.then(|| (func, key.clone()));
3299        let saved_observed = self.observed;
3300        let saved_densities = self.densities;
3301        let saved_local = (self.local_density_at.take(), self.returned_densities.take());
3302        // Enumerated, even when sampling (section 14).
3303        let sampler = self.sampler.take();
3304        let callback = self.callback.take();
3305        self.nested += 1;
3306        let result = self.call(func, key, span);
3307        self.nested -= 1;
3308        self.sampler = sampler;
3309        self.callback = callback;
3310        // Observations inside `simulate` condition its result only.
3311        self.observed = saved_observed;
3312        self.densities = saved_densities;
3313        let local_density_at = std::mem::replace(&mut self.local_density_at, saved_local.0);
3314        self.returned_densities = saved_local.1;
3315        let result = result?;
3316        if let Some(span) = local_density_at.filter(|_| !result.unresolved.is_zero()) {
3317            return Err(OpError::unsupported(
3318                "observing a value from a continuous distribution while some probability is unresolved isn't supported when enumerating",
3319            )
3320            .help("the unresolved worlds could have any density at the value, so nothing would bound the evidence; sample the model with `@mode sample(runs: 10_000)`")
3321            .at(span));
3322        }
3323        if !result.pending.is_empty() {
3324            return Err(OpError::unsupported(
3325                "a `simulate` block that comes back to a call still running isn't supported yet",
3326            )
3327            .at(span));
3328        }
3329        // It's built whole or not at all: a fault on one of its paths, not
3330        // caught inside it, fails the world that runs it.
3331        if let Some(g) = result.failures.groups.first() {
3332            return Err(g.error.clone());
3333        }
3334        let resolved = Weight::sum(result.outcomes.iter().map(|(_, w, _)| *w));
3335        if resolved.is_zero() {
3336            let message = if result.unresolved.is_zero() {
3337                "every world in this `simulate` was ruled out by `observe`"
3338            } else {
3339                "this `simulate` left all of its weight unresolved"
3340            };
3341            return Err(RuntimeError::new(span, message));
3342        }
3343        let denom = resolved + result.unresolved;
3344        let pairs: Vec<(Value, f64)> = result
3345            .outcomes
3346            .iter()
3347            .map(|(v, w, _)| (v.clone(), w.ratio(denom)))
3348            .collect();
3349        let missing = result.unresolved.ratio(denom);
3350        // Outcomes of its own draws make a joint distribution.
3351        if pairs.iter().any(|(v, _)| analytic::contains(v)) {
3352            let v = crate::joint::of(pairs, missing, &mut self.budget).map_err(|e| e.at(span))?;
3353            if let Some(call_key) = call_key {
3354                if self.simulated.len() >= self.config.max_cached_calls {
3355                    self.simulated.clear();
3356                }
3357                self.simulated.insert(call_key, v.clone());
3358            }
3359            return Ok(v);
3360        }
3361        ops::combine(pairs, missing, &mut self.budget).map_err(|e| e.at(span))
3362    }
3363
3364    /// Call a closure that must not split worlds (used by `map`, `filter`, …).
3365    fn call_pure(&mut self, closure: &Value, args: Vec<Value>, what: &'static str, span: Span) -> Result<Value> {
3366        if let Value::Builtin(b) = closure {
3367            return self.builtin_values(*b, &args, &[], Weight::ONE, span);
3368        }
3369        let Value::Closure(c) = closure else {
3370            return Err(RuntimeError::new(
3371                span,
3372                format!(
3373                    "`{what}` needs a function, like `x -> x * 2`, found {}",
3374                    ops::article(&closure.kind())
3375                ),
3376            ));
3377        };
3378        self.check_arity(c, args.len(), span)?;
3379        let mut key = args;
3380        key.extend(c.captured.iter().cloned());
3381        let previous = self.callback.replace(what);
3382        let result = self.call(c.func, key, span);
3383        self.callback = previous;
3384        let result = result?;
3385        if !result.pending.is_empty() {
3386            return Err(OpError::unsupported(format!(
3387                "a function given to `{what}` that comes back to a call still running isn't supported yet"
3388            ))
3389            .at(span));
3390        }
3391        // A callback that faults fails the whole operation, in the world
3392        // that runs it.
3393        if let Some(g) = result.failures.groups.first() {
3394            return Err(g.error.clone());
3395        }
3396        match result.outcomes.as_slice() {
3397            [(v, p, c)] if c.is_empty() && (p.to_f64() - 1.0).abs() < 1e-12 && result.unresolved.is_zero() => {
3398                Ok(v.clone())
3399            }
3400            _ => Err(RuntimeError::new(
3401                span,
3402                format!("the function given to `{what}` can't branch on chances, draw values or observe"),
3403            )),
3404        }
3405    }
3406
3407    // ── Places ───────────────────────────────────────────────────────────
3408
3409    fn check_callback_effect(&self, span: Span) -> Result<()> {
3410        match self.callback {
3411            Some(what) => Err(RuntimeError::new(
3412                span,
3413                format!("the function given to `{what}` can't branch on chances, draw values or observe"),
3414            )
3415            .with_help("use a loop for probabilistic traversal, or `simulate` for a local distribution")),
3416            None => Ok(()),
3417        }
3418    }
3419
3420    fn record_place_type(&self, place: &Place, w: &World) -> Option<TypeSpec> {
3421        if place.path.is_empty() {
3422            return None;
3423        }
3424        let Value::Record(r) = &w.slots[place.slot as usize] else {
3425            return None;
3426        };
3427        let id = self
3428            .prog
3429            .records
3430            .iter()
3431            .position(|t| Some(t.name.as_str()) == r.ty.as_deref())?;
3432        let mut ty = TypeSpec::Record(id as u32);
3433        for element in &place.path {
3434            ty = match (element, ty) {
3435                (PathElem::Field(name), TypeSpec::Record(id)) => self.prog.records[id as usize]
3436                    .fields
3437                    .iter()
3438                    .find(|f| f.name == *name)?
3439                    .ty
3440                    .clone(),
3441                (PathElem::Field(name), TypeSpec::AnonRecord(fields)) => fields.into_iter().find(|(n, _)| n == name)?.1,
3442                (PathElem::Index(_), TypeSpec::List(t) | TypeSpec::Map(_, t)) => *t,
3443                _ => return None,
3444            };
3445        }
3446        Some(ty)
3447    }
3448
3449    fn assign(&mut self, f: FnId, place: &Place, v: Value, w: &mut World, span: Span) -> Result<()> {
3450        if place.path.is_empty() {
3451            w.slots[place.slot as usize] = v;
3452            return Ok(());
3453        }
3454        let v = match self.record_place_type(place, w) {
3455            Some(ty) => {
3456                let v = self.coerce(v, &ty, span)?;
3457                if !self.conforms(&v, &ty) {
3458                    return Err(RuntimeError::new(
3459                        span,
3460                        format!(
3461                            "assigned field should be {}, found {}",
3462                            ty.describe(self.prog),
3463                            v.kind()
3464                        ),
3465                    ));
3466                }
3467                v
3468            }
3469            None => v,
3470        };
3471        let mut keys = Vec::with_capacity(place.path.len());
3472        for elem in &place.path {
3473            keys.push(match elem {
3474                PathElem::Field(name) => PathKey::Field(name.as_str()),
3475                PathElem::Index(e) => {
3476                    let key = self.eval(f, e, w)?;
3477                    if analytic::contains(&key) {
3478                        return Err(analytic::unsupported("an analytic assignment index").at(e.span));
3479                    }
3480                    PathKey::Index(key)
3481                }
3482            });
3483        }
3484        let target = &mut w.slots[place.slot as usize];
3485        if matches!(target, Value::Dead) {
3486            return Err(self.no_value(f, place.slot, span));
3487        }
3488        update(target, &keys, v, &mut self.budget).map_err(|e| e.at(span))
3489    }
3490
3491    /// Convert a variable to its declared type, or fail.
3492    fn check_slot(&mut self, f: FnId, slot: SlotId, ty: &TypeSpec, w: &mut World, span: Span) -> Result<()> {
3493        w.slots[slot as usize] = self.coerce(w.slots[slot as usize].clone(), ty, span)?;
3494        if !self.conforms(&w.slots[slot as usize], ty) {
3495            return Err(self.declared_type_error(f, slot, ty, w, span));
3496        }
3497        Ok(())
3498    }
3499
3500    fn declared_type_error(&self, f: FnId, slot: SlotId, ty: &TypeSpec, w: &World, span: Span) -> RuntimeError {
3501        let name = &self.prog.functions[f as usize].slots[slot as usize].name;
3502        let what = if name == TEMP {
3503            "the result".to_string()
3504        } else {
3505            format!("`{name}`")
3506        };
3507        RuntimeError::new(
3508            span,
3509            format!(
3510                "{what} should be {}, but it's {}",
3511                ops::article(&ty.describe(self.prog)),
3512                ops::article(&w.slots[slot as usize].kind())
3513            ),
3514        )
3515    }
3516
3517    /// `place = value`, where `value` applies a method like `push` to
3518    /// `place`, then the check of a variable with a declared type. Out of
3519    /// line: every statement runs the statement loop.
3520    #[inline(never)]
3521    fn update(
3522        &mut self,
3523        f: FnId,
3524        place: &Place,
3525        value: &Expr,
3526        check: Option<&UpdateCheck>,
3527        w: &mut World,
3528        span: Span,
3529    ) -> Result<()> {
3530        let ExprKind::Builtin { func, args, named } = &value.kind else {
3531            return Err(RuntimeError::new(span, "internal error: an update that isn't a method"));
3532        };
3533        let mut values = Vec::with_capacity(args.len() + named.len());
3534        for a in args.iter().chain(named.iter().map(|(_, a)| a)) {
3535            values.push(self.eval(f, a, w)?);
3536        }
3537        let field = self.record_place_type(place, w);
3538        let types = match check {
3539            Some(c) => c.args.as_deref().map(Some),
3540            None => Some(None),
3541        };
3542        if let (None, Some(types), true) = (&field, types, named.is_empty()) {
3543            if self.in_place(f, place, *func, &mut values, types, check, w, span)? {
3544                return Ok(());
3545            }
3546        }
3547        let names: Vec<String> = named.iter().map(|(n, _)| n.clone()).collect();
3548        let mut v = self.builtin_values(*func, &values, &names, w.weight, span)?;
3549        if let Some(ty) = field {
3550            v = self.coerce(v, &ty, value.span)?;
3551        }
3552        self.assign(f, place, v, w, span)?;
3553        if let Some(c) = check {
3554            self.check_slot(f, place.slot, &c.ty, w, span)?;
3555        }
3556        Ok(())
3557    }
3558
3559    /// Change the list or map at `place` as `func` would, given the values
3560    /// of its arguments, the collection first, without copying it unless
3561    /// something else holds it. What it adds is checked against `types`, as
3562    /// a check of the whole variable would. False, with nothing changed,
3563    /// where it can't, as for a distribution, which the method lifts over.
3564    #[allow(clippy::too_many_arguments)]
3565    fn in_place(
3566        &mut self,
3567        f: FnId,
3568        place: &Place,
3569        func: Builtin,
3570        values: &mut [Value],
3571        types: Option<&[Option<TypeSpec>]>,
3572        check: Option<&UpdateCheck>,
3573        w: &mut World,
3574        span: Span,
3575    ) -> Result<bool> {
3576        let plain = |v: &Value| {
3577            !v.is_uncertain()
3578                && !matches!(
3579                    v,
3580                    Value::Analytic(_) | Value::Event(_) | Value::Delayed(_) | Value::Dead
3581                )
3582        };
3583        let len = match (func, &values[0]) {
3584            (Builtin::Push | Builtin::Insert | Builtin::Remove | Builtin::DropLast, Value::List(items)) => items.len(),
3585            (Builtin::Insert | Builtin::Remove, Value::Map(m)) => m.len(),
3586            _ => return Ok(false),
3587        };
3588        if !values[1..].iter().all(plain) {
3589            return Ok(false);
3590        }
3591        let at = |e: OpError| e.at(span);
3592        // Whatever can fail does before anything changes.
3593        let list = matches!(values[0], Value::List(_));
3594        let index = match func {
3595            Builtin::Push | Builtin::Insert => {
3596                self.budget.collection(len as u128 + 1).map_err(at)?;
3597                match (func, list) {
3598                    (Builtin::Insert, true) => {
3599                        Some(ops::as_index(&values[1], len as u128 + 1, &mut self.budget).map_err(at)?)
3600                    }
3601                    _ => None,
3602                }
3603            }
3604            Builtin::Remove if list => Some(ops::as_index(&values[1], len as u128, &mut self.budget).map_err(at)?),
3605            _ => None,
3606        };
3607        if let (Some(types), Some(check)) = (types, check) {
3608            for (i, ty) in types.iter().enumerate() {
3609                let Some(ty) = ty else { continue };
3610                let given = &values[i + 1];
3611                let v = self.coerce(given.clone(), ty, span)?;
3612                if !self.conforms(&v, ty) {
3613                    return Err(self.declared_type_error(f, place.slot, &check.ty, w, span));
3614                }
3615                // A key that converts could meet one already there, which the
3616                // check of the whole map reports.
3617                if !list && i == 0 && v != *given {
3618                    return Ok(false);
3619                }
3620                values[i + 1] = v;
3621            }
3622        }
3623        let mut keys = Vec::with_capacity(place.path.len());
3624        for elem in &place.path {
3625            keys.push(match elem {
3626                PathElem::Field(name) => PathKey::Field(name.as_str()),
3627                PathElem::Index(e) => {
3628                    let key = self.eval(f, e, w)?;
3629                    if analytic::contains(&key) {
3630                        return Ok(false);
3631                    }
3632                    PathKey::Index(key)
3633                }
3634            });
3635        }
3636        // The value read for the method is another reference to the
3637        // collection, which would make changing it copy it.
3638        values[0] = Value::Unit;
3639        let target = place_mut(&mut w.slots[place.slot as usize], &keys, &mut self.budget).map_err(at)?;
3640        match (func, target) {
3641            (Builtin::Push, Value::List(items)) => {
3642                Arc::make_mut(items).push(std::mem::replace(&mut values[1], Value::Unit))
3643            }
3644            (Builtin::Insert, Value::List(items)) => Arc::make_mut(items).insert(
3645                index.unwrap_or_default() as usize,
3646                std::mem::replace(&mut values[2], Value::Unit),
3647            ),
3648            (Builtin::Remove, Value::List(items)) => {
3649                Arc::make_mut(items).remove(index.unwrap_or_default() as usize);
3650            }
3651            (Builtin::DropLast, Value::List(items)) => {
3652                Arc::make_mut(items).pop();
3653            }
3654            (Builtin::Insert, Value::Map(m)) => {
3655                let value = std::mem::replace(&mut values[2], Value::Unit);
3656                Arc::make_mut(m).insert(std::mem::replace(&mut values[1], Value::Unit), value);
3657            }
3658            (Builtin::Remove, Value::Map(m)) => {
3659                Arc::make_mut(m).remove(&values[1]);
3660            }
3661            _ => {
3662                return Err(RuntimeError::new(
3663                    span,
3664                    "internal error: an update found another collection",
3665                ));
3666            }
3667        }
3668        Ok(true)
3669    }
3670
3671    fn read_place(&mut self, f: FnId, place: &Place, w: &World, span: Span) -> Result<Value> {
3672        let mut v = self.slot(f, place.slot, w, span)?;
3673        for elem in &place.path {
3674            v = match elem {
3675                PathElem::Field(name) => ops::field(&v, name, &mut self.budget),
3676                PathElem::Index(e) => {
3677                    let i = self.eval(f, e, w)?;
3678                    ops::index(&v, &i, &mut self.budget)
3679                }
3680            }
3681            .map_err(|e| e.at(span))?;
3682        }
3683        Ok(v)
3684    }
3685
3686    /// Add the values `callee` captures to `out`, from the slots of `f` that
3687    /// hold them. A global it holds only for the function values it may call
3688    /// can have no value yet, as can any, with `lenient`: that's an error
3689    /// only where the global is read.
3690    #[allow(clippy::too_many_arguments)]
3691    fn capture(
3692        &mut self,
3693        out: &mut Vec<Value>,
3694        f: FnId,
3695        callee: FnId,
3696        args: &[SlotId],
3697        w: &World,
3698        span: Span,
3699        lenient: bool,
3700    ) -> Result<()> {
3701        let prog = self.prog;
3702        let captures = &prog.functions[callee as usize].captures;
3703        for (&s, capture) in args.iter().zip(captures) {
3704            out.push(match &w.slots[s as usize] {
3705                Value::Dead if lenient || capture.for_values => Value::Dead,
3706                _ => self.slot(f, s, w, span)?,
3707            });
3708        }
3709        Ok(())
3710    }
3711
3712    fn slot(&mut self, f: FnId, s: SlotId, w: &World, span: Span) -> Result<Value> {
3713        match &w.slots[s as usize] {
3714            Value::Dead => Err(self.no_value(f, s, span)),
3715            Value::Delayed(_) => {
3716                let name = &self.prog.functions[f as usize].slots[s as usize].name;
3717                Err(
3718                    OpError::internal("internal error: a variable was read before it was drawn")
3719                        .at(span)
3720                        .with_note(format!(
3721                            "`{name}`'s draw was delayed for an exact update (docs/semantics.md, section 14)"
3722                        )),
3723                )
3724            }
3725            v => analytic::resolve(v, &w.constraints, &mut self.budget).map_err(|e| e.at(span)),
3726        }
3727    }
3728
3729    fn no_value(&self, f: FnId, s: SlotId, span: Span) -> RuntimeError {
3730        let name = &self.prog.functions[f as usize].slots[s as usize].name;
3731        RuntimeError::new(span, format!("`{name}` has no value here")).with_help(
3732            "it's used before it's given a value; if a function reads it, call the function after the variable is set",
3733        )
3734    }
3735
3736    // ── Expressions ──────────────────────────────────────────────────────
3737
3738    fn eval_expected(&mut self, f: FnId, e: &Expr, w: &World, ty: &TypeSpec) -> Result<Value> {
3739        match (&e.kind, ty) {
3740            (ExprKind::List(xs), TypeSpec::List(t)) => {
3741                self.budget.collection(xs.len() as u128).map_err(|err| err.at(e.span))?;
3742                let mut out = Vec::with_capacity(xs.len());
3743                for x in xs {
3744                    out.push(self.eval_expected(f, x, w, t)?);
3745                }
3746                Ok(Value::list(out))
3747            }
3748            (ExprKind::Map(xs), TypeSpec::Map(kt, vt)) => {
3749                let mut out = BTreeMap::new();
3750                for (k, v) in xs {
3751                    let key = self.eval_expected(f, k, w, kt)?;
3752                    if analytic::contains(&key) {
3753                        return Err(analytic::unsupported("analytic map keys").at(k.span));
3754                    }
3755                    if key.is_uncertain() {
3756                        return Err(RuntimeError::new(k.span, "map keys can't be distributions"));
3757                    }
3758                    out.insert(key, self.eval_expected(f, v, w, vt)?);
3759                }
3760                Ok(Value::map(out))
3761            }
3762            (ExprKind::Record { ty: None, fields }, TypeSpec::AnonRecord(types)) => {
3763                let mut out = Vec::with_capacity(fields.len());
3764                for (n, e) in fields {
3765                    let v = match types.iter().find(|(name, _)| name == n) {
3766                        Some((_, ty)) => self.eval_expected(f, e, w, ty)?,
3767                        None => self.eval(f, e, w)?,
3768                    };
3769                    out.push((Arc::from(n.as_str()), v));
3770                }
3771                Ok(ops::make_record(None, out))
3772            }
3773            _ => {
3774                let v = self.eval(f, e, w)?;
3775                self.coerce(v, ty, e.span)
3776            }
3777        }
3778    }
3779
3780    /// A shared field type supplies conversion context even when the record is
3781    /// represented by a finite distribution. Heterogeneous records have no
3782    /// shared context, and their individual declarations are checked below.
3783    fn record_field_type(&self, value: &Value, name: &str) -> Option<TypeSpec> {
3784        match value {
3785            Value::Record(r) => self
3786                .prog
3787                .records
3788                .iter()
3789                .find(|t| Some(t.name.as_str()) == r.ty.as_deref())?
3790                .fields
3791                .iter()
3792                .find(|f| f.name == name)
3793                .map(|f| f.ty.clone()),
3794            Value::Dist(d) => {
3795                let first = self.record_field_type(&d.outcomes.first()?.0, name)?;
3796                d.outcomes
3797                    .iter()
3798                    .all(|(v, _)| self.record_field_type(v, name).as_ref() == Some(&first))
3799                    .then_some(first)
3800            }
3801            _ => None,
3802        }
3803    }
3804
3805    fn check_updated_records(&mut self, value: Value, span: Span) -> Result<Value> {
3806        match value {
3807            Value::Record(ref r) => {
3808                let Some(id) = self
3809                    .prog
3810                    .records
3811                    .iter()
3812                    .position(|t| Some(t.name.as_str()) == r.ty.as_deref())
3813                else {
3814                    return Ok(value);
3815                };
3816                let ty = TypeSpec::Record(id as u32);
3817                let value = self.coerce(value, &ty, span)?;
3818                if !self.conforms(&value, &ty) {
3819                    return Err(RuntimeError::new(
3820                        span,
3821                        format!("updated record doesn't conform to {}", ty.describe(self.prog)),
3822                    ));
3823                }
3824                Ok(value)
3825            }
3826            Value::Dist(d) => {
3827                let mut outcomes = Vec::with_capacity(d.outcomes.len());
3828                for (v, p) in &d.outcomes {
3829                    outcomes.push((self.check_updated_records(v.clone(), span)?, *p));
3830                }
3831                ops::combine(outcomes, d.missing, &mut self.budget).map_err(|e| e.at(span))
3832            }
3833            v => Ok(v),
3834        }
3835    }
3836
3837    fn eval(&mut self, f: FnId, e: &Expr, w: &World) -> Result<Value> {
3838        let span = e.span;
3839        let at = |err: OpError| err.at(span);
3840        match &e.kind {
3841            ExprKind::Lit(l) => self.literal(l).map_err(at),
3842            ExprKind::Slot(s) => self.slot(f, *s, w, span),
3843            ExprKind::Unary(op, x) => {
3844                let v = self.eval(f, x, w)?;
3845                ops::unary(*op, &v, &mut self.budget).map_err(at)
3846            }
3847            ExprKind::Binary(op @ (BinOp::And | BinOp::Or), a, b) => {
3848                let and = *op == BinOp::And;
3849                let word = if and { "and" } else { "or" };
3850                let va = self.eval(f, a, w)?;
3851                let ta = ops::truth(&va, word).map_err(|err| err.at(a.span))?;
3852                if let Truth::Fact(x) = ta {
3853                    if x != and {
3854                        // `false and …` is false; `true or …` is true.
3855                        return Ok(Value::Bool(x));
3856                    }
3857                }
3858                let vb = self.eval(f, b, w)?;
3859                let tb = ops::truth(&vb, word).map_err(|err| err.at(b.span))?;
3860                ops::logic(and, ta, tb, &mut self.budget).map_err(at)
3861            }
3862            ExprKind::Binary(op, a, b) => {
3863                let va = self.eval(f, a, w)?;
3864                let vb = self.eval(f, b, w)?;
3865                ops::binary(*op, &va, &vb, &mut self.budget).map_err(at)
3866            }
3867            ExprKind::List(items) => {
3868                self.budget.collection(items.len() as u128).map_err(at)?;
3869                let mut values = Vec::with_capacity(items.len());
3870                for item in items {
3871                    values.push(self.eval(f, item, w)?);
3872                }
3873                Ok(Value::list(values))
3874            }
3875            ExprKind::Map(entries) => {
3876                let mut map = BTreeMap::new();
3877                for (k, v) in entries {
3878                    let key = self.eval(f, k, w)?;
3879                    if analytic::contains(&key) {
3880                        return Err(analytic::unsupported("analytic map keys").at(k.span));
3881                    }
3882                    if key.is_uncertain() {
3883                        return Err(RuntimeError::new(k.span, "map keys can't be distributions"));
3884                    }
3885                    let value = self.eval(f, v, w)?;
3886                    map.insert(key, value);
3887                }
3888                Ok(Value::map(map))
3889            }
3890            ExprKind::Record { ty, fields } => {
3891                let mut values = Vec::with_capacity(fields.len());
3892                for (name, v) in fields {
3893                    let field_ty = ty
3894                        .and_then(|t| self.prog.records[t as usize].fields.iter().find(|d| d.name == *name))
3895                        .map(|d| d.ty.clone());
3896                    let value = match field_ty {
3897                        Some(t) => {
3898                            let value = self.eval(f, v, w)?;
3899                            let value = self.coerce(value, &t, v.span)?;
3900                            if !self.conforms(&value, &t) {
3901                                return Err(RuntimeError::new(
3902                                    v.span,
3903                                    format!(
3904                                        "field `{name}` should be {}, found {}",
3905                                        t.describe(self.prog),
3906                                        value.kind()
3907                                    ),
3908                                ));
3909                            }
3910                            value
3911                        }
3912                        None => self.eval(f, v, w)?,
3913                    };
3914                    values.push((Arc::from(name.as_str()), value));
3915                }
3916                Ok(ops::make_record(
3917                    ty.map(|t| self.record_names[t as usize].clone()),
3918                    values,
3919                ))
3920            }
3921            ExprKind::Field(x, name) => {
3922                let v = self.eval(f, x, w)?;
3923                ops::field(&v, name, &mut self.budget).map_err(at)
3924            }
3925            ExprKind::Index(x, i) => {
3926                let v = self.eval(f, x, w)?;
3927                let i = self.eval(f, i, w)?;
3928                ops::index(&v, &i, &mut self.budget).map_err(at)
3929            }
3930            ExprKind::With(x, fields) => {
3931                let base = self.eval(f, x, w)?;
3932                let mut updates = Vec::with_capacity(fields.len());
3933                for (name, v) in fields {
3934                    let field_ty = self.record_field_type(&base, name);
3935                    let value = match field_ty {
3936                        Some(ty) => {
3937                            let value = self.eval_expected(f, v, w, &ty)?;
3938                            if !self.conforms(&value, &ty) {
3939                                return Err(RuntimeError::new(
3940                                    v.span,
3941                                    format!(
3942                                        "field `{name}` should be {}, found {}",
3943                                        ty.describe(self.prog),
3944                                        value.kind()
3945                                    ),
3946                                ));
3947                            }
3948                            value
3949                        }
3950                        None => self.eval(f, v, w)?,
3951                    };
3952                    updates.push((Arc::from(name.as_str()), value));
3953                }
3954                let value = ops::lift1(&base, &mut self.budget, |b, _| ops::with_fields(b, &updates)).map_err(at)?;
3955                self.check_updated_records(value, span)
3956            }
3957            ExprKind::Builtin { func, args, named } => self.builtin(f, *func, args, named, w, span),
3958            ExprKind::Closure { func, capture_args } => {
3959                // A named function reads its globals when it's called, so a
3960                // reference to it doesn't need them yet.
3961                let named = self.prog.functions[*func as usize].kind == FnKind::Named;
3962                let mut captured = Vec::with_capacity(capture_args.len());
3963                self.capture(&mut captured, f, *func, capture_args, w, span, named)?;
3964                Ok(Value::Closure(Arc::new(Closure {
3965                    func: *func,
3966                    captured,
3967                    named,
3968                })))
3969            }
3970            ExprKind::Bind { value, globals } => {
3971                let v = self.eval(f, value, w)?;
3972                let Value::Closure(c) = &v else {
3973                    return Ok(v);
3974                };
3975                if !c.named {
3976                    return Ok(v);
3977                }
3978                // A named function reads its globals now, as a call by name
3979                // does.
3980                let prog = self.prog;
3981                let captures = &prog.functions[c.func as usize].captures;
3982                let mut captured = Vec::with_capacity(captures.len());
3983                for capture in captures {
3984                    let CaptureSource::Global(g) = capture.source else {
3985                        return Err(RuntimeError::new(
3986                            span,
3987                            "internal error: a named function captures a local",
3988                        ));
3989                    };
3990                    let Ok(i) = globals.binary_search_by_key(&g, |&(g, _)| g) else {
3991                        return Err(RuntimeError::new(
3992                            span,
3993                            "internal error: a function value's globals aren't available here",
3994                        ));
3995                    };
3996                    captured.push(self.slot(f, globals[i].1, w, span)?);
3997                }
3998                Ok(Value::Closure(Arc::new(Closure {
3999                    func: c.func,
4000                    captured,
4001                    named: true,
4002                })))
4003            }
4004            ExprKind::Simulate { func, capture_args } => {
4005                let mut key = Vec::with_capacity(capture_args.len());
4006                self.capture(&mut key, f, *func, capture_args, w, span, false)?;
4007                self.simulate(*func, key, span)
4008            }
4009            ExprKind::Interp(parts) => {
4010                let mut text = String::new();
4011                for part in parts {
4012                    match part {
4013                        InterpPart::Lit(s) => crate::text::push(&mut text, s, &mut self.budget).map_err(at)?,
4014                        InterpPart::Expr(e) => {
4015                            let value = self.eval(f, e, w)?;
4016                            crate::text::push_value(&mut text, &value, &mut self.budget).map_err(at)?;
4017                        }
4018                    }
4019                }
4020                Ok(Value::str(&text))
4021            }
4022            ExprKind::Input(i) => Ok(self.inputs[*i as usize].clone()),
4023        }
4024    }
4025
4026    fn literal(&mut self, l: &Lit) -> OpResult<Value> {
4027        Ok(match l {
4028            Lit::Builtin(b) => Value::Builtin(*b),
4029            Lit::Unit => Value::Unit,
4030            Lit::Bool(b) => Value::Bool(*b),
4031            Lit::Int(i) => {
4032                self.budget.integer_bits(i.bits())?;
4033                self.budget.work(i.bits().div_ceil(64).max(1))?;
4034                Value::Int(i.clone())
4035            }
4036            Lit::Float(x) | Lit::FloatConstant(x) => Value::Float(*x),
4037            Lit::Prob(p) => Value::Prob(*p),
4038            Lit::Str(s) => crate::text::value(s, &mut self.budget)?,
4039            Lit::Dice { count, sides } => {
4040                if let Some(d) = self.dice.get(&(*count, *sides)) {
4041                    return Ok(d.clone());
4042                }
4043                let d = Dist::dice(*count, *sides, &mut self.budget)?.into_value();
4044                self.dice.insert((*count, *sides), d.clone());
4045                d
4046            }
4047            Lit::Enum { ty, variant } => self.enum_values[*ty as usize][*variant as usize].clone(),
4048        })
4049    }
4050
4051    fn builtin(
4052        &mut self,
4053        f: FnId,
4054        b: Builtin,
4055        args: &[Expr],
4056        named: &[(String, Expr)],
4057        w: &World,
4058        span: Span,
4059    ) -> Result<Value> {
4060        if b == Builtin::Typeof {
4061            // A direct inspection knows a delayed variable's outcome type
4062            // without drawing it and losing subsequent conjugate updates.
4063            let v = match args[0].kind {
4064                ExprKind::Slot(s) if matches!(w.slots[s as usize], Value::Delayed(_)) => w.slots[s as usize].clone(),
4065                _ => self.eval(f, &args[0], w)?,
4066            };
4067            return crate::type_name::of(&v, &self.prog.enums, &mut self.budget).map_err(|e| e.at(span));
4068        }
4069        let mut values = Vec::with_capacity(args.len());
4070        for a in args {
4071            values.push(self.eval(f, a, w)?);
4072        }
4073        let mut names = Vec::with_capacity(named.len());
4074        for (name, value) in named {
4075            names.push(name.clone());
4076            values.push(self.eval(f, value, w)?);
4077        }
4078        self.builtin_values(b, &values, &names, w.weight, span)
4079    }
4080
4081    fn check_function_arity(&self, value: &Value, given: usize, span: Span) -> Result<()> {
4082        match value {
4083            Value::Closure(c) => self.check_arity(c, given, span),
4084            Value::Builtin(b) => {
4085                let (min, max) = b.arity();
4086                if given < min || given > max || (*b == Builtin::Date && given == 2) {
4087                    return Err(RuntimeError::new(
4088                        span,
4089                        format!(
4090                            "`{}` takes {}, but {given} were given",
4091                            b.name(),
4092                            if *b == Builtin::Date {
4093                                "one ISO string or three integers".into()
4094                            } else if min == max {
4095                                format!("{min} argument(s)")
4096                            } else if max == usize::MAX {
4097                                format!("at least {min} arguments")
4098                            } else {
4099                                format!("{min} to {max} arguments")
4100                            }
4101                        ),
4102                    ));
4103                }
4104                Ok(())
4105            }
4106            _ => Err(RuntimeError::new(
4107                span,
4108                "expected a comparator function, like `(a, b) -> a - b`",
4109            )),
4110        }
4111    }
4112
4113    /// Shared dispatch for direct calls and first-class builtin values.
4114    fn builtin_values(
4115        &mut self,
4116        b: Builtin,
4117        values: &[Value],
4118        named: &[String],
4119        weight: Weight,
4120        span: Span,
4121    ) -> Result<Value> {
4122        let extrema = matches!(b, Builtin::Minimum | Builtin::Maximum);
4123        if !named.is_empty() && (!extrema || named != ["default"]) {
4124            return Err(RuntimeError::new(
4125                span,
4126                format!(
4127                    "`{}` does not accept these named arguments; only minimum/maximum support `default`",
4128                    b.name()
4129                ),
4130            ));
4131        }
4132        let positional = values.len() - named.len();
4133        if !named.is_empty() && positional == 0 {
4134            return Err(RuntimeError::new(
4135                span,
4136                "a collection argument is required before `default`",
4137            ));
4138        }
4139        if !named.is_empty() && positional >= 3 {
4140            return Err(RuntimeError::new(
4141                span,
4142                "the default was supplied both positionally and by name",
4143            ));
4144        }
4145        self.check_function_arity(&Value::Builtin(b), values.len(), span)?;
4146        let (values, default) = if extrema && (!named.is_empty() || values.len() == 3) {
4147            (&values[..values.len() - 1], values.last())
4148        } else {
4149            (values, None)
4150        };
4151        let at = |err: OpError| err.at(span);
4152        builtins::check_query_input(b, values).map_err(at)?;
4153        // Asking the built-in first spares a search of every collection
4154        // argument, on every call, for those that move values unseen.
4155        if !handles_analytic(b, values) && values.iter().any(analytic::contains) {
4156            // Internal built-ins stand for the syntax that uses them.
4157            let name = match b {
4158                Builtin::ScoreLaw => "score",
4159                Builtin::RepeatCount => "repeat",
4160                Builtin::Typeof => "typeof",
4161                b => b.name(),
4162            };
4163            return Err(analytic::unsupported(&format!("`{name}` on this outcome")).at(span));
4164        }
4165        match b {
4166            Builtin::Typeof => crate::type_name::of(&values[0], &self.prog.enums, &mut self.budget).map_err(at),
4167            Builtin::RunDate => self.config.today.map(Value::Date).ok_or_else(|| {
4168                RuntimeError::new(span, "the host did not supply an execution date for `today`")
4169                    .with_help("set Options.today, or supply today in the WASM request")
4170            }),
4171            Builtin::Print => {
4172                let mut text = String::new();
4173                for (i, value) in values.iter().enumerate() {
4174                    if i != 0 {
4175                        crate::text::push(&mut text, " ", &mut self.budget).map_err(at)?;
4176                    }
4177                    crate::text::push_value(&mut text, value, &mut self.budget).map_err(at)?;
4178                }
4179                let line = if weight == Weight::ONE {
4180                    text
4181                } else {
4182                    format!("[{}] {text}", fmt_weight(weight))
4183                };
4184                self.printed += line.len() + 1;
4185                if self.printed > self.config.max_output {
4186                    return Err(too_much_output(span));
4187                }
4188                match &mut self.lines {
4189                    Some(lines) => lines.push((span, line)),
4190                    None => (self.print)(&line),
4191                }
4192                Ok(Value::Unit)
4193            }
4194            Builtin::Map | Builtin::Filter | Builtin::Reduce => self.higher_order(b, values, span),
4195            Builtin::Count if values.len() == 2 => self.higher_order(b, values, span),
4196            Builtin::Minimum | Builtin::Maximum => {
4197                if values.len() == 2 {
4198                    self.collection_extreme(b, values, default, span)
4199                } else {
4200                    builtins::population_extreme(&values[0], b == Builtin::Maximum, default, &mut self.budget)
4201                        .map_err(at)
4202                }
4203            }
4204            Builtin::Sort | Builtin::SortDesc if values.len() == 2 => self.higher_order(b, values, span),
4205            Builtin::Highest | Builtin::Lowest if values.len() == 3 => self.higher_order(b, values, span),
4206            Builtin::Min | Builtin::Max => builtins::call_plain(b, values, &mut self.budget).map_err(at),
4207            Builtin::Roll => self.roll(values).map_err(at),
4208            Builtin::Take => Err(RuntimeError::new(
4209                span,
4210                "use `deck.take()` to draw and remove an item from a mutable bag",
4211            )),
4212            _ if b.lifting() == Lifting::Raw => builtins::call_raw(b, values, &mut self.budget).map_err(at),
4213            _ if values.iter().any(|v| matches!(v, Value::Continuous(_))) => {
4214                let kind = values
4215                    .iter()
4216                    .find(|v| matches!(v, Value::Continuous(_)))
4217                    .unwrap()
4218                    .kind();
4219                Err(
4220                    RuntimeError::new(span, format!("`{}` needs a value, not a {kind}", b.name()))
4221                        .with_help("draw a value first, like `let x ~ normal(0, 1)`"),
4222                )
4223            }
4224            _ => ops::lift_n(values, &mut self.budget, &|a, budget| {
4225                builtins::call_plain(b, a, budget)
4226            })
4227            .map_err(at),
4228        }
4229    }
4230
4231    fn roll(&mut self, values: &[Value]) -> OpResult<Value> {
4232        if values[0].is_uncertain() {
4233            return Err(OpError::new("the number of dice to roll must be a plain number")
4234                .help("draw it first, like `let n ~ d4`"));
4235        }
4236        let count = ops::integer(&values[0], "roll count", &mut self.budget)?;
4237        let count = count
4238            .to_u64()
4239            .filter(|n| *n <= 1000)
4240            .ok_or_else(|| OpError::new("roll needs between 0 and 1000 dice"))? as u32;
4241        let die = match &values[1] {
4242            v @ (Value::Int(_) | Value::Float(_)) => {
4243                let sides = ops::integer(v, "roll sides", &mut self.budget)?;
4244                let sides = sides
4245                    .to_u64()
4246                    .filter(|n| *n >= 1 && *n <= u32::MAX as u64)
4247                    .ok_or_else(|| OpError::new("roll needs between 1 and 4294967295 sides"))?;
4248                Dist::dice(1, sides as u32, &mut self.budget)?
4249            }
4250            Value::Dist(d) => (**d).clone(),
4251            v => {
4252                return Err(OpError::new(format!(
4253                    "roll needs a die, like `d6`, found {}",
4254                    ops::article(&v.kind())
4255                )));
4256            }
4257        };
4258        let key = (count, die.clone().into_value());
4259        if let Some(pool) = self.pools.get(&key) {
4260            return Ok(pool.clone());
4261        }
4262        let pool = Dist::pool(count, &die, &mut self.budget)?.into_value();
4263        self.pools.insert(key, pool.clone());
4264        Ok(pool)
4265    }
4266
4267    fn compare_callback(
4268        &mut self,
4269        comparator: &Value,
4270        a: &Value,
4271        other: &Value,
4272        b: Builtin,
4273        span: Span,
4274    ) -> Result<std::cmp::Ordering> {
4275        self.budget.work(1).map_err(|e| e.at(span))?;
4276        match self.call_pure(comparator, vec![a.clone(), other.clone()], b.name(), span)? {
4277            Value::Int(n) => Ok(n.cmp(&probl_number::Integer::ZERO)),
4278            Value::Float(x) if x.is_finite() => Ok(x.partial_cmp(&0.0).expect("finite comparator result")),
4279            other => Err(RuntimeError::new(
4280                span,
4281                format!(
4282                    "the comparator given to `{}` must return a finite int or float (negative, zero, or positive), found {}",
4283                    b.name(),
4284                    ops::article(&other.kind())
4285                ),
4286            )),
4287        }
4288    }
4289
4290    fn collection_extreme(
4291        &mut self,
4292        b: Builtin,
4293        values: &[Value],
4294        default: Option<&Value>,
4295        span: Span,
4296    ) -> Result<Value> {
4297        if matches!(values[0], Value::Dist(_) | Value::Continuous(_)) {
4298            return Err(RuntimeError::new(
4299                span,
4300                "a comparator is supported only for collection extrema, not distribution support bounds",
4301            ));
4302        }
4303        let comparator = &values[1];
4304        self.check_function_arity(comparator, 2, span)?;
4305        let items = builtins::items(&values[0], b.name(), &mut self.budget).map_err(|e| e.at(span))?;
4306        let mut iter = items.into_iter();
4307        let Some(mut best) = iter.next() else {
4308            return builtins::empty_extreme(b.name(), default).map_err(|e| e.at(span));
4309        };
4310        for x in iter {
4311            let order = self.compare_callback(comparator, &x, &best, b, span)?;
4312            if (b == Builtin::Maximum && order.is_gt()) || (b == Builtin::Minimum && order.is_lt()) {
4313                best = x;
4314            }
4315        }
4316        Ok(best)
4317    }
4318
4319    /// Collection operations which call a function, including custom sorting.
4320    fn higher_order(&mut self, b: Builtin, values: &[Value], span: Span) -> Result<Value> {
4321        if let Value::Dist(d) = &values[0] {
4322            let mut results = Vec::with_capacity(d.outcomes.len());
4323            for (coll, p) in &d.outcomes {
4324                let mut args = values.to_vec();
4325                args[0] = coll.clone();
4326                results.push((self.higher_order(b, &args, span)?, *p));
4327            }
4328            return ops::combine(results, d.missing, &mut self.budget).map_err(|e| e.at(span));
4329        }
4330        if matches!(b, Builtin::Highest | Builtin::Lowest) {
4331            if let Value::Dist(d) = &values[1] {
4332                let mut results = Vec::with_capacity(d.outcomes.len());
4333                for (count, p) in &d.outcomes {
4334                    let mut args = values.to_vec();
4335                    args[1] = count.clone();
4336                    results.push((self.higher_order(b, &args, span)?, *p));
4337                }
4338                return ops::combine(results, d.missing, &mut self.budget).map_err(|e| e.at(span));
4339            }
4340        }
4341        let items = builtins::items(&values[0], b.name(), &mut self.budget).map_err(|e| e.at(span))?;
4342        match b {
4343            Builtin::Sort | Builtin::SortDesc | Builtin::Highest | Builtin::Lowest => {
4344                let comparator = values.last().unwrap();
4345                // Validate even empty/singleton collections without invoking the callback.
4346                self.check_function_arity(comparator, 2, span)?;
4347                let count = if matches!(b, Builtin::Highest | Builtin::Lowest) {
4348                    Some(builtins::extreme_count(&values[1], &mut self.budget).map_err(|e| e.at(span))?)
4349                } else {
4350                    None
4351                };
4352                crate::ordering::reserve_sort(items.len(), &mut self.budget).map_err(|e| e.at(span))?;
4353                let mut items = items;
4354                crate::ordering::try_sort_by(&mut items, |a, other| {
4355                    let order = self.compare_callback(comparator, a, other, b, span)?;
4356                    Ok(if matches!(b, Builtin::SortDesc | Builtin::Highest) {
4357                        order.reverse()
4358                    } else {
4359                        order
4360                    })
4361                })?;
4362                if let Some(n) = count {
4363                    items.truncate(n);
4364                }
4365                Ok(Value::list(items))
4366            }
4367            Builtin::Map => {
4368                let mut out = Vec::with_capacity(items.len());
4369                for x in items {
4370                    out.push(self.call_pure(&values[1], vec![x], "map", span)?);
4371                }
4372                Ok(Value::list(out))
4373            }
4374            Builtin::Filter | Builtin::Count => {
4375                let mut kept = Vec::new();
4376                for x in items {
4377                    let test = self.call_pure(&values[1], vec![x.clone()], b.name(), span)?;
4378                    match test {
4379                        Value::Bool(true) => kept.push(x),
4380                        Value::Bool(false) => {}
4381                        other => {
4382                            return Err(RuntimeError::new(
4383                                span,
4384                                format!("the test given to `{}` must give a fact (true or false)", b.name()),
4385                            )
4386                            .with_help(format!("it gave {}", ops::article(&other.kind()))));
4387                        }
4388                    }
4389                }
4390                Ok(if b == Builtin::Count {
4391                    Value::Int((kept.len() as i64).into())
4392                } else {
4393                    Value::list(kept)
4394                })
4395            }
4396            Builtin::Reduce => {
4397                let callback = &values[1];
4398                if !matches!(callback, Value::Closure(_) | Value::Builtin(_)) {
4399                    return Err(
4400                        RuntimeError::new(span, "`reduce` needs a function as its second argument")
4401                            .with_help("write `reduce(xs, f)` or `reduce(xs, f, initial)`"),
4402                    );
4403                }
4404                self.check_function_arity(callback, 2, span)?;
4405                let mut items = items.into_iter();
4406                let mut acc = match values.get(2) {
4407                    Some(initial) => initial.clone(),
4408                    None => items.next().ok_or_else(|| {
4409                        RuntimeError::new(span, "`reduce` of an empty collection needs an initial value")
4410                            .with_help("supply an initial value: `reduce(xs, f, initial)`")
4411                    })?,
4412                };
4413                for x in items {
4414                    acc = self.call_pure(callback, vec![acc, x], "reduce", span)?;
4415                }
4416                Ok(acc)
4417            }
4418            _ => unreachable!(),
4419        }
4420    }
4421}
4422
4423/// A call as written: `f(3, true)`.
4424/// The most rewards a loop's chain follows.
4425const MAX_REWARDS: usize = 4;
4426
4427/// A loop's rewards, as whole numbers or floats by their bits.
4428type Reward = [u64; MAX_REWARDS];
4429
4430/// Rewards as a world has them: their bits, and which are floats.
4431type Raw = (Reward, u8);
4432
4433/// A loop's chain with rewards, as `solve_loop` found it: for each state,
4434/// its steps with how much the rewards grow, and what leaves the loop from
4435/// it, in one run of the body from the state with its rewards at 0.
4436struct Rewarded<'a> {
4437    rewards: &'a [usize],
4438    moves: &'a [Vec<(u32, Raw, f64)>],
4439    exits: Vec<Vec<World>>,
4440    returns: Vec<Vec<Returned>>,
4441    unresolved: Vec<Weight>,
4442    failed: Vec<Failures>,
4443    faulted: Vec<Vec<(World, RuntimeError)>>,
4444}
4445
4446fn describe_call(fun: &Function, key: &[Value]) -> String {
4447    let args: Vec<String> = key[..fun.n_params as usize].iter().map(|v| format!("{v:?}")).collect();
4448    format!("{}({})", fun.name, args.join(", "))
4449}
4450
4451/// The error when `print` at `span` goes over the output limit.
4452/// A world faults to a catch only inside a `try` of its own call, which takes
4453/// it: none can leave the call that way.
4454fn uncaught(flow: &Flow) -> Result<()> {
4455    match flow.faulted.first() {
4456        Some((_, e)) => Err(OpError::internal("a fault went past the `try` meant to catch it").at(e.span)),
4457        None => Ok(()),
4458    }
4459}
4460
4461/// Remove the worlds at the indices `failed`, which are in increasing order.
4462fn drop_failed(worlds: &mut Vec<World>, failed: &[usize]) {
4463    if failed.is_empty() {
4464        return;
4465    }
4466    let mut failed = failed.iter().copied().peekable();
4467    let mut i = 0;
4468    worlds.retain(|_| {
4469        let drop = failed.next_if_eq(&i).is_some();
4470        i += 1;
4471        !drop
4472    });
4473}
4474
4475pub fn too_much_output(span: Span) -> RuntimeError {
4476    RuntimeError::limit(span, "the program printed more than the output limit")
4477}
4478
4479/// A weight as a percentage, even when it's too small for an `f64`.
4480pub fn fmt_weight(w: Weight) -> String {
4481    let x = w.to_f64();
4482    if x == 0.0 && !w.is_zero() {
4483        let l = w.log10() + 2.0;
4484        return format!("{:.1}e{}%", libm::pow(10.0, l - l.floor()), l.floor());
4485    }
4486    fmt_prob(x)
4487}
4488
4489enum PathKey<'a> {
4490    Field(&'a str),
4491    Index(Value),
4492}
4493
4494fn update(target: &mut Value, keys: &[PathKey], v: Value, budget: &mut Budget) -> OpResult<()> {
4495    let Some((first, rest)) = keys.split_first() else {
4496        *target = v;
4497        return Ok(());
4498    };
4499    match (first, target) {
4500        (PathKey::Field(name), Value::Record(r)) => {
4501            let r = Arc::make_mut(r);
4502            let slot = r
4503                .get_mut(name)
4504                .ok_or_else(|| OpError::new(format!("this record has no field `{name}`")))?;
4505            update(slot, rest, v, budget)
4506        }
4507        (PathKey::Index(i), Value::List(items)) => {
4508            let items = Arc::make_mut(items);
4509            let k = ops::as_index(i, items.len() as u128, budget)? as usize;
4510            update(&mut items[k], rest, v, budget)
4511        }
4512        (PathKey::Index(k), Value::Map(m)) => {
4513            let m = Arc::make_mut(m);
4514            if rest.is_empty() {
4515                m.insert(k.clone(), v);
4516                return Ok(());
4517            }
4518            let slot = m
4519                .get_mut(k)
4520                .ok_or_else(|| OpError::new(format!("the key {k:?} isn't in the map")))?;
4521            update(slot, rest, v, budget)
4522        }
4523        (PathKey::Field(name), other) => Err(OpError::new(format!(
4524            "can't set the field `{name}` of {}",
4525            ops::article(&other.kind())
4526        ))),
4527        (PathKey::Index(_), other) => Err(OpError::new(format!(
4528            "can't set an element of {}",
4529            ops::article(&other.kind())
4530        ))),
4531    }
4532}
4533
4534/// The value at `keys` in `target`, to change in place: each level is
4535/// copied only if something else holds it.
4536fn place_mut<'v>(target: &'v mut Value, keys: &[PathKey], budget: &mut Budget) -> OpResult<&'v mut Value> {
4537    let Some((first, rest)) = keys.split_first() else {
4538        return Ok(target);
4539    };
4540    match (first, target) {
4541        (PathKey::Field(name), Value::Record(r)) => {
4542            let slot = Arc::make_mut(r)
4543                .get_mut(name)
4544                .ok_or_else(|| OpError::new(format!("this record has no field `{name}`")))?;
4545            place_mut(slot, rest, budget)
4546        }
4547        (PathKey::Index(i), Value::List(items)) => {
4548            let items = Arc::make_mut(items);
4549            let k = ops::as_index(i, items.len() as u128, budget)? as usize;
4550            place_mut(&mut items[k], rest, budget)
4551        }
4552        (PathKey::Index(k), Value::Map(m)) => {
4553            let slot = Arc::make_mut(m)
4554                .get_mut(k)
4555                .ok_or_else(|| OpError::new(format!("the key {k:?} isn't in the map")))?;
4556            place_mut(slot, rest, budget)
4557        }
4558        (PathKey::Field(name), other) => Err(OpError::new(format!(
4559            "can't set the field `{name}` of {}",
4560            ops::article(&other.kind())
4561        ))),
4562        (PathKey::Index(_), other) => Err(OpError::new(format!(
4563            "can't set an element of {}",
4564            ops::article(&other.kind())
4565        ))),
4566    }
4567}
4568
4569/// Whether observing a value from `d` uses a density.
4570fn is_density(d: &Value) -> bool {
4571    match d {
4572        Value::Continuous(_) => true,
4573        Value::Dist(dist) => dist.outcomes.iter().any(|(x, _)| matches!(x, Value::Continuous(_))),
4574        _ => false,
4575    }
4576}
4577
4578/// Whether a value is a discrete outcome of a draw, like `floor(x)`. Out of
4579/// line: assignments run it on every analytic value they store.
4580#[cold]
4581#[inline(never)]
4582fn is_discrete(v: &Value) -> bool {
4583    matches!(v, Value::Analytic(a) if a.is_discrete())
4584}
4585
4586/// Whether a built-in deals with these analytic arguments deliberately.
4587/// The others would look inside them as if they were plain values.
4588fn handles_analytic(b: Builtin, values: &[Value]) -> bool {
4589    use Builtin as B;
4590    let plain = |v: &Value| !analytic::contains(v);
4591    let number = |v: &Value| matches!(v, Value::Analytic(_) | Value::Int(_) | Value::Float(_) | Value::Prob(_));
4592    let numbers = |v: &Value| matches!(v, Value::List(xs) if xs.iter().all(number));
4593    match b {
4594        B::Map | B::Filter | B::Reduce | B::Len | B::IterItems | B::Settled | B::BooleanLaw | B::IsListOfLen => true,
4595        // They move values without looking at them.
4596        B::Slice
4597        | B::Reverse
4598        | B::Keys
4599        | B::Values
4600        | B::Enumerate
4601        | B::Zip
4602        | B::Push
4603        | B::Pop
4604        | B::Last
4605        | B::DropLast => true,
4606        B::Count => values.len() == 1,
4607        // A key is compared with others, so it can't be an outcome; an index
4608        // is a number, which an outcome isn't.
4609        B::Get | B::Insert | B::Remove => matches!(values[0], Value::List(_)) || plain(&values[1]),
4610        // Affine in one draw, piecewise affine.
4611        B::Sum | B::Mean => numbers(&values[0]),
4612        B::Floor | B::Ceil | B::Trunc => matches!(values[0], Value::Analytic(_)),
4613        B::Sqrt | B::Exp | B::Ln | B::Exp2 | B::Log10 | B::Log2 | B::Log1p | B::Expm1 => {
4614            matches!(values[0], Value::Analytic(_))
4615        }
4616        B::Round => matches!(values[0], Value::Analytic(_)) && values[1..].iter().all(plain),
4617        B::Abs | B::Min | B::Max | B::Clamp => values
4618            .iter()
4619            .all(|v| number(v) || matches!(v, Value::Dist(_)) && plain(v)),
4620        B::Minimum | B::Maximum => values.len() == 1 && numbers(&values[0]),
4621        _ => false,
4622    }
4623}
4624
4625/// What observing `v` from `d` weighs.
4626enum Weighs {
4627    /// The probability of `v`, the probability that is missing from `d` (so
4628    /// the true value may be up to that much higher), and the probability
4629    /// of the other outcomes, which the observation rules out. When
4630    /// sampling, a continuous `d` gives its density here (section 13).
4631    Probability(f64, f64, f64),
4632    /// When enumerating, the logarithm of a continuous `d`'s density at `v`.
4633    LogDensity(f64),
4634}
4635
4636fn likelihood(d: &Value, v: &Value, sampling: bool) -> OpResult<Weighs> {
4637    let continuous = match d {
4638        Value::Continuous(f) => Some(vec![(**f, 1.0)]),
4639        Value::Dist(dist) if dist.outcomes.iter().any(|(x, _)| matches!(x, Value::Continuous(_))) => {
4640            let parts: Option<Vec<_>> = dist
4641                .outcomes
4642                .iter()
4643                .map(|(x, p)| match x {
4644                    Value::Continuous(f) => Some((**f, *p)),
4645                    _ => None,
4646                })
4647                .collect();
4648            let parts = parts.ok_or_else(|| {
4649                OpError::new("`observe … from` can't mix a density with the probabilities of single values")
4650            })?;
4651            Some(parts)
4652        }
4653        _ => None,
4654    };
4655    if let Some(parts) = continuous {
4656        if let Value::Dist(dist) = d {
4657            if dist.missing > 0.0 && !sampling {
4658                return Err(OpError::unsupported(
4659                    "observing a value from a mixture with unresolved probability isn't supported when enumerating",
4660                )
4661                .help("the unresolved part could have any density at the value; sample the model with `@mode sample(runs: 10_000)`"));
4662            }
4663        }
4664        let x = match v {
4665            Value::Bool(_) => None,
4666            _ => v.as_f64(),
4667        }
4668        .ok_or_else(|| {
4669            OpError::new(format!(
4670                "a continuous distribution can't produce {}",
4671                ops::article(&v.kind())
4672            ))
4673        })?;
4674        if !sampling {
4675            return Ok(Weighs::LogDensity(crate::stats::log_sum_exp(
4676                parts.iter().map(|(f, p)| libm::log(*p) + f.ln_pdf(x)),
4677            )));
4678        }
4679        return Ok(Weighs::Probability(
4680            parts.iter().map(|(f, p)| p * f.pdf(x)).sum(),
4681            0.0,
4682            0.0,
4683        ));
4684    }
4685    match d {
4686        // As observing compares values: `2.0` is the outcome 2.
4687        Value::Counts(law) => {
4688            let x = match v.as_complex() {
4689                Some(z) if z.im() == 0.0 => z.re(),
4690                _ => f64::NAN,
4691            };
4692            let p = if x.is_nan() { 0.0 } else { law.pmf(x) };
4693            Ok(Weighs::Probability(p, 0.0, 1.0 - p))
4694        }
4695        Value::Dist(dist) => {
4696            let (mut seen, mut other) = (0.0, 0.0);
4697            for (x, p) in &dist.outcomes {
4698                if ops::equals(x, v) {
4699                    seen += p;
4700                } else {
4701                    other += p;
4702                }
4703            }
4704            Ok(Weighs::Probability(seen, dist.missing, other))
4705        }
4706        Value::Prob(_) => Err(OpError::new("`observe … from` needs a distribution, not a probability")
4707            .help("to observe that a fact with probability p is true, write `observe true from bernoulli(p)`")),
4708        other => Ok(if ops::equals(other, v) {
4709            Weighs::Probability(1.0, 0.0, 0.0)
4710        } else {
4711            Weighs::Probability(0.0, 0.0, 1.0)
4712        }),
4713    }
4714}