Skip to main content

sva_samples/machine/
live.rs

1// Concern: cuts one renderer into spans, each compiled without the reads and crops zero there | Non-concern: where a read is zero, running a span | IO: (NodeRenderer, supports) -> Spanned
2
3use super::Program;
4use super::ops::{self, Layout, lowered};
5use super::renderer::{Formula, Index, NodeRenderer, Slot};
6use crate::collapse::Extent;
7use crate::error::SampleError;
8
9#[derive(Clone)]
10pub struct Span {
11    pub from: i64,
12    pub to: i64,
13    pub renderer: NodeRenderer,
14}
15
16/// A renderer cut into spans each pruned of what is exact zero there: a sample's program
17/// depends on its index alone.
18#[derive(Clone)]
19pub struct Spanned {
20    spans: Vec<(Span, Program)>,
21    layout: Layout,
22}
23
24impl Spanned {
25    /// Outside `live[k]` read `k` answers exactly +0.0.
26    pub fn new(
27        renderer: &NodeRenderer,
28        layout: &Layout,
29        (from, to): (i64, i64),
30        live: &[Extent],
31    ) -> Result<Spanned, SampleError> {
32        let spans = renderer
33            .spans(layout, (from, to), live)?
34            .into_iter()
35            .map(|span| {
36                let program = span.renderer.compile(layout)?;
37                Ok((span, program))
38            })
39            .collect::<Result<_, SampleError>>()?;
40        Ok(Spanned {
41            spans,
42            layout: layout.clone(),
43        })
44    }
45
46    pub fn spans(&self) -> impl Iterator<Item = &Span> {
47        self.spans.iter().map(|(span, _)| span)
48    }
49
50    pub(super) fn grid(&self) -> super::renderer::Grid {
51        self.layout.grid
52    }
53
54    pub(super) fn compiled(&self) -> &[(Span, Program)] {
55        &self.spans
56    }
57
58    /// A program writing +0 where no span reaches.
59    pub(super) fn silent(&self) -> Result<Program, SampleError> {
60        let zero = match self.layout.width {
61            0 | 1 => NodeRenderer::Const(0.0),
62            w => NodeRenderer::Join(vec![NodeRenderer::Const(0.0); w]),
63        };
64        zero.compile(&self.layout)
65    }
66
67    /// One per op over `[from, to)`, span by span, and each formula's own.
68    pub fn ops(&self, from: i64, to: i64) -> u128 {
69        self.spans
70            .iter()
71            .map(|(span, program)| {
72                let n = (to.min(span.to) - from.max(span.from)).max(0) as u128;
73                let formulas: usize = program.formulas.iter().map(Formula::ops).sum();
74                n * (program.ops.len() + formulas) as u128
75            })
76            .sum()
77    }
78}
79
80impl NodeRenderer {
81    /// `[from, to)` cut where the dead reads or shut crops change, each span pruned; any cut
82    /// of a run writes the same bits.
83    pub fn spans(
84        &self,
85        layout: &Layout,
86        (from, to): (i64, i64),
87        live: &[Extent],
88    ) -> Result<Vec<Span>, SampleError> {
89        let mut root = Cut::of(self, layout, live)?;
90        let mut edges = vec![from, to];
91        root.edges(&mut edges);
92        edges.retain(|e| from <= *e && *e <= to);
93        edges.sort_unstable();
94        edges.dedup();
95        let mut out: Vec<Span> = Vec::new();
96        for pair in edges.windows(2) {
97            let span = Extent::new(pair[0], pair[1]);
98            let changed = root.prune(span, layout)?;
99            match out.last_mut() {
100                Some(last) if !changed => last.to = span.end,
101                last => {
102                    let renderer = root.made();
103                    match last {
104                        Some(last) if last.renderer == renderer => last.to = span.end,
105                        _ => out.push(Span {
106                            from: span.start,
107                            to: span.end,
108                            renderer,
109                        }),
110                    }
111                }
112            }
113        }
114        Ok(out)
115    }
116
117    /// Each index read no reach bounds held to the samples up to the one being written, all a
118    /// machine stepping beside its source has, where `beside` names that source.
119    pub fn stepwise(&self, beside: &dyn Fn(Slot) -> bool) -> NodeRenderer {
120        match self {
121            NodeRenderer::Indexed {
122                slot,
123                index,
124                reach: None,
125            } if beside(*slot) => NodeRenderer::Indexed {
126                slot: *slot,
127                index: index.clone(),
128                reach: Some((i64::MIN, 0)),
129            },
130            other => rebuilt(other, &mut |p| p.stepwise(beside)),
131        }
132    }
133
134    /// One per op, and a formula's own.
135    pub fn ops(&self, layout: &Layout) -> Result<usize, SampleError> {
136        let (lowered, _) = lowered(self, layout)?;
137        let formulas: usize = lowered.formulas.iter().map(|f| f.ops()).sum();
138        Ok(lowered.ops.len() + formulas)
139    }
140}
141
142/// One node of the renderer, its width settled once, and what pruning made of it over the
143/// latest span; that holds over every later span ending by `until`, since only the reads
144/// and crops under it decide it.
145struct Cut<'r> {
146    node: &'r NodeRenderer,
147    width: usize,
148    operands: Vec<Cut<'r>>,
149    /// Where a read of another node can be nonzero, in this node's samples.
150    reach: Option<Extent>,
151    until: i64,
152    made: Made,
153    held: Held,
154}
155
156#[derive(Clone, PartialEq)]
157enum Made {
158    Kept,
159    /// Exact +0 in each component.
160    Zeros,
161    /// A sum over only these operands.
162    Terms(Vec<usize>),
163    /// A difference whose subtrahend is exact zero, as its minuend.
164    Minuend,
165}
166
167/// What the node as made holds over the span.
168#[derive(Default)]
169struct Held {
170    /// Exactly +0.0 at every sample.
171    zero: bool,
172    /// Exactly +0.0 or -0.0 at every sample, which a sum starting from +0 drops alike.
173    nil: bool,
174    stateless: bool,
175    /// Each component where every sample is the same number.
176    constant: Option<Vec<f64>>,
177}
178
179impl<'r> Cut<'r> {
180    fn of(
181        node: &'r NodeRenderer,
182        layout: &Layout,
183        live: &[Extent],
184    ) -> Result<Cut<'r>, SampleError> {
185        let operands = node
186            .operands()
187            .into_iter()
188            .map(|p| Cut::of(p, layout, live))
189            .collect::<Result<Vec<_>, _>>()?;
190        let widths: Vec<usize> = operands.iter().map(|o| o.width).collect();
191        let reach = match node {
192            NodeRenderer::Read {
193                slot: Slot::Read(id),
194                map,
195            } => Some(map.preimage(live[id.0 as usize])),
196            _ => None,
197        };
198        Ok(Cut {
199            node,
200            width: ops::width(node, &widths, layout)?,
201            operands,
202            reach,
203            until: i64::MIN,
204            made: Made::Kept,
205            held: Held::default(),
206        })
207    }
208
209    fn edges(&self, out: &mut Vec<i64>) {
210        if let Some(reach) = self.reach {
211            out.extend([reach.start, reach.end]);
212        }
213        if let NodeRenderer::Crop { window, .. } = self.node {
214            out.extend([window.0, window.1]);
215        }
216        for o in &self.operands {
217            o.edges(out);
218        }
219    }
220
221    /// Re-prunes what `span` passes the `until` of, bottom-up; whether what it makes changed.
222    fn prune(&mut self, span: Extent, layout: &Layout) -> Result<bool, SampleError> {
223        if span.end <= self.until {
224            return Ok(false);
225        }
226        let mut below = false;
227        for o in &mut self.operands {
228            below |= o.prune(span, layout)?;
229        }
230        let mut until = self
231            .operands
232            .iter()
233            .map(|o| o.until)
234            .min()
235            .unwrap_or(i64::MAX);
236        let over = |e: Extent| {
237            [e.start, e.end]
238                .into_iter()
239                .filter(|&x| x > span.start)
240                .min()
241        };
242        let shut = match self.node {
243            NodeRenderer::Crop { window, .. } => {
244                let open = Extent::new(window.0.min(window.1), window.1);
245                until = until.min(over(open).unwrap_or(i64::MAX));
246                open.intersect(span).is_empty()
247            }
248            _ => false,
249        };
250        let dead = self.reach.is_some_and(|reach| {
251            until = until.min(over(reach).unwrap_or(i64::MAX));
252            reach.intersect(span).is_empty()
253        });
254        let made = self.decided(shut, layout)?;
255        let changed = self.until == i64::MIN || made != self.made || (below && made != Made::Zeros);
256        self.held = self.holding(&made, shut, dead);
257        self.made = made;
258        self.until = until;
259        Ok(changed)
260    }
261
262    /// A product with a factor exact zero over the span, every other factor holding no
263    /// state, is exact +0 there whatever those factors hold: its zero factor is outside its
264    /// support. A sum drops the terms that are exact zero where that keeps its width.
265    fn decided(&self, shut: bool, layout: &Layout) -> Result<Made, SampleError> {
266        let ops = &self.operands;
267        Ok(match self.node {
268            NodeRenderer::Crop { .. } if shut && ops[0].held.stateless => Made::Zeros,
269            NodeRenderer::Mul(_)
270                if ops.iter().any(|o| o.held.zero) && ops.iter().all(|o| o.held.stateless) =>
271            {
272                Made::Zeros
273            }
274            NodeRenderer::Add(_) => {
275                let live: Vec<usize> = (0..ops.len()).filter(|&k| !ops[k].held.nil).collect();
276                let widths: Vec<usize> = live.iter().map(|&k| ops[k].width).collect();
277                match live.is_empty() {
278                    true if self.width == 1 => Made::Zeros,
279                    false if live.len() == ops.len() => Made::Kept,
280                    false if ops::width(self.node, &widths, layout)? == self.width => {
281                        Made::Terms(live)
282                    }
283                    _ => Made::Kept,
284                }
285            }
286            NodeRenderer::Sub(..) if ops[1].held.zero && ops[0].width == self.width => {
287                Made::Minuend
288            }
289            _ => Made::Kept,
290        })
291    }
292
293    fn holding(&self, made: &Made, shut: bool, dead: bool) -> Held {
294        let ops = &self.operands;
295        let of = |k: usize| &ops[k].held;
296        match made {
297            Made::Zeros => Held {
298                zero: true,
299                nil: true,
300                stateless: true,
301                constant: Some(vec![0.0; self.width]),
302            },
303            Made::Minuend => Held {
304                zero: of(0).zero,
305                nil: of(0).nil,
306                stateless: of(0).stateless,
307                constant: of(0).constant.clone(),
308            },
309            Made::Terms(live) => {
310                let zero = live.iter().all(|&k| of(k).nil);
311                Held {
312                    zero,
313                    nil: zero,
314                    stateless: live.iter().all(|&k| of(k).stateless),
315                    constant: fold(live.iter().map(|&k| of(k)), 0.0, |a, b| a + b),
316                }
317            }
318            Made::Kept => {
319                let all = |f: fn(&Held) -> bool| ops.iter().all(|o| f(&o.held));
320                let zero = match self.node {
321                    NodeRenderer::Read {
322                        slot: Slot::Read(_),
323                        ..
324                    } => dead,
325                    NodeRenderer::Const(v) => v.to_bits() == 0,
326                    NodeRenderer::Crop { .. } => shut || of(0).zero,
327                    NodeRenderer::Add(_) => all(|h| h.nil),
328                    NodeRenderer::Join(_) => all(|h| h.zero),
329                    NodeRenderer::Sub(..) => of(0).zero && of(1).zero,
330                    _ => false,
331                };
332                let nil = match self.node {
333                    NodeRenderer::Mul(_) => match ops.iter().position(|o| o.held.nil) {
334                        None => false,
335                        Some(at) => {
336                            finite(fold(ops[..at].iter().map(|o| &o.held), 1.0, |a, b| a * b))
337                                && ops[at + 1..]
338                                    .iter()
339                                    .all(|o| o.held.nil || finite(o.held.constant.clone()))
340                        }
341                    },
342                    NodeRenderer::Crop { .. } => zero || of(0).nil,
343                    _ => zero,
344                };
345                let held = ops.iter().map(|o| &o.held);
346                let constant = match self.node {
347                    NodeRenderer::Const(v) => Some(vec![*v]),
348                    NodeRenderer::Join(_) => {
349                        held.map(|h| h.constant.clone())
350                            .try_fold(Vec::new(), |mut all, c| {
351                                all.extend(c?);
352                                Some(all)
353                            })
354                    }
355                    NodeRenderer::Add(_) => fold(held, 0.0, |a, b| a + b),
356                    NodeRenderer::Mul(_) => fold(held, 1.0, |a, b| a * b),
357                    _ => None,
358                };
359                Held {
360                    zero,
361                    nil,
362                    stateless: !self.node.holds_state() && all(|h| h.stateless),
363                    constant,
364                }
365            }
366        }
367    }
368
369    fn made(&self) -> NodeRenderer {
370        match &self.made {
371            Made::Zeros => match self.width {
372                1 => NodeRenderer::Const(0.0),
373                w => NodeRenderer::Join(vec![NodeRenderer::Const(0.0); w]),
374            },
375            Made::Minuend => self.operands[0].made(),
376            Made::Terms(live) => {
377                NodeRenderer::Add(live.iter().map(|&k| self.operands[k].made()).collect())
378            }
379            Made::Kept => {
380                let mut each = self.operands.iter();
381                rebuilt(self.node, &mut |_| {
382                    each.next().expect("one per operand").made()
383                })
384            }
385        }
386    }
387}
388
389fn finite(value: Option<Vec<f64>>) -> bool {
390    value.is_some_and(|v| v.iter().all(|x| x.is_finite()))
391}
392
393/// As the machine folds operands, each component from `start`, a mono operand read at every one.
394fn fold<'h>(
395    set: impl Iterator<Item = &'h Held>,
396    start: f64,
397    op: fn(f64, f64) -> f64,
398) -> Option<Vec<f64>> {
399    let values = set
400        .map(|h| h.constant.as_deref())
401        .collect::<Option<Vec<_>>>()?;
402    let width = values.iter().map(|v| v.len()).max().unwrap_or(1);
403    Some(
404        (0..width)
405            .map(|c| {
406                values
407                    .iter()
408                    .fold(start, |acc, v| op(acc, super::part(v, c)))
409            })
410            .collect(),
411    )
412}
413
414fn rebuilt(r: &NodeRenderer, each: &mut dyn FnMut(&NodeRenderer) -> NodeRenderer) -> NodeRenderer {
415    let mut one = |p: &NodeRenderer| Box::new(each(p));
416    match r {
417        NodeRenderer::Add(set) => NodeRenderer::Add(set.iter().map(&mut *each).collect()),
418        NodeRenderer::Mul(set) => NodeRenderer::Mul(set.iter().map(&mut *each).collect()),
419        NodeRenderer::Join(set) => NodeRenderer::Join(set.iter().map(&mut *each).collect()),
420        NodeRenderer::Sub(a, b) => NodeRenderer::Sub(one(a), one(b)),
421        NodeRenderer::Div(a, b) => NodeRenderer::Div(one(a), one(b)),
422        NodeRenderer::Pow(a, b) => NodeRenderer::Pow(one(a), one(b)),
423        NodeRenderer::Zip(f, a, b) => NodeRenderer::Zip(*f, one(a), one(b)),
424        NodeRenderer::Map(f, x) => NodeRenderer::Map(*f, one(x)),
425        NodeRenderer::Channel { x, k } => NodeRenderer::Channel { x: one(x), k: *k },
426        NodeRenderer::Crop {
427            x,
428            window,
429            a,
430            b,
431            rise,
432            fall,
433        } => NodeRenderer::Crop {
434            x: one(x),
435            window: *window,
436            a: *a,
437            b: *b,
438            rise: *rise,
439            fall: *fall,
440        },
441        NodeRenderer::Filter {
442            site,
443            from,
444            x,
445            cutoff,
446            q,
447            gain,
448        } => NodeRenderer::Filter {
449            site: *site,
450            from: *from,
451            x: one(x),
452            cutoff: one(cutoff),
453            q: one(q),
454            gain: one(gain),
455        },
456        NodeRenderer::Physics { site, from, args } => NodeRenderer::Physics {
457            site: *site,
458            from: *from,
459            args: args.iter().map(&mut *each).collect(),
460        },
461        NodeRenderer::Formula {
462            formula,
463            width,
464            time,
465        } => NodeRenderer::Formula {
466            formula: formula.clone(),
467            width: *width,
468            time: one(time),
469        },
470        NodeRenderer::Indexed { slot, index, reach } => NodeRenderer::Indexed {
471            slot: *slot,
472            index: held(index, &mut one),
473            reach: *reach,
474        },
475        NodeRenderer::Instant(index) => NodeRenderer::Instant(held(index, &mut one)),
476        leaf => leaf.clone(),
477    }
478}
479
480fn held(index: &Index, one: &mut dyn FnMut(&NodeRenderer) -> Box<NodeRenderer>) -> Index {
481    let mapped = index.mapped(&mut |t| Ok::<_, std::convert::Infallible>(one(t)));
482    match mapped {
483        Ok(index) => index,
484    }
485}