Skip to main content

sv_sim_sim/constraints/
order.rs

1//! Variable solve order from `solve ... before` (plan C4; LRM 18.5.9).
2//!
3//! `solve a before b;` says `a` is solved first, uniformly over the values it
4//! can take in *some* legal solution, and then `b` is solved given `a`. A
5//! constraint block can hold any number of these, so they form a directed
6//! graph over the random variables and the LRM asks for three things of it:
7//!
8//! * it must be acyclic ("circular dependencies ... are not allowed");
9//! * variables that are not ordered at all are solved *with the last set* of
10//!   ordered variables (so an unconstrained-by-ordering variable behaves like
11//!   the last ordered one: Table 18-1 is simply the case where nothing is
12//!   ordered and everything is one set);
13//! * a variable that is only partially ordered is solved "with the latest set
14//!   of variables that meets the constraints" (as late as the edges allow).
15//!
16//! [`levels`] gives each variable its *level*: level 0 is solved first and the
17//! highest level is solved last. A variable's level is `L - h` where `h` is the
18//! length of the longest chain of `before` edges leaving it and `L` is the
19//! longest chain anywhere, which is exactly the as-late-as-possible schedule
20//! of the three rules above (a variable no edge leaves, ordered or not, has
21//! `h = 0` and lands in the last level). [`groups`] collects the variables of
22//! each level, in id order, in the shape
23//! [`super::solve::Solver::sample_groups`] consumes.
24//!
25//! `randc` variables are *not* part of this graph: they are always solved
26//! before every `rand` variable (LRM 18.4.2) and cannot appear in a `solve`
27//! (LRM 18.5.9); the builder rejects the latter and the solve runs them first.
28
29use super::ir::VarId;
30use core::fmt;
31
32/// Why a set of `before` edges has no solve order.
33#[derive(Clone, Debug, PartialEq, Eq)]
34pub enum OrderError {
35    /// The edges contain a cycle (LRM 18.5.9: "circular dependencies ... are
36    /// not allowed"). `vars` is one cycle, in `before` order, without
37    /// repeating its first variable (a self-edge `solve a before a` is the
38    /// one-variable cycle).
39    Cycle {
40        /// The variables on the cycle.
41        vars: Vec<VarId>,
42    },
43    /// An edge names a variable id outside `0..n`.
44    UnknownVar {
45        /// The offending id.
46        var: VarId,
47    },
48}
49
50impl fmt::Display for OrderError {
51    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52        match self {
53            OrderError::Cycle { vars } => write!(
54                f,
55                "the `solve ... before` constraints are circular through random variables {vars:?} \
56                 (IEEE 1800-2023 18.5.9)"
57            ),
58            OrderError::UnknownVar { var } => write!(
59                f,
60                "a `solve ... before` edge names random variable #{var} which does not exist"
61            ),
62        }
63    }
64}
65
66impl std::error::Error for OrderError {}
67
68/// The level of each of the `n` variables under the `before` edges
69/// `(first, later)` (module docs): 0 is solved first, `max` last. With no
70/// edges every variable is at level 0, one set.
71pub fn levels(n: usize, edges: &[(VarId, VarId)]) -> Result<Vec<usize>, OrderError> {
72    let mut succ: Vec<Vec<usize>> = vec![Vec::new(); n];
73    for &(a, b) in edges {
74        for v in [a, b] {
75            if v as usize >= n {
76                return Err(OrderError::UnknownVar { var: v });
77            }
78        }
79        if !succ[a as usize].contains(&(b as usize)) {
80            succ[a as usize].push(b as usize);
81        }
82    }
83    // Peel from the sinks: a variable is finished once all its successors
84    // are, and its height is one more than its tallest successor's.
85    let mut pred: Vec<Vec<usize>> = vec![Vec::new(); n];
86    for (a, ss) in succ.iter().enumerate() {
87        for &b in ss {
88            pred[b].push(a);
89        }
90    }
91    let mut pending: Vec<usize> = succ.iter().map(Vec::len).collect();
92    let mut height = vec![0usize; n];
93    let mut ready: Vec<usize> = (0..n).filter(|&v| pending[v] == 0).collect();
94    let mut done = 0usize;
95    while let Some(v) = ready.pop() {
96        done += 1;
97        for &p in &pred[v] {
98            height[p] = height[p].max(height[v] + 1);
99            pending[p] -= 1;
100            if pending[p] == 0 {
101                ready.push(p);
102            }
103        }
104    }
105    if done < n {
106        // Every unfinished variable still has an unfinished successor, so
107        // walking successors among them must revisit one: that is a cycle.
108        let unfinished: Vec<bool> = pending.iter().map(|&p| p > 0).collect();
109        let start = (0..n).find(|&v| unfinished[v]).expect("done < n");
110        let mut seen_at = vec![usize::MAX; n];
111        let mut path: Vec<usize> = Vec::new();
112        let mut cur = start;
113        loop {
114            if seen_at[cur] != usize::MAX {
115                let vars = path[seen_at[cur]..].iter().map(|&v| v as VarId).collect();
116                return Err(OrderError::Cycle { vars });
117            }
118            seen_at[cur] = path.len();
119            path.push(cur);
120            cur = *succ[cur]
121                .iter()
122                .find(|&&s| unfinished[s])
123                .expect("an unfinished variable has an unfinished successor");
124        }
125    }
126    let top = height.iter().copied().max().unwrap_or(0);
127    Ok(height.into_iter().map(|h| top - h).collect())
128}
129
130/// The variables of each level of [`levels`], first-solved level first, each
131/// in id order. Every level is non-empty.
132pub fn groups(n: usize, edges: &[(VarId, VarId)]) -> Result<Vec<Vec<VarId>>, OrderError> {
133    let lv = levels(n, edges)?;
134    let count = lv.iter().copied().max().map_or(0, |m| m + 1);
135    let mut out: Vec<Vec<VarId>> = vec![Vec::new(); count];
136    for (v, l) in lv.into_iter().enumerate() {
137        out[l].push(v as VarId);
138    }
139    debug_assert!(out.iter().all(|g| !g.is_empty()));
140    Ok(out)
141}
142
143#[cfg(test)]
144mod tests {
145    use super::*;
146
147    #[test]
148    fn no_edges_is_one_set() {
149        assert_eq!(levels(3, &[]).unwrap(), vec![0, 0, 0]);
150        assert_eq!(groups(3, &[]).unwrap(), vec![vec![0, 1, 2]]);
151        assert_eq!(groups(0, &[]).unwrap(), Vec::<Vec<VarId>>::new());
152    }
153
154    #[test]
155    fn a_before_b_leaves_unordered_variables_with_the_last_set() {
156        // Variables a=0, b=1, u=2 (unordered): u is solved with b, the last
157        // ordered set (LRM 18.5.9).
158        assert_eq!(levels(3, &[(0, 1)]).unwrap(), vec![0, 1, 1]);
159        assert_eq!(groups(3, &[(0, 1)]).unwrap(), vec![vec![0], vec![1, 2]]);
160    }
161
162    #[test]
163    fn a_partially_ordered_variable_is_solved_as_late_as_the_edges_allow() {
164        // a before b before c, and d before c: d has the one edge to the last
165        // variable so it is solved with b (level 1), not with a (level 0).
166        let e = [(0, 1), (1, 2), (3, 2)];
167        assert_eq!(levels(4, &e).unwrap(), vec![0, 1, 2, 1]);
168        assert_eq!(groups(4, &e).unwrap(), vec![vec![0], vec![1, 3], vec![2]]);
169    }
170
171    #[test]
172    fn several_names_on_one_side_fan_in_and_out() {
173        // solve a, b before c, d
174        let e = [(0, 2), (0, 3), (1, 2), (1, 3)];
175        assert_eq!(groups(4, &e).unwrap(), vec![vec![0, 1], vec![2, 3]]);
176    }
177
178    #[test]
179    fn duplicate_edges_are_harmless() {
180        assert_eq!(levels(2, &[(0, 1), (0, 1), (0, 1)]).unwrap(), vec![0, 1]);
181    }
182
183    #[test]
184    fn cycles_are_rejected_and_named() {
185        assert_eq!(
186            levels(2, &[(0, 0)]).unwrap_err(),
187            OrderError::Cycle { vars: vec![0] }
188        );
189        let e = levels(3, &[(0, 1), (1, 2), (2, 1)]).unwrap_err();
190        let OrderError::Cycle { vars } = e else {
191            panic!("{e:?}")
192        };
193        let mut sorted = vars.clone();
194        sorted.sort_unstable();
195        assert_eq!(
196            sorted,
197            vec![1, 2],
198            "the cycle, not variable 0 upstream of it"
199        );
200        // The reported list is a genuine cycle in `before` order.
201        assert!(vars == vec![1, 2] || vars == vec![2, 1]);
202    }
203
204    #[test]
205    fn an_unknown_variable_is_an_error() {
206        assert_eq!(
207            levels(2, &[(0, 5)]).unwrap_err(),
208            OrderError::UnknownVar { var: 5 }
209        );
210    }
211
212    #[test]
213    fn errors_have_messages() {
214        assert!(
215            OrderError::Cycle { vars: vec![1, 2] }
216                .to_string()
217                .contains("circular")
218        );
219        assert!(OrderError::UnknownVar { var: 9 }.to_string().contains("#9"));
220    }
221}