smt_scope/analysis/
dependencies.rs1use std::{cmp::Ordering, ops::Deref};
2
3use fxhash::FxHashSet;
4
5use crate::{
6 items::{QuantIdx, QuantPatVec},
7 BoxSlice, FxHashMap, TiVec, Z3Parser,
8};
9
10use super::{
11 raw::{IndexesInstGraph, NodeKind},
12 InstGraph, RawNodeIndex,
13};
14
15#[derive(Clone)]
16pub struct QuantifierAnalysis(QuantPatVec<QuantPatInfo>);
17
18#[derive(Debug, Default, Clone)]
19pub struct QuantPatInfo {
20 pub costs: f64,
23 #[cfg(any())]
25 pub children: f64,
28 pub direct_deps: Vec<DirectDep>,
31}
32
33#[derive(Debug, Clone, Default)]
34pub struct DirectDep {
35 pub enode: FxHashMap<Option<QuantIdx>, u32>,
36 pub eqs: FxHashMap<BoxSlice<QuantIdx>, u32>,
37}
38
39type TransQuantAnalaysis = TiVec<QuantIdx, FxHashSet<QuantIdx>>;
40
41impl Deref for QuantifierAnalysis {
42 type Target = QuantPatVec<QuantPatInfo>;
43 fn deref(&self) -> &Self::Target {
44 &self.0
45 }
46}
47
48impl QuantifierAnalysis {
49 pub fn new(parser: &Z3Parser, inst_graph: &InstGraph) -> Self {
50 let mut quant_deps = FxHashMap::<RawNodeIndex, FxHashSet<QuantIdx>>::default();
51 for node in inst_graph.subgraphs.topo_node_indices() {
52 let qdeps = quant_deps.entry(node).or_default();
53 let ig = &inst_graph.raw[node];
54 if let NodeKind::Instantiation(i) = *ig.kind() {
55 if let Some(q) = parser.get_inst(i).match_.kind.quant_idx() {
56 qdeps.insert(q);
57 continue;
58 }
59 }
60 let graph = &inst_graph.raw.graph;
61 for parent in graph.neighbors_directed(node.0, petgraph::Direction::Incoming) {
62 let parents = RawNodeIndex(parent);
63 let parent = quant_deps[&parents].clone();
64 quant_deps.get_mut(&node).unwrap().extend(parent);
65 }
66 }
67
68 let mut self_ = QuantifierAnalysis(parser.new_quant_pat_vec(|_| QuantPatInfo::default()));
69 for data in parser.instantiations_data() {
70 let Some(qpat) = data.match_.kind.quant_pat() else {
71 continue;
72 };
73 let qinfo = &mut self_.0[qpat];
74
75 let ginst = &inst_graph.raw[data.iidx];
76 qinfo.costs += ginst.cost;
77
78 #[cfg(any())]
80 for &child in ginst.children.insts.iter() {
81 let cq = parser.get_inst(child).match_.kind.quant_idx();
82 if cq.is_some_and(|q| q == qpat.quant) {
83 continue;
85 }
86 let parents = inst_graph.raw[child].parents.insts.len() as f64;
87 qinfo.children += 1.0 / parents;
88 }
89
90 let pat = parser.get_pattern_term(qpat);
91 let subpats = pat.map(|p| p.child_ids.len()).unwrap_or_default();
92 for (i, blame) in data.match_.pattern_matches().enumerate() {
93 if i == qinfo.direct_deps.len() {
95 if i >= subpats {
96 break;
101 }
102 qinfo.direct_deps.push(DirectDep::default());
103 }
104 let direct_dep = &mut qinfo.direct_deps[i];
105
106 let created_by = parser[blame.enode].blame.inst();
107 let created_by =
108 created_by.and_then(|iidx| parser.get_inst(iidx).match_.kind.quant_idx());
109 *direct_dep.enode.entry(created_by).or_default() += 1;
110
111 for &eq in blame.equalities.iter() {
112 let nidx = eq.index(&inst_graph.raw);
113 let quants = &quant_deps[&nidx];
114 let mut quants: BoxSlice<_> = quants.iter().copied().collect();
115 quants.sort();
116 *direct_dep.eqs.entry(quants).or_default() += 1;
117 }
118 }
119 }
120 self_
121 }
122
123 pub fn total_costs(&self) -> f64 {
124 self.iter_enumerated().map(|(_, info)| info.costs).sum()
125 }
126
127 pub fn quant_sum_cost(&self, quant: QuantIdx) -> f64 {
128 let data = &self.0 .0[quant];
129 data.mbqi.costs + data.pats.iter().map(|d| d.costs).sum::<f64>()
130 }
131
132 pub fn quants_costs(&self) -> impl Iterator<Item = (QuantIdx, f64)> + '_ {
133 self.0
134 .0
135 .iter_enumerated()
136 .map(|(quant, data)| (quant, data.iter_enumerated().map(|(_, d)| d.costs).sum()))
137 }
138
139 #[cfg(any())]
141 pub fn quants_children(&self) -> impl Iterator<Item = (QuantIdx, f64)> + '_ {
142 self.0
143 .0
144 .iter_enumerated()
145 .map(|(quant, data)| (quant, data.iter_enumerated().map(|(_, d)| d.children).sum()))
146 }
147
148 pub fn calculate_transitive(&self, mut steps: Option<u32>) -> TransQuantAnalaysis {
149 let mut initial: TiVec<QuantIdx, _> =
150 (0..self.0 .0.len()).map(|_| FxHashSet::default()).collect();
151 for (qpat, data) in self.iter_enumerated() {
152 initial[qpat.quant].extend(data.keys());
153 }
154 while steps.is_none_or(|steps| steps != 0) {
155 if !self.calculate_transitive_one(&mut initial) {
156 break;
157 }
158 if let Some(steps) = &mut steps {
159 *steps -= 1;
160 }
161 }
162 initial
163 }
164 fn calculate_transitive_one(&self, analysis: &mut TransQuantAnalaysis) -> bool {
165 let mut changed = false;
166 for (idx, info) in self.iter_enumerated() {
167 for ddep in info.keys() {
168 let (curr, ddep) = match idx.quant.cmp(&ddep) {
169 Ordering::Less => {
170 let (left, right) = analysis.split_at_mut(ddep);
171 (&mut left[idx.quant], right.first().unwrap())
172 }
173 Ordering::Greater => {
174 let (left, right) = analysis.split_at_mut(idx.quant);
175 (right.first_mut().unwrap(), &left[ddep])
176 }
177 Ordering::Equal => continue,
178 };
179 let old_len = curr.len();
180 curr.extend(ddep);
181 changed |= old_len != curr.len();
182 }
183 }
184 changed
185 }
186}
187
188impl QuantPatInfo {
189 pub fn keys(&self) -> impl Iterator<Item = QuantIdx> + '_ {
192 self.iter().map(|(q, _)| q)
193 }
194
195 pub fn values(&self) -> impl Iterator<Item = u32> + '_ {
198 self.iter().map(|(_, c)| c)
199 }
200
201 pub fn iter(&self) -> impl Iterator<Item = (QuantIdx, u32)> + '_ {
204 self.direct_deps.iter().flat_map(|ddep| {
205 let enode = ddep.enode.iter().filter_map(|(q, c)| q.zip(Some(*c)));
206 let eqs = ddep
207 .eqs
208 .iter()
209 .flat_map(|(q, c)| q.iter().map(move |q| (*q, *c)));
210 enode.chain(eqs)
211 })
212 }
213}