1mod draft;
4mod folds;
5
6use std::collections::{BTreeMap, BTreeSet};
7use std::sync::Arc;
8
9use sva_formula::filter::Shape;
10use sva_formula::{ClosedForm, Codomain, Env, Held, NodeId, Origin, ParamId, Ty, Var, infer};
11use sva_samples::Params;
12
13use crate::arguments::{Arguments, Called, Chosen};
14use crate::cast::Cast;
15use crate::error::{Diagnostic, EngineError, Located};
16use crate::instantiate::Instances;
17use crate::lower;
18use crate::schedule::Order;
19use crate::time::Grid;
20
21use draft::{Draft, Entry, Units};
22
23#[derive(Clone, Debug, PartialEq)]
25pub enum Value {
26 ClosedForm(ClosedForm),
27 Cast(Cast, NodeId),
28 Op {
29 name: String,
30 args: Vec<NodeId>,
31 },
32 SelfAt {
33 at: When,
34 },
35 Read {
36 source: NodeId,
37 at: When,
38 site: Origin,
39 },
40 Noise(u64),
41 Recorded(std::sync::Arc<sva_ast::Recording>),
43 Filter {
45 shape: Shape,
46 x: NodeId,
47 cutoff: NodeId,
48 q: NodeId,
49 gain: NodeId,
50 },
51 Solver {
52 params: Box<Params>,
53 varying: Vec<(&'static str, NodeId)>,
54 },
55}
56
57#[derive(Clone, Debug, PartialEq)]
60pub enum When {
61 At(crate::time::Affine),
62 Moving(NodeId),
63 Index(crate::index::Index),
64 Step(Step),
65}
66
67#[derive(Clone, Debug, PartialEq)]
70pub enum Step {
71 Index(crate::index::Index),
72 Nearest(NodeId, crate::index::Round),
73 Add(Vec<Step>),
74 Neg(Box<Step>),
75 Mul(Vec<Step>),
76}
77
78impl Step {
79 pub(crate) fn times(&self, out: &mut Vec<NodeId>) {
81 match self {
82 Step::Index(_) => {}
83 Step::Nearest(time, _) => out.push(*time),
84 Step::Add(parts) | Step::Mul(parts) => parts.iter().for_each(|p| p.times(out)),
85 Step::Neg(p) => p.times(out),
86 }
87 }
88}
89
90impl When {
91 pub(crate) fn moving(&self) -> Vec<NodeId> {
93 let mut out = Vec::new();
94 match self {
95 When::Moving(id) => out.push(*id),
96 When::Step(step) => step.times(&mut out),
97 When::At(_) | When::Index(_) => {}
98 }
99 out
100 }
101}
102
103#[derive(Clone, Debug, PartialEq)]
104pub struct Node {
105 pub name: Arc<str>,
106 pub ty: Ty,
107 pub var: Var,
108 pub value: Value,
109 pub grid: Grid,
110}
111
112#[derive(Clone, Debug, Default, PartialEq)]
114pub struct Typing {
115 nodes: Vec<Option<Node>>,
116 free: Vec<u32>,
117 arguments: BTreeMap<String, Arguments>,
118 by_path: BTreeMap<String, NodeId>,
119 aliases: BTreeSet<String>,
121 copies: BTreeMap<String, BTreeMap<Grid, NodeId>>,
122 lowering: BTreeSet<String>,
123 files: BTreeMap<String, Vec<NodeId>>,
124 origins: Vec<Option<(u32, Option<sva_ast::ByteSpan>)>>,
125 free_origins: Vec<u32>,
126 sites: Vec<Option<(String, u32)>>,
128 free_sites: Vec<u32>,
129 site_ids: BTreeMap<String, u32>,
130 pending: BTreeSet<NodeId>,
131 indices: u32,
132 sum: Option<(NodeId, Vec<SumSlot>)>,
133 folds: folds::Folds,
134 readers: BTreeMap<NodeId, BTreeSet<NodeId>>,
136 lowered: Vec<String>,
138 units: BTreeMap<String, Units>,
139 making: Vec<(Arc<str>, Grid)>,
140 draft: Draft,
141}
142
143#[derive(Clone, Copy, Debug, PartialEq)]
145pub(crate) enum SumSlot {
146 Node(NodeId),
147 Retired(sva_samples::Extent),
148}
149
150impl Typing {
151 pub(crate) fn parted(&self) -> Typing {
154 let mut copy = self.clone();
155 copy.folds = self.folds.parted();
156 copy
157 }
158
159 pub(crate) fn name_sum(&mut self, node: NodeId, slots: Vec<SumSlot>) {
161 self.draft.sum = Some(Some((node, slots)));
162 self.forget([node]);
163 }
164
165 pub(crate) fn forget(&mut self, changed: impl IntoIterator<Item = NodeId>) {
167 let mut gone: BTreeSet<NodeId> = changed.into_iter().collect();
168 if gone.is_empty() {
169 return;
170 }
171 let mut open: Vec<NodeId> = gone.iter().copied().collect();
172 while let Some(at) = open.pop() {
173 let summed = self
174 .summed()
175 .filter(|(_, slots)| slots.contains(&SumSlot::Node(at)));
176 let held = self.readers.get(&at).into_iter().flatten().copied();
177 for reader in held.chain(summed.map(|(sum, _)| *sum)) {
178 if gone.insert(reader) {
179 open.push(reader);
180 }
181 }
182 }
183 self.folds.forget(&gone);
184 }
185
186 pub(super) fn place(&mut self, id: NodeId, node: Option<Node>) {
188 if self.nodes[id.0 as usize].is_some() {
189 for read in self.own_edges(id) {
190 if let Some(held) = self.readers.get_mut(&read)
191 && held.remove(&id)
192 && held.is_empty()
193 {
194 self.readers.remove(&read);
195 }
196 }
197 }
198 self.nodes[id.0 as usize] = node;
199 if self.nodes[id.0 as usize].is_some() {
200 for read in self.own_edges(id) {
201 self.readers.entry(read).or_default().insert(id);
202 }
203 }
204 }
205
206 pub(crate) fn readers_of(&self, id: NodeId) -> impl Iterator<Item = NodeId> + '_ {
208 self.readers.get(&id).into_iter().flatten().copied()
209 }
210
211 fn summed(&self) -> Option<&(NodeId, Vec<SumSlot>)> {
212 match &self.draft.sum {
213 Some(staged) => staged.as_ref(),
214 None => self.sum.as_ref(),
215 }
216 }
217
218 pub(crate) fn retired_sum(&self) -> Option<NodeId> {
220 let (sum, slots) = self.summed()?;
221 let retired = slots.iter().any(|slot| matches!(slot, SumSlot::Retired(_)));
222 retired.then_some(*sum)
223 }
224
225 pub(crate) fn operands(&self, id: NodeId) -> Vec<NodeId> {
227 match self.value(id) {
228 Value::ClosedForm(form) => crate::refs::nodes_in(&form.body),
229 Value::Cast(_, source) | Value::Read { source, .. } => vec![*source],
230 Value::Op { args, .. } => args.clone(),
231 Value::Filter {
232 x, cutoff, q, gain, ..
233 } => vec![*x, *cutoff, *q, *gain],
234 Value::Solver { varying, .. } => varying.iter().map(|(_, a)| *a).collect(),
235 Value::SelfAt { .. } | Value::Noise(_) | Value::Recorded(_) => Vec::new(),
236 }
237 }
238
239 pub(crate) fn reads(&self, id: NodeId, of: NodeId) -> bool {
241 let (mut open, mut seen) = (vec![id], BTreeSet::new());
242 while let Some(at) = open.pop() {
243 if at == of {
244 return true;
245 }
246 if seen.insert(at) {
247 open.extend(self.edges(at));
248 }
249 }
250 false
251 }
252
253 pub(crate) fn unfolded(&self, root: NodeId, held: impl Fn(NodeId) -> bool) -> Vec<NodeId> {
256 self.unfolded_over(root, |id| self.edges(id), held)
257 }
258
259 pub(crate) fn unfolded_over(
260 &self,
261 root: NodeId,
262 reads: impl Fn(NodeId) -> Vec<NodeId>,
263 held: impl Fn(NodeId) -> bool,
264 ) -> Vec<NodeId> {
265 let mut order = Vec::new();
266 let mut seen = BTreeSet::new();
267 let mut open = vec![(root, false)];
268 while let Some((at, read)) = open.pop() {
269 if read {
270 order.push(at);
271 continue;
272 }
273 if held(at) || !seen.insert(at) {
274 continue;
275 }
276 open.push((at, true));
277 open.extend(reads(at).into_iter().rev().map(|n| (n, false)));
278 }
279 order
280 }
281
282 fn own_edges(&self, id: NodeId) -> Vec<NodeId> {
284 let mut out = self.operands(id);
285 if let Value::Read { at, .. } | Value::SelfAt { at, .. } = self.value(id) {
286 out.extend(at.moving());
287 }
288 out
289 }
290
291 fn edges(&self, id: NodeId) -> Vec<NodeId> {
293 let mut out = self.own_edges(id);
294 let slots = self.sum_slots(id).unwrap_or_default().iter();
295 out.extend(slots.filter_map(|slot| match slot {
296 SumSlot::Node(read) if *read != id => Some(*read),
297 _ => None,
298 }));
299 out
300 }
301
302 pub(crate) fn sum_slots(&self, node: NodeId) -> Option<&[SumSlot]> {
303 self.summed()
304 .filter(|(held, _)| *held == node)
305 .map(|(_, slots)| slots.as_slice())
306 }
307
308 pub(crate) fn lowering(&mut self, path: &str) {
309 self.lowered.push(path.to_string());
310 }
311
312 pub(crate) fn lowered(&self) -> &[String] {
313 &self.lowered
314 }
315
316 pub(crate) fn next_index(&mut self) -> sva_formula::IndexId {
317 self.indices += 1;
318 sva_formula::IndexId(self.indices)
319 }
320
321 pub(crate) fn note(&mut self, node: &str, call: Option<Called>, chosen: Vec<Chosen>) {
323 let mut held = self.arguments(node).cloned().unwrap_or_else(|| Arguments {
324 node: node.to_string(),
325 ..Arguments::default()
326 });
327 if let Some(call) = call {
328 held.calls
329 .retain(|c| (c.at.start, &c.name) != (call.at.start, &call.name));
330 held.calls.push(call);
331 }
332 for one in chosen {
333 held.chosen.retain(|c| c.at.start != one.at.start);
334 held.chosen.push(one);
335 }
336 let old = self.draft.arguments.insert(node.to_string(), held);
337 self.draft.journal.push(Entry::Noted(node.to_string(), old));
338 }
339
340 pub fn arguments(&self, node: &str) -> Option<&Arguments> {
341 match self.draft.arguments.get(node) {
342 Some(held) => Some(held),
343 None if self.draft.hidden.contains(node) => None,
344 None => self.arguments.get(node),
345 }
346 }
347
348 pub fn ty(&self, n: NodeId) -> Ty {
349 self.at(n).ty
350 }
351
352 pub fn var(&self, n: NodeId) -> Var {
353 self.at(n).var
354 }
355
356 pub fn value(&self, n: NodeId) -> &Value {
357 &self.at(n).value
358 }
359
360 pub fn name(&self, n: NodeId) -> &str {
361 &self.at(n).name
362 }
363
364 pub fn grid(&self, n: NodeId) -> Grid {
365 self.at(n).grid
366 }
367
368 pub(crate) fn copy(&self, path: &str, grid: Grid) -> Option<NodeId> {
369 let key = (path.to_string(), grid);
370 match self.draft.copies.get(&key) {
371 Some(id) => Some(*id),
372 None if self.draft.hidden.contains(path) => None,
373 None => self
374 .copies
375 .get(path)
376 .and_then(|held| held.get(&grid))
377 .copied(),
378 }
379 }
380
381 pub(crate) fn copied(&mut self, path: &str, grid: Grid, id: NodeId) {
382 let key = (path.to_string(), grid);
383 let old = self.draft.copies.insert(key.clone(), id);
384 self.draft.journal.push(Entry::Copy(key, old));
385 }
386
387 pub(crate) fn opened(&mut self, path: &str) -> bool {
389 self.lowering.insert(path.to_string())
390 }
391
392 pub(crate) fn closed(&mut self, path: &str) {
393 self.lowering.remove(path);
394 }
395
396 pub fn at(&self, n: NodeId) -> &Node {
397 self.nodes[n.0 as usize]
398 .as_ref()
399 .expect("a node the typing holds")
400 }
401
402 pub fn ids(&self) -> impl Iterator<Item = NodeId> + '_ {
403 let held = self.nodes.iter().enumerate();
404 held.filter(|(_, n)| n.is_some())
405 .map(|(at, _)| NodeId(at as u32))
406 }
407
408 #[cfg(test)]
409 pub(crate) fn len(&self) -> usize {
410 self.nodes.len() - self.free.len()
411 }
412
413 pub fn id(&self, path: &str) -> Option<NodeId> {
414 match self.draft.by_path.get(path) {
415 Some(id) => Some(*id),
416 None if self.draft.hidden.contains(path) => None,
417 None => self.by_path.get(path).copied(),
418 }
419 }
420
421 pub fn paths(&self) -> impl Iterator<Item = (&str, NodeId)> {
423 let drafted = self.draft.by_path.iter();
424 let kept = self.by_path.iter().filter(|(path, _)| {
425 !self.draft.hidden.contains(*path) && !self.draft.by_path.contains_key(*path)
426 });
427 let all: BTreeMap<&String, &NodeId> = drafted.chain(kept).collect();
428 all.into_iter().map(|(p, id)| (p.as_str(), *id))
429 }
430
431 pub fn resolve(&self, path: &str) -> Result<NodeId, EngineError> {
433 if let Some(id) = self.id(path) {
434 return Ok(id);
435 }
436 let held = self.files.get(path).cloned().unwrap_or_default();
437 let file = path.split_once('(').map(|(file, _)| file);
438 let spelt = file.and_then(|file| self.files.get(file));
439 if let Some(ids) = spelt.filter(|_| held.is_empty()) {
440 let names = ids.iter().map(|id| self.name(*id).to_string()).collect();
441 return Err(EngineError::UnknownInstance(path.to_string(), names));
442 }
443 crate::instantiate::sole(
444 path,
445 held.into_iter()
446 .map(|id| (self.name(id).to_string(), id))
447 .collect(),
448 )
449 }
450
451 pub fn locate(&self, origin: Origin) -> Located {
453 let held = self.origins.get(origin.token() as usize).copied().flatten();
454 held.and_then(|(site, span)| {
455 let (name, _) = self.sites[site as usize].as_ref()?;
456 Some(Located::at(name.as_str(), span))
457 })
458 .unwrap_or_default()
459 }
460
461 pub(crate) fn mark(&mut self, node: &str, span: Option<sva_ast::ByteSpan>) -> Origin {
462 let site = match self.site_ids.get(node) {
463 Some(&site) => site,
464 None => {
465 let held = Some((node.to_string(), 0));
466 let site = match self.free_sites.pop() {
467 Some(site) => {
468 self.sites[site as usize] = held;
469 site
470 }
471 None => {
472 self.sites.push(held);
473 (self.sites.len() - 1) as u32
474 }
475 };
476 self.site_ids.insert(node.to_string(), site);
477 site
478 }
479 };
480 self.sites[site as usize].as_mut().expect("a site").1 += 1;
481 let token = match self.free_origins.pop() {
482 Some(token) => {
483 self.origins[token as usize] = Some((site, span));
484 token
485 }
486 None => {
487 self.origins.push(Some((site, span)));
488 (self.origins.len() - 1) as u32
489 }
490 };
491 let unit = self.unit();
492 self.draft.journal.push(Entry::Origin(token, unit));
493 Origin::new(token)
494 }
495
496 pub(crate) fn seed(&mut self, path: &str, held: Held, grid: Grid) -> NodeId {
497 if let Some(id) = self.id(path) {
498 return id;
499 }
500 let name = self.begin(path, grid);
501 let id = self.push(
502 Node {
503 name,
504 ty: Ty {
505 dual: held.is_closed_form(),
506 ..Ty::discrete(held, Codomain::Real)
507 },
508 var: Var::T,
509 value: Value::Op {
510 name: "loop".to_string(),
511 args: Vec::new(),
512 },
513 grid,
514 },
515 Some(path),
516 );
517 self.end();
518 self.pending.insert(id);
519 self.draft.journal.push(Entry::Pending(id));
520 id
521 }
522
523 pub(crate) fn pending(&self, id: NodeId) -> bool {
524 self.pending.contains(&id)
525 }
526
527 pub(crate) fn settle(&mut self, id: NodeId, node: Node) {
529 self.place(id, Some(node));
530 self.pending.remove(&id);
531 self.forget([id]);
532 }
533
534 pub(crate) fn folds(&self) -> &folds::Folds {
535 &self.folds
536 }
537
538 pub(crate) fn alias(&mut self, path: &str, id: NodeId) {
539 let old = self.draft.by_path.insert(path.to_string(), id);
540 self.draft.journal.push(Entry::Path(path.to_string(), old));
541 }
542
543 pub(crate) fn push(&mut self, node: Node, path: Option<&str>) -> NodeId {
544 let id = match self.free.pop() {
545 Some(at) => NodeId(at),
546 None => {
547 self.nodes.push(None);
548 NodeId((self.nodes.len() - 1) as u32)
549 }
550 };
551 self.place(id, Some(node));
552 let unit = self.unit();
553 self.draft.journal.push(Entry::Node(id, unit));
554 if let Some(path) = path {
555 self.alias(path, id);
556 }
557 id
558 }
559
560 pub(crate) fn begin(&mut self, path: &str, grid: Grid) -> Arc<str> {
563 let name: Arc<str> = path.into();
564 self.making.push((Arc::clone(&name), grid));
565 name
566 }
567
568 pub(crate) fn end(&mut self) {
569 self.making.pop();
570 }
571
572 fn unit(&self) -> (Arc<str>, Grid) {
573 self.making
574 .last()
575 .cloned()
576 .unwrap_or_else(|| (Arc::from(""), Grid::of(1)))
577 }
578
579 pub(crate) fn infer_closed_form(&self, form: &ClosedForm) -> Result<Ty, EngineError> {
580 let inferred = crate::refs::read_through(self, |through| {
581 infer(form, &TypedNodesEnv(&self.nodes, through))
582 });
583 inferred.map_err(|r| {
584 EngineError::of_closed_form(
585 &r,
586 self.locate(r.origin),
587 "write the subterm inside sample(...) to leave A deliberately",
588 )
589 })
590 }
591}
592
593struct TypedNodesEnv<'a>(&'a [Option<Node>], &'a dyn sva_formula::Reads);
594
595impl Env for TypedNodesEnv<'_> {
596 fn node(&self, id: NodeId) -> Ty {
597 self.0[id.0 as usize]
598 .as_ref()
599 .expect("a node the typing holds")
600 .ty
601 }
602
603 fn param(&self, _: ParamId) -> Ty {
605 Ty::form(Var::T, true, Codomain::Real)
606 }
607
608 fn reads(&self) -> &dyn sva_formula::Reads {
609 self.1
610 }
611}
612
613pub fn infer_all(inst: &Instances, order: &Order) -> Result<Typing, EngineError> {
615 let mut typing = Typing::default();
616 typing.lower(inst, &order.groups, &|_| ())?;
617 typing.commit(inst);
618 Ok(typing)
619}
620
621impl Typing {
622 pub(crate) fn lower(
625 &mut self,
626 inst: &Instances,
627 groups: &[Vec<String>],
628 told: &dyn Fn(usize),
629 ) -> Result<(), EngineError> {
630 self.lowered.clear();
631 self.hide(groups.iter().flatten().cloned());
632 let mut typed = 0;
633 for group in groups {
634 typed += group.len();
635 match crate::schedule::is_loop(inst, group) {
636 true => settle_loop(self, inst, group)?,
637 false => {
638 for path in group {
639 lower::node(path, inst, self)?;
640 }
641 }
642 }
643 told(typed);
644 }
645 Ok(())
646 }
647}
648
649const SEEDS: [Held; 2] = [Held::Form(Var::T), Held::Sampled];
650
651fn settle_loop(typing: &mut Typing, inst: &Instances, group: &[String]) -> Result<(), EngineError> {
654 let mut seed = SEEDS[0];
655 let mut refusal = None;
656 for _ in 0..=SEEDS.len() {
657 let mark = typing.checkpoint();
658 for path in group {
659 typing.seed(path, seed, inst.grid());
660 }
661 let walked = group
662 .iter()
663 .try_for_each(|path| lower::node(path, inst, typing).map(|_| ()));
664 match walked {
665 Ok(()) => match held_by(typing, group) {
666 reached if reached == seed => return Ok(()),
667 reached => seed = reached,
668 },
669 Err(e) => {
670 refusal = Some(e);
671 seed = elsewhere(seed);
672 }
673 }
674 typing.rollback(mark);
675 }
676 Err(refusal.unwrap_or_else(|| mixed(group)))
677}
678
679fn elsewhere(seed: Held) -> Held {
681 match seed {
682 Held::Sampled => Held::Form(Var::T),
683 _ => Held::Sampled,
684 }
685}
686
687fn held_by(typing: &Typing, group: &[String]) -> Held {
689 let sampled = group
690 .iter()
691 .filter_map(|path| typing.id(path))
692 .any(|id| !typing.ty(id).is_closed_form());
693 match sampled {
694 true => Held::Sampled,
695 false => Held::Form(Var::T),
696 }
697}
698
699fn mixed(group: &[String]) -> EngineError {
701 let at = group.first().map_or("", String::as_str);
702 EngineError::refused(Diagnostic {
703 code: "type.samples_in_closed_form".to_string(),
704 message: format!(
705 "the loop over `{}` holds a closed form and samples at once.",
706 group.join("`, `")
707 ),
708 location: Located::at(at, None),
709 help: "write sample(...) on the members that are closed forms, so the whole loop runs \
710 on the grid"
711 .to_string(),
712 })
713}