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 crate::instantiate::sole(
436 path,
437 held.into_iter()
438 .map(|id| (self.name(id).to_string(), id))
439 .collect(),
440 )
441 }
442
443 pub fn locate(&self, origin: Origin) -> Located {
445 let held = self.origins.get(origin.token() as usize).copied().flatten();
446 held.and_then(|(site, span)| {
447 let (name, _) = self.sites[site as usize].as_ref()?;
448 Some(Located::at(name.as_str(), span))
449 })
450 .unwrap_or_default()
451 }
452
453 pub(crate) fn mark(&mut self, node: &str, span: Option<sva_ast::ByteSpan>) -> Origin {
454 let site = match self.site_ids.get(node) {
455 Some(&site) => site,
456 None => {
457 let held = Some((node.to_string(), 0));
458 let site = match self.free_sites.pop() {
459 Some(site) => {
460 self.sites[site as usize] = held;
461 site
462 }
463 None => {
464 self.sites.push(held);
465 (self.sites.len() - 1) as u32
466 }
467 };
468 self.site_ids.insert(node.to_string(), site);
469 site
470 }
471 };
472 self.sites[site as usize].as_mut().expect("a site").1 += 1;
473 let token = match self.free_origins.pop() {
474 Some(token) => {
475 self.origins[token as usize] = Some((site, span));
476 token
477 }
478 None => {
479 self.origins.push(Some((site, span)));
480 (self.origins.len() - 1) as u32
481 }
482 };
483 let unit = self.unit();
484 self.draft.journal.push(Entry::Origin(token, unit));
485 Origin::new(token)
486 }
487
488 pub(crate) fn seed(&mut self, path: &str, held: Held, grid: Grid) -> NodeId {
489 if let Some(id) = self.id(path) {
490 return id;
491 }
492 let name = self.begin(path, grid);
493 let id = self.push(
494 Node {
495 name,
496 ty: Ty {
497 dual: held.is_closed_form(),
498 ..Ty::discrete(held, Codomain::Real)
499 },
500 var: Var::T,
501 value: Value::Op {
502 name: "loop".to_string(),
503 args: Vec::new(),
504 },
505 grid,
506 },
507 Some(path),
508 );
509 self.end();
510 self.pending.insert(id);
511 self.draft.journal.push(Entry::Pending(id));
512 id
513 }
514
515 pub(crate) fn pending(&self, id: NodeId) -> bool {
516 self.pending.contains(&id)
517 }
518
519 pub(crate) fn settle(&mut self, id: NodeId, node: Node) {
521 self.place(id, Some(node));
522 self.pending.remove(&id);
523 self.forget([id]);
524 }
525
526 pub(crate) fn folds(&self) -> &folds::Folds {
527 &self.folds
528 }
529
530 pub(crate) fn alias(&mut self, path: &str, id: NodeId) {
531 let old = self.draft.by_path.insert(path.to_string(), id);
532 self.draft.journal.push(Entry::Path(path.to_string(), old));
533 }
534
535 pub(crate) fn push(&mut self, node: Node, path: Option<&str>) -> NodeId {
536 let id = match self.free.pop() {
537 Some(at) => NodeId(at),
538 None => {
539 self.nodes.push(None);
540 NodeId((self.nodes.len() - 1) as u32)
541 }
542 };
543 self.place(id, Some(node));
544 let unit = self.unit();
545 self.draft.journal.push(Entry::Node(id, unit));
546 if let Some(path) = path {
547 self.alias(path, id);
548 }
549 id
550 }
551
552 pub(crate) fn begin(&mut self, path: &str, grid: Grid) -> Arc<str> {
555 let name: Arc<str> = path.into();
556 self.making.push((Arc::clone(&name), grid));
557 name
558 }
559
560 pub(crate) fn end(&mut self) {
561 self.making.pop();
562 }
563
564 fn unit(&self) -> (Arc<str>, Grid) {
565 self.making
566 .last()
567 .cloned()
568 .unwrap_or_else(|| (Arc::from(""), Grid::of(1)))
569 }
570
571 pub(crate) fn infer_closed_form(&self, form: &ClosedForm) -> Result<Ty, EngineError> {
572 let inferred = crate::refs::read_through(self, |through| {
573 infer(form, &TypedNodesEnv(&self.nodes, through))
574 });
575 inferred.map_err(|r| {
576 EngineError::of_closed_form(
577 &r,
578 self.locate(r.origin),
579 "write the subterm inside sample(...) to leave A deliberately",
580 )
581 })
582 }
583}
584
585struct TypedNodesEnv<'a>(&'a [Option<Node>], &'a dyn sva_formula::Reads);
586
587impl Env for TypedNodesEnv<'_> {
588 fn node(&self, id: NodeId) -> Ty {
589 self.0[id.0 as usize]
590 .as_ref()
591 .expect("a node the typing holds")
592 .ty
593 }
594
595 fn param(&self, _: ParamId) -> Ty {
597 Ty::form(Var::T, true, Codomain::Real)
598 }
599
600 fn reads(&self) -> &dyn sva_formula::Reads {
601 self.1
602 }
603}
604
605pub fn infer_all(inst: &Instances, order: &Order) -> Result<Typing, EngineError> {
607 let mut typing = Typing::default();
608 typing.lower(inst, &order.groups, &|_| ())?;
609 typing.commit(inst);
610 Ok(typing)
611}
612
613impl Typing {
614 pub(crate) fn lower(
617 &mut self,
618 inst: &Instances,
619 groups: &[Vec<String>],
620 told: &dyn Fn(usize),
621 ) -> Result<(), EngineError> {
622 self.lowered.clear();
623 self.hide(groups.iter().flatten().cloned());
624 let mut typed = 0;
625 for group in groups {
626 typed += group.len();
627 match crate::schedule::is_loop(inst, group) {
628 true => settle_loop(self, inst, group)?,
629 false => {
630 for path in group {
631 lower::node(path, inst, self)?;
632 }
633 }
634 }
635 told(typed);
636 }
637 Ok(())
638 }
639}
640
641const SEEDS: [Held; 2] = [Held::Form(Var::T), Held::Sampled];
642
643fn settle_loop(typing: &mut Typing, inst: &Instances, group: &[String]) -> Result<(), EngineError> {
646 let mut seed = SEEDS[0];
647 let mut refusal = None;
648 for _ in 0..=SEEDS.len() {
649 let mark = typing.checkpoint();
650 for path in group {
651 typing.seed(path, seed, inst.grid());
652 }
653 let walked = group
654 .iter()
655 .try_for_each(|path| lower::node(path, inst, typing).map(|_| ()));
656 match walked {
657 Ok(()) => match held_by(typing, group) {
658 reached if reached == seed => return Ok(()),
659 reached => seed = reached,
660 },
661 Err(e) => {
662 refusal = Some(e);
663 seed = elsewhere(seed);
664 }
665 }
666 typing.rollback(mark);
667 }
668 Err(refusal.unwrap_or_else(|| mixed(group)))
669}
670
671fn elsewhere(seed: Held) -> Held {
673 match seed {
674 Held::Sampled => Held::Form(Var::T),
675 _ => Held::Sampled,
676 }
677}
678
679fn held_by(typing: &Typing, group: &[String]) -> Held {
681 let sampled = group
682 .iter()
683 .filter_map(|path| typing.id(path))
684 .any(|id| !typing.ty(id).is_closed_form());
685 match sampled {
686 true => Held::Sampled,
687 false => Held::Form(Var::T),
688 }
689}
690
691fn mixed(group: &[String]) -> EngineError {
693 let at = group.first().map_or("", String::as_str);
694 EngineError::refused(Diagnostic {
695 code: "type.samples_in_closed_form".to_string(),
696 message: format!(
697 "the loop over `{}` holds a closed form and samples at once.",
698 group.join("`, `")
699 ),
700 location: Located::at(at, None),
701 help: "write sample(...) on the members that are closed forms, so the whole loop runs \
702 on the grid"
703 .to_string(),
704 })
705}