Skip to main content

slotted_egraphs/egraph/
check.rs

1use vec_collections::AbstractVecSet;
2
3use crate::*;
4
5impl<L: Language, N: Analysis<L>> EGraph<L, N> {
6    // mk_sem_applied_id & friends.
7    #[track_caller]
8    pub(crate) fn mk_sem_applied_id(&self, i: Id, m: SlotMap) -> AppliedId {
9        let app_id = AppliedId::new(i, m);
10
11        if CHECKS {
12            self.check_sem_applied_id(&app_id);
13        }
14
15        app_id
16    }
17
18
19    // public API:
20    #[track_caller]
21    pub fn mk_identity_applied_id(&self, i: Id) -> AppliedId {
22        self.mk_sem_identity_applied_id(i)
23    }
24
25    #[track_caller]
26    pub(crate) fn mk_sem_identity_applied_id(&self, i: Id) -> AppliedId {
27        self.mk_sem_applied_id(i, SlotMap::identity(&self.slots(i)))
28    }
29
30    #[track_caller]
31    pub(crate) fn check_sem_applied_id(&self, app_id: &AppliedId) {
32        app_id.check();
33        assert_eq!(
34            self.slots(app_id.id),
35            app_id.m.keys(),
36            "checking sem AppliedId failed: Wrong key-set, {app_id:?}"
37        );
38    }
39
40    // mk_syn_applied_id & friends.
41    #[track_caller]
42    pub(crate) fn mk_syn_applied_id(&self, i: Id, m: SlotMap) -> AppliedId {
43        let app_id = AppliedId::new(i, m);
44
45        if CHECKS {
46            self.check_syn_applied_id(&app_id);
47        }
48
49        app_id
50    }
51
52    #[track_caller]
53    pub(crate) fn mk_syn_identity_applied_id(&self, i: Id) -> AppliedId {
54        self.mk_syn_applied_id(i, SlotMap::identity(&self.syn_slots(i)))
55    }
56
57    #[track_caller]
58    pub(crate) fn check_syn_applied_id(&self, app_id: &AppliedId) {
59        app_id.check();
60        assert_eq!(
61            self.syn_slots(app_id.id),
62            app_id.m.keys(),
63            "checking syn AppliedId failed: Wrong key-set, {app_id:?}"
64        );
65    }
66
67    pub fn check(&self) {
68        // Checks whether the hashcons / usages are correct.
69        // And also checks that each Shape comes up in at most one EClass!
70        let mut hashcons = HashMap::default();
71        let mut usages = HashMap::default();
72
73        for (i, _) in &self.classes {
74            usages.insert(*i, HashSet::default());
75        }
76
77        // redundancy-check for leaders.
78        // TODO add a similar check for followers, using unionfind_get.
79        for (i, _) in &self.classes {
80            if !self.is_alive(*i) {
81                continue;
82            }
83
84            // There should be no symmetries witnessed by the unionfind.
85            // It would make stuff just weird.
86            let sem = self.mk_sem_identity_applied_id(*i);
87            let sem2 = self.find_applied_id(&sem);
88            assert_eq!(sem, sem2);
89
90            #[cfg(feature = "explanations")]
91            {
92                let c = &self.classes[i];
93                let eq = self.proven_unionfind_get(*i).proof.equ();
94                // eq.l.m :: slots(i) -> X
95                // eq.r.m :: slots(i) -> X
96                let tmp = eq.l.m.compose_partial(&eq.r.m.inverse());
97                assert!(tmp.is_perm());
98                assert_eq!(c.slots, tmp.keys());
99                assert_eq!(c.slots, tmp.values());
100            }
101        }
102
103        for (i, c) in &self.classes {
104            for sh in c.nodes.keys() {
105                assert!(!hashcons.contains_key(sh));
106                hashcons.insert(sh.clone(), *i);
107
108                for ref_id in sh.ids() {
109                    usages.get_mut(&ref_id).unwrap().insert(sh.clone());
110                }
111            }
112        }
113
114        assert_eq!(hashcons, self.hashcons);
115        for (i, c) in &self.classes {
116            assert_eq!(usages[&i], c.usages);
117        }
118
119        for (_, c) in &self.classes {
120            for p in c.group.all_perms() {
121                p.check();
122            }
123        }
124
125        // check that self.classes contains exactly these classes which point to themselves in the unionfind.
126        let all_keys = self
127            .unionfind_iter()
128            .map(|(x, _)| x)
129            .collect::<HashSet<_>>();
130        let all_values = self
131            .unionfind_iter()
132            .map(|(_, x)| x.id)
133            .collect::<HashSet<_>>();
134        let all_classes = self.classes.keys().copied().collect::<HashSet<_>>();
135        let all: HashSet<Id> = &(&all_keys | &all_values) | &all_classes;
136        for i in all {
137            // if they point to themselves, they should do it using the identity.
138            if self.is_alive(i) {
139                assert_eq!(self.unionfind_get(i), self.mk_sem_identity_applied_id(i));
140            } else {
141                assert!(self.classes[&i].nodes.is_empty());
142                for sh in &self.classes[&i].usages {
143                    assert_eq!(self.pending.get(&sh), Some(&PendingType::Full));
144                }
145            }
146        }
147
148        // check that no EClass has Slot(0) in its API.
149        for (_, c) in &self.classes {
150            assert!(!c.slots.contains(&Slot::numeric(0)));
151        }
152
153        // Check that the Unionfind has valid AppliedIds.
154        for (_, app_id) in self.unionfind_iter() {
155            check_internal_applied_id::<L, N>(self, &app_id);
156        }
157
158        // Check that all ENodes are valid.
159        for (_, c) in &self.classes {
160            for (sh, ProvenSourceNode { elem: bij, .. }) in &c.nodes {
161                let real = sh.apply_slotmap(bij);
162                assert!(real.slots().is_superset(&c.slots));
163
164                if self.pending.get(&sh) == Some(&PendingType::Full) {
165                    continue;
166                }
167
168                let (computed_sh, computed_bij) = self.shape(&real);
169                assert_eq!(&computed_sh, sh);
170
171                // computed_bij :: shape-slots -> slots(i)
172                // bij :: shape-slots -> slots(i)
173                let perm = computed_bij.inverse().compose_partial(&bij);
174                assert!(c.group.contains(&perm));
175
176                for x in real.applied_id_occurrences() {
177                    check_internal_applied_id::<L, N>(self, &x);
178                }
179            }
180        }
181
182        fn check_internal_applied_id<L: Language, N: Analysis<L>>(
183            eg: &EGraph<L, N>,
184            app_id: &AppliedId,
185        ) {
186            // 1. the app_id needs to be normalized!
187            let y = eg.find_applied_id(app_id);
188            assert_eq!(app_id, &y);
189
190            // 2. It needs to have exactly the same slots as the underlying EClass.
191            assert_eq!(&app_id.m.keys(), &eg.classes[&app_id.id].slots);
192        }
193    }
194}