1use std::collections::{BTreeSet, HashMap};
4
5use sva_formula::{Held, NodeId, Var};
6
7use crate::cast::Cast;
8use crate::error::EngineError;
9use crate::instantiate::Instances;
10use crate::query::Ask;
11use crate::typing::{Typing, Value, When};
12
13pub struct Order<'i> {
15 pub groups: Vec<Vec<String>>,
16 inst: &'i Instances,
17}
18
19impl<'i> Order<'i> {
20 pub fn deps(&self, path: &str) -> &'i [String] {
21 self.inst.deps(path)
22 }
23
24 pub fn is_loop(&self, group: &[String]) -> bool {
26 is_loop(self.inst, group)
27 }
28}
29
30pub(crate) fn is_loop(inst: &Instances, group: &[String]) -> bool {
31 match group {
32 [only] => inst.deps(only).iter().any(|d| d == only),
33 _ => true,
34 }
35}
36
37struct Frame {
38 node: String,
39 refs: Vec<String>,
40 idx: usize,
41}
42
43pub fn schedule_from<'i>(inst: &'i Instances, roots: &[String]) -> Result<Order<'i>, EngineError> {
45 let mut walk = Walk {
46 inst,
47 within: &|_| true,
48 index: HashMap::new(),
49 low: HashMap::new(),
50 open: Vec::new(),
51 next: 0,
52 groups: Vec::new(),
53 };
54 for root in roots {
55 if !inst.holds(root) {
56 return Err(EngineError::UnknownNode(root.to_string()));
57 }
58 walk.from(root);
59 }
60 Ok(Order {
61 groups: walk.groups,
62 inst,
63 })
64}
65
66pub(crate) fn grouped(
68 inst: &Instances,
69 starts: &[String],
70 region: &dyn Fn(&str) -> bool,
71) -> Vec<Vec<String>> {
72 let mut walk = Walk {
73 inst,
74 within: region,
75 index: HashMap::new(),
76 low: HashMap::new(),
77 open: Vec::new(),
78 next: 0,
79 groups: Vec::new(),
80 };
81 for start in starts.iter().filter(|s| region(s)) {
82 walk.from(start);
83 }
84 walk.groups
85}
86
87struct Walk<'a> {
88 inst: &'a Instances,
89 within: &'a dyn Fn(&str) -> bool,
90 index: HashMap<String, usize>,
91 low: HashMap<String, usize>,
92 open: Vec<String>,
93 next: usize,
94 groups: Vec<Vec<String>>,
95}
96
97impl Walk<'_> {
98 fn reads(&self, node: &str) -> Vec<String> {
99 let reads = self.inst.deps(node).iter();
100 reads.filter(|read| (self.within)(read)).cloned().collect()
101 }
102
103 fn from(&mut self, root: &str) {
104 if self.index.contains_key(root) {
105 return;
106 }
107 self.index.insert(root.to_string(), self.next);
108 self.low.insert(root.to_string(), self.next);
109 self.next += 1;
110 self.open.push(root.to_string());
111 let seed = self.reads(root);
112 let mut stack = vec![Frame {
113 node: root.to_string(),
114 refs: seed,
115 idx: 0,
116 }];
117
118 while let Some(frame) = stack.last_mut() {
119 if frame.idx < frame.refs.len() {
120 let target = frame.refs[frame.idx].clone();
121 let node = frame.node.clone();
122 frame.idx += 1;
123 match self.index.get(&target).copied() {
124 None => {
125 self.index.insert(target.clone(), self.next);
126 self.low.insert(target.clone(), self.next);
127 self.next += 1;
128 self.open.push(target.clone());
129 let refs = self.reads(&target);
130 stack.push(Frame {
131 node: target,
132 refs,
133 idx: 0,
134 });
135 }
136 Some(at) if self.open.contains(&target) => {
137 let mine = self.low[&node];
138 self.low.insert(node, mine.min(at));
139 }
140 Some(_) => {}
141 }
142 continue;
143 }
144
145 let node = frame.node.clone();
146 let mine = self.low[&node];
147 stack.pop();
148 if let Some(parent) = stack.last() {
149 let above = self.low[&parent.node];
150 self.low.insert(parent.node.clone(), above.min(mine));
151 }
152 if mine == self.index[&node] {
153 let at = self
154 .open
155 .iter()
156 .rposition(|n| *n == node)
157 .expect("a root of its group is still open");
158 let mut group = self.open.split_off(at);
159 group.sort();
160 self.groups.push(group);
161 }
162 }
163 }
164}
165
166#[derive(Clone, Debug, Default, PartialEq)]
168pub struct Schedule {
169 pub wanted: Vec<NodeId>,
171 pub compose: Vec<NodeId>,
173}
174
175pub fn plan(typing: &Typing, root: NodeId, asks: &[Ask]) -> Schedule {
177 let mut wanted: BTreeSet<NodeId> = BTreeSet::new();
178 let mut compose: Vec<NodeId> = Vec::new();
179 let audio = asks.is_empty();
180 for ask in asks {
181 let Some(id) = typing.id(&ask.node) else {
182 continue;
183 };
184 if ask.representation.reading().reads == crate::query::Reads::Structure {
185 continue;
186 }
187 match ask.representation.consumes(typing.ty(id).is_closed_form()) {
188 sva_samples::Consumes::ClosedForm if !compose.contains(&id) => compose.push(id),
189 sva_samples::Consumes::ClosedForm => {}
190 _ => {
191 wanted.insert(id);
192 }
193 }
194 if let crate::query::Representation::Ledger { depth } = ask.representation {
195 attributed(typing, id, depth, &mut wanted);
196 }
197 }
198 if audio || !wanted.is_empty() {
199 wanted.insert(root);
200 }
201 compose.retain(|id| !wanted.contains(id));
202 Schedule {
203 wanted: wanted.into_iter().collect(),
204 compose,
205 }
206}
207
208fn attributed(typing: &Typing, id: NodeId, depth: usize, wanted: &mut BTreeSet<NodeId>) {
210 let mut seen = BTreeSet::from([id]);
212 let mut level = vec![id];
213 for _ in 0..depth {
214 let mut next = Vec::new();
215 while let Some(held) = level.pop() {
216 for operand in read_operands(typing, held) {
217 if !seen.insert(operand) {
218 continue;
219 }
220 wanted.insert(operand);
221 match typing.name(operand) == typing.name(held) {
222 true => level.push(operand),
223 false => next.push(operand),
224 }
225 }
226 }
227 level = next;
228 }
229}
230
231pub(crate) fn holds_self(typing: &Typing, id: NodeId, seen: &mut BTreeSet<NodeId>) -> bool {
234 if !seen.insert(id) {
235 return false;
236 }
237 match typing.value(id) {
238 Value::SelfAt { .. } => true,
239 Value::Cast(Cast::Sample, _) | Value::Read { .. } => false,
240 Value::Cast(_, source) => holds_self(typing, *source, seen),
241 Value::Op { args, .. } => args.iter().any(|a| holds_self(typing, *a, seen)),
242 Value::Filter {
243 x, cutoff, q, gain, ..
244 } => [x, cutoff, q, gain]
245 .into_iter()
246 .any(|operand| holds_self(typing, *operand, seen)),
247 Value::Solver { varying, .. } => varying.iter().any(|(_, a)| holds_self(typing, *a, seen)),
248 Value::ClosedForm(_) | Value::Noise(_) => false,
249 }
250}
251
252pub(crate) fn materialized_operands(typing: &Typing, id: NodeId) -> Vec<NodeId> {
255 let sampled = |set: Vec<NodeId>| -> Vec<NodeId> {
256 let mut out = Vec::new();
257 for op in set {
258 if typing.ty(op).is_closed_form() {
259 continue;
260 }
261 let inlined = matches!(
262 typing.value(op),
263 Value::Op { .. } | Value::Filter { .. } | Value::Read { .. }
264 );
265 match inlined || holds_self(typing, op, &mut BTreeSet::new()) {
266 true => out.extend(materialized_operands(typing, op)),
267 false => out.push(op),
268 }
269 }
270 out
271 };
272 match typing.value(id) {
273 Value::ClosedForm(_) | Value::Noise(_) => Vec::new(),
274 Value::SelfAt { at, .. } => sampled(at.moving()),
275 Value::Solver { varying, .. } => sampled(varying.iter().map(|(_, a)| *a).collect()),
276 Value::Cast(Cast::Sample, source) => vec![*source],
277 Value::Read { source, at, .. } => {
278 let mut out = match (at, anywhere(typing, *source)) {
279 (When::Moving(_) | When::Step(_), true) => Vec::new(),
280 _ => vec![*source],
281 };
282 out.extend(sampled(at.moving()));
283 out
284 }
285 Value::Cast(_, source) => sampled(vec![*source]),
286 Value::Op { args, .. } => sampled(args.clone()),
287 Value::Filter {
288 x, cutoff, q, gain, ..
289 } => sampled(vec![*x, *cutoff, *q, *gain]),
290 }
291}
292
293pub(crate) fn anywhere(typing: &Typing, id: NodeId) -> bool {
295 match typing.value(id) {
296 Value::Noise(_) => true,
297 Value::Cast(Cast::Sample, of) => typing.ty(*of).held == Held::Form(Var::T),
298 Value::ClosedForm(form) => form.var == Var::T,
299 _ => false,
300 }
301}
302
303pub(crate) fn read_operands(typing: &Typing, id: NodeId) -> Vec<NodeId> {
305 match typing.value(id) {
306 Value::ClosedForm(form) => crate::refs::nodes_in(&form.body),
307 _ if typing.ty(id).is_closed_form() => {
308 let mut out = Vec::new();
309 reads_under(typing, id, &mut BTreeSet::new(), &mut out);
310 out.dedup();
311 out
312 }
313 _ => materialized_operands(typing, id),
314 }
315}
316
317fn reads_under(typing: &Typing, id: NodeId, seen: &mut BTreeSet<NodeId>, out: &mut Vec<NodeId>) {
319 if !seen.insert(id) {
320 return;
321 }
322 match typing.value(id) {
323 Value::ClosedForm(form) => out.extend(crate::refs::nodes_in(&form.body)),
324 Value::Read { source, .. } => out.push(*source),
325 Value::Cast(_, source) => read_through(typing, *source, seen, out),
326 Value::Op { args, .. } => {
327 for arg in args {
328 read_through(typing, *arg, seen, out);
329 }
330 }
331 Value::Filter {
332 x, cutoff, q, gain, ..
333 } => {
334 for operand in [x, cutoff, q, gain] {
335 read_through(typing, *operand, seen, out);
336 }
337 }
338 Value::Solver { varying, .. } => {
339 for (_, arg) in varying {
340 read_through(typing, *arg, seen, out);
341 }
342 }
343 Value::SelfAt { .. } | Value::Noise(_) => {}
344 }
345}
346
347fn read_through(typing: &Typing, id: NodeId, seen: &mut BTreeSet<NodeId>, out: &mut Vec<NodeId>) {
349 let Value::ClosedForm(_) = typing.value(id) else {
350 return reads_under(typing, id, seen, out);
351 };
352 if seen.insert(id) {
353 out.push(id);
354 }
355}