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