sva_engine/render/
quiet.rs1use std::collections::{BTreeMap, BTreeSet};
4
5use sva_ast::Graph;
6use sva_formula::NodeId;
7
8use super::bound::Tail;
9use super::table::Table;
10use super::{Ends, Render, RenderConfig, prepared, range_of};
11use crate::error::EngineError;
12use crate::schedule;
13
14pub const QUIET_LEVEL: f64 = 1.0 / (1u64 << 24) as f64;
16
17pub const QUIET_AFTER_SECS: f64 = 1.0;
18
19#[derive(Clone, Debug, PartialEq)]
21pub struct QuietTail {
22 pub instance: String,
23 pub file: String,
24 pub quiet_from: f64,
25 pub runs_to: f64,
26}
27
28pub fn quiet_tails(
31 graph: &Graph,
32 target: &str,
33 config: RenderConfig,
34) -> Result<Vec<QuietTail>, EngineError> {
35 let held = prepared(graph, target, config.rate)?;
36 let schedule = schedule::plan(&held.tys, held.root, &[]);
37 let shell = Render::shell(held.tys.clone(), held.root, config, schedule);
38 let range = range_of(&shell, Ends::Pulled)?;
39 let table = Table::build(&shell.tys, shell.root, &[], &shell.config.profile)?;
40 let needs = table.demand(range);
41 let mut out = BTreeMap::new();
42 for (path, id) in held.tys.paths().filter(|(p, _)| held.instances.holds(p)) {
43 let Some(at) = table.of(id) else {
44 continue;
45 };
46 let asked = needs[at].hold.hull();
47 if asked.is_empty() {
48 continue;
49 }
50 let grid = shell.grid(id);
51 let edge = |n: i64| match n {
52 i64::MIN => f64::NEG_INFINITY,
53 i64::MAX => f64::INFINITY,
54 n => grid.instant(n),
55 };
56 let (from, to) = (edge(asked.start), edge(asked.end));
57 let Some(tail) = Tail::of(&held.tys, &shell.config, id) else {
58 continue;
59 };
60 if let Some(quiet_from) = quiet(&tail, from.max(0.0), to) {
61 let file = held.instances.origin(path).unwrap_or(path).to_string();
62 let tail = QuietTail {
63 instance: path.to_string(),
64 file,
65 quiet_from,
66 runs_to: to,
67 };
68 out.insert(id, tail);
69 }
70 }
71 let named: BTreeSet<NodeId> = out.keys().copied().collect();
72 Ok(out
73 .into_iter()
74 .filter(|(id, _)| under(&held.tys, *id).is_disjoint(&named))
75 .map(|(_, tail)| tail)
76 .collect())
77}
78
79fn under(tys: &crate::typing::Typing, id: NodeId) -> BTreeSet<NodeId> {
81 let (mut seen, mut work) = (BTreeSet::new(), schedule::read_operands(tys, id));
82 while let Some(next) = work.pop() {
83 if next != id && seen.insert(next) {
84 work.extend(schedule::read_operands(tys, next));
85 }
86 }
87 seen
88}
89
90fn quiet(tail: &Tail, from: f64, to: f64) -> Option<f64> {
93 let under = |t: f64| tail.from(t) < QUIET_LEVEL;
94 let last = match to.is_finite() {
95 true => to - QUIET_AFTER_SECS,
96 false => (0..24)
97 .map(|k| from + f64::from(1 << k))
98 .find(|t| under(*t))?,
99 };
100 if last < from || !under(last) {
101 return None;
102 }
103 let (mut lo, mut hi) = (from, last);
104 if under(lo) {
105 return None;
106 }
107 while hi - lo > 1e-3 {
108 let mid = 0.5 * (lo + hi);
109 match under(mid) {
110 true => hi = mid,
111 false => lo = mid,
112 }
113 }
114 Some(hi)
115}