smt_scope/items/
equality.rs

1use std::num::NonZeroUsize;
2
3#[cfg(feature = "mem_dbg")]
4use mem_dbg::{MemDbg, MemSize};
5
6use crate::{BoxSlice, IString, NonMaxU32};
7use crate::{Result, StringTable};
8
9use super::{ENodeIdx, EqGivenIdx, EqTransIdx, LitIdx, TheoryKind};
10
11/// A Z3 equality explanation.
12/// Root represents a term that is a root of its equivalence class.
13/// All other variants represent an equality between two terms and where it came from.
14#[cfg_attr(feature = "mem_dbg", derive(MemSize, MemDbg))]
15#[derive(Debug, Clone, PartialEq)]
16pub enum EqualityExpl {
17    Root {
18        id: ENodeIdx,
19    },
20    Literal {
21        from: ENodeIdx,
22        /// The equality term this is from
23        eq: LitIdx,
24        to: ENodeIdx,
25    },
26    Congruence {
27        from: ENodeIdx,
28        arg_eqs: Box<[(ENodeIdx, ENodeIdx)]>,
29        to: ENodeIdx,
30        /// The `arg_eqs` need to be evaluated whenever this is used.
31        uses: Vec<BoxSlice<EqTransIdx>>,
32    },
33    Theory {
34        from: ENodeIdx,
35        theory: TheoryKind,
36        to: ENodeIdx,
37    },
38    Axiom {
39        from: ENodeIdx,
40        to: ENodeIdx,
41    },
42    Unknown {
43        kind: IString,
44        from: ENodeIdx,
45        args: BoxSlice<IString>,
46        to: ENodeIdx,
47    },
48}
49
50impl EqualityExpl {
51    pub fn is_trivial(&self) -> bool {
52        self.from() == self.to()
53    }
54    pub fn from(&self) -> ENodeIdx {
55        use EqualityExpl::*;
56        match *self {
57            Root { id } => id,
58            Literal { from, .. }
59            | Congruence { from, .. }
60            | Theory { from, .. }
61            | Axiom { from, .. }
62            | Unknown { from, .. } => from,
63        }
64    }
65    pub fn to(&self) -> ENodeIdx {
66        use EqualityExpl::*;
67        match *self {
68            Root { id } => id,
69            Literal { to, .. }
70            | Congruence { to, .. }
71            | Theory { to, .. }
72            | Axiom { to, .. }
73            | Unknown { to, .. } => to,
74        }
75    }
76    pub fn walk_any(&self, from: ENodeIdx) -> ENodeIdx {
77        let Some(to) = self.walk(from, true).or_else(|| self.walk(from, false)) else {
78            panic!(
79                "walking from {from} with {} <--> {}",
80                self.from(),
81                self.to()
82            );
83        };
84        to
85    }
86    pub fn walk(&self, from: ENodeIdx, fwd: bool) -> Option<ENodeIdx> {
87        if fwd {
88            (self.from() == from).then(|| self.to())
89        } else {
90            (self.to() == from).then(|| self.from())
91        }
92    }
93    pub fn kind(&self) -> core::result::Result<&'static str, IString> {
94        let kind = match *self {
95            EqualityExpl::Root { .. } => "root",
96            EqualityExpl::Literal { .. } => "literal",
97            EqualityExpl::Congruence { .. } => "congruence",
98            EqualityExpl::Theory { .. } => "theory",
99            EqualityExpl::Axiom { .. } => "axiom",
100            EqualityExpl::Unknown { kind, .. } => return Err(kind),
101        };
102        Ok(kind)
103    }
104    pub fn kind_str<'a>(&self, strings: &'a StringTable) -> &'a str {
105        self.kind().unwrap_or_else(|kind| &strings[*kind])
106    }
107}
108
109// Whenever a pair of enodes are said to be equal this uses transitive reasoning
110// with one or more `EqualityExpl` to explain why.
111#[cfg_attr(feature = "mem_dbg", derive(MemSize, MemDbg))]
112#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
113#[derive(Debug)]
114pub struct TransitiveExpl {
115    pub path: Box<[TransitiveExplSegment]>,
116    pub given_len: Option<NonZeroUsize>,
117    pub to: ENodeIdx,
118}
119type BackwardIter<'a> = std::iter::Map<
120    std::iter::Rev<std::iter::Copied<std::slice::Iter<'a, TransitiveExplSegment>>>,
121    fn(TransitiveExplSegment) -> TransitiveExplSegment,
122>;
123#[derive(Clone)]
124pub enum TransitiveExplIter<'a> {
125    Forward(std::iter::Copied<std::slice::Iter<'a, TransitiveExplSegment>>),
126    Backward(BackwardIter<'a>),
127}
128impl Iterator for TransitiveExplIter<'_> {
129    type Item = TransitiveExplSegment;
130    fn next(&mut self) -> Option<Self::Item> {
131        match self {
132            Self::Forward(iter) => iter.next(),
133            Self::Backward(iter) => iter.next(),
134        }
135    }
136}
137
138impl TransitiveExpl {
139    pub fn new(
140        i: impl ExactSizeIterator<Item = TransitiveExplSegment>,
141        given_len: NonZeroUsize,
142        to: ENodeIdx,
143    ) -> Result<Self> {
144        let mut path = Vec::new();
145        path.try_reserve_exact(i.len())?;
146        path.extend(i);
147        Ok(Self {
148            path: path.into_boxed_slice(),
149            given_len: Some(given_len),
150            to,
151        })
152    }
153    pub fn error(from: ENodeIdx, to: ENodeIdx) -> Self {
154        Self {
155            path: Box::new([TransitiveExplSegment {
156                forward: true,
157                kind: TransitiveExplSegmentKind::Error(from),
158            }]),
159            given_len: None,
160            to,
161        }
162    }
163    pub fn all(&self, fwd: bool) -> TransitiveExplIter<'_> {
164        let iter = self.path.iter().copied();
165        if fwd {
166            TransitiveExplIter::Forward(iter)
167        } else {
168            TransitiveExplIter::Backward(TransitiveExplSegment::rev(iter))
169        }
170    }
171    pub fn error_from(&self) -> Option<ENodeIdx> {
172        self.given_len.is_none().then(|| match self.path[0].kind {
173            TransitiveExplSegmentKind::Error(from) => from,
174            _ => panic!("expected error segment"),
175        })
176    }
177}
178
179#[cfg_attr(feature = "mem_dbg", derive(MemSize, MemDbg))]
180#[cfg_attr(feature = "mem_dbg", copy_type)]
181#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
182#[derive(Debug, Clone, Copy)]
183pub struct TransitiveExplSegment {
184    pub forward: bool,
185    pub kind: TransitiveExplSegmentKind,
186}
187impl TransitiveExplSegment {
188    pub fn rev<I: Iterator<Item = TransitiveExplSegment> + std::iter::DoubleEndedIterator>(
189        iter: I,
190    ) -> std::iter::Map<std::iter::Rev<I>, fn(TransitiveExplSegment) -> TransitiveExplSegment> {
191        // Negate the forward direction since we're walking
192        // backwards (`.rev()` above).
193        iter.rev().map(TransitiveExplSegment::rev_single)
194    }
195    fn rev_single(mut self) -> Self {
196        self.forward = !self.forward;
197        self
198    }
199}
200
201pub type EqGivenUse = (EqGivenIdx, Option<NonMaxU32>);
202
203#[cfg_attr(feature = "mem_dbg", derive(MemSize, MemDbg))]
204#[cfg_attr(feature = "mem_dbg", copy_type)]
205#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
206#[derive(Debug, Clone, Copy)]
207pub enum TransitiveExplSegmentKind {
208    Given(EqGivenUse),
209    Transitive(EqTransIdx),
210    Error(ENodeIdx),
211}
212impl TransitiveExplSegmentKind {
213    pub fn given(self) -> Option<EqGivenUse> {
214        match self {
215            Self::Given(eq_use) => Some(eq_use),
216            _ => None,
217        }
218    }
219}