|
|
|
@ -6,7 +6,9 @@ use itertools::Itertools;
|
|
|
|
|
|
|
|
|
|
use crate::data::keyword::{Keyword, PROG_ENTRY};
|
|
|
|
|
use crate::query::compile::{Atom, DatalogProgram, RuleSet};
|
|
|
|
|
use crate::query::graph::{reachable_components, strongly_connected_components, Graph, StratifiedGraph, generalized_kahn};
|
|
|
|
|
use crate::query::graph::{
|
|
|
|
|
generalized_kahn, reachable_components, strongly_connected_components, Graph, StratifiedGraph,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#[derive(thiserror::Error, Debug)]
|
|
|
|
|
pub enum GraphError {
|
|
|
|
@ -16,7 +18,6 @@ pub enum GraphError {
|
|
|
|
|
GraphNotStratified(BTreeSet<Keyword>),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl Atom {
|
|
|
|
|
fn contained_rules(&self) -> BTreeMap<&Keyword, bool> {
|
|
|
|
|
match self {
|
|
|
|
@ -27,22 +28,23 @@ impl Atom {
|
|
|
|
|
.into_iter()
|
|
|
|
|
.map(|(k, is_neg)| (k, !is_neg))
|
|
|
|
|
.collect(),
|
|
|
|
|
Atom::Conjunction(args) | Atom::Disjunction(args) => {
|
|
|
|
|
let mut ret: BTreeMap<&Keyword, bool> = Default::default();
|
|
|
|
|
for arg in args {
|
|
|
|
|
for (k, v) in arg.contained_rules() {
|
|
|
|
|
match ret.entry(k) {
|
|
|
|
|
Entry::Vacant(e) => {
|
|
|
|
|
e.insert(v);
|
|
|
|
|
}
|
|
|
|
|
Entry::Occupied(mut e) => {
|
|
|
|
|
let old = *e.get();
|
|
|
|
|
e.insert(old || v);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ret
|
|
|
|
|
Atom::Conjunction(_args) | Atom::Disjunction(_args) => {
|
|
|
|
|
panic!("expect program in disjunctive normal form");
|
|
|
|
|
// let mut ret: BTreeMap<&Keyword, bool> = Default::default();
|
|
|
|
|
// for arg in args {
|
|
|
|
|
// for (k, v) in arg.contained_rules() {
|
|
|
|
|
// match ret.entry(k) {
|
|
|
|
|
// Entry::Vacant(e) => {
|
|
|
|
|
// e.insert(v);
|
|
|
|
|
// }
|
|
|
|
|
// Entry::Occupied(mut e) => {
|
|
|
|
|
// let old = *e.get();
|
|
|
|
|
// e.insert(old || v);
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
// ret
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -79,9 +81,9 @@ fn reduce_to_graph<'a>(g: &StratifiedGraph<&'a Keyword>) -> Graph<&'a Keyword> {
|
|
|
|
|
.collect()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn verify_no_cycle(g: &StratifiedGraph<&'_ Keyword>, sccs: Vec<BTreeSet<&Keyword>>) -> Result<()> {
|
|
|
|
|
fn verify_no_cycle(g: &StratifiedGraph<&'_ Keyword>, sccs: &[BTreeSet<&Keyword>]) -> Result<()> {
|
|
|
|
|
for (k, vs) in g {
|
|
|
|
|
for scc in &sccs {
|
|
|
|
|
for scc in sccs {
|
|
|
|
|
if scc.contains(k) {
|
|
|
|
|
for (v, negated) in vs {
|
|
|
|
|
if *negated && scc.contains(v) {
|
|
|
|
@ -97,7 +99,39 @@ fn verify_no_cycle(g: &StratifiedGraph<&'_ Keyword>, sccs: Vec<BTreeSet<&Keyword
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(crate) fn stratify_program(prog: DatalogProgram) -> Result<Vec<DatalogProgram>> {
|
|
|
|
|
fn make_scc_reduced_graph<'a>(
|
|
|
|
|
sccs: &[BTreeSet<&'a Keyword>],
|
|
|
|
|
graph: &StratifiedGraph<&Keyword>,
|
|
|
|
|
) -> (BTreeMap<&'a Keyword, usize>, StratifiedGraph<usize>) {
|
|
|
|
|
let indices = sccs
|
|
|
|
|
.iter()
|
|
|
|
|
.enumerate()
|
|
|
|
|
.flat_map(|(idx, scc)| scc.iter().map(move |k| (*k, idx)))
|
|
|
|
|
.collect::<BTreeMap<_, _>>();
|
|
|
|
|
let mut ret: BTreeMap<usize, BTreeMap<usize, bool>> = Default::default();
|
|
|
|
|
for (from, tos) in graph {
|
|
|
|
|
let from_idx = *indices.get(from).unwrap();
|
|
|
|
|
let cur_entry = ret.entry(from_idx).or_default();
|
|
|
|
|
for (to, poisoned) in tos {
|
|
|
|
|
let to_idx = *indices.get(to).unwrap();
|
|
|
|
|
if from_idx == to_idx {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
match cur_entry.entry(to_idx) {
|
|
|
|
|
Entry::Vacant(e) => {
|
|
|
|
|
e.insert(*poisoned);
|
|
|
|
|
}
|
|
|
|
|
Entry::Occupied(mut e) => {
|
|
|
|
|
let old_p = *e.get();
|
|
|
|
|
e.insert(old_p || *poisoned);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
(indices, ret)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(crate) fn stratify_program(prog: &DatalogProgram) -> Result<Vec<DatalogProgram>> {
|
|
|
|
|
// prerequisite: the program is already in disjunctive normal form
|
|
|
|
|
// 0. build a graph of the program
|
|
|
|
|
let prog_entry: &Keyword = &PROG_ENTRY;
|
|
|
|
@ -115,11 +149,11 @@ pub(crate) fn stratify_program(prog: DatalogProgram) -> Result<Vec<DatalogProgra
|
|
|
|
|
// 2. prune the graph of unreachable clauses
|
|
|
|
|
let stratified_graph: StratifiedGraph<_> = stratified_graph
|
|
|
|
|
.into_iter()
|
|
|
|
|
.filter(|(k, _)| !reachable.contains(k))
|
|
|
|
|
.filter(|(k, _)| reachable.contains(k))
|
|
|
|
|
.collect();
|
|
|
|
|
let graph: Graph<_> = graph
|
|
|
|
|
.into_iter()
|
|
|
|
|
.filter(|(k, _)| !reachable.contains(k))
|
|
|
|
|
.filter(|(k, _)| reachable.contains(k))
|
|
|
|
|
.collect();
|
|
|
|
|
// 3. find SCC of the clauses
|
|
|
|
|
let sccs: Vec<BTreeSet<&Keyword>> = strongly_connected_components(&graph)
|
|
|
|
@ -127,9 +161,24 @@ pub(crate) fn stratify_program(prog: DatalogProgram) -> Result<Vec<DatalogProgra
|
|
|
|
|
.map(|scc| scc.into_iter().cloned().collect())
|
|
|
|
|
.collect_vec();
|
|
|
|
|
// 4. for each SCC, verify that no neg/agg edges are present so that it is really stratifiable
|
|
|
|
|
verify_no_cycle(&stratified_graph, sccs)?;
|
|
|
|
|
verify_no_cycle(&stratified_graph, &sccs)?;
|
|
|
|
|
// 5. build a reduced graph for the SCC's
|
|
|
|
|
let (invert_indices, reduced_graph) = make_scc_reduced_graph(&sccs, &stratified_graph);
|
|
|
|
|
// 6. topological sort the reduced graph to get a stratification
|
|
|
|
|
let sort_result = generalized_kahn(&reduced_graph, stratified_graph.len());
|
|
|
|
|
let n_strata = sort_result.len();
|
|
|
|
|
let invert_sort_result = sort_result.into_iter().enumerate().flat_map(|(stratum, indices)| {
|
|
|
|
|
indices.into_iter().map(move |idx| (idx, stratum))
|
|
|
|
|
}).collect::<BTreeMap<_, _>>();
|
|
|
|
|
// 7. translate the stratification into datalog program
|
|
|
|
|
todo!()
|
|
|
|
|
let mut ret: Vec<DatalogProgram> = vec![Default::default(); n_strata];
|
|
|
|
|
for (name, ruleset) in prog {
|
|
|
|
|
if let Some(scc_idx) = invert_indices.get(&name) {
|
|
|
|
|
let stratum_idx = *invert_sort_result.get(scc_idx).unwrap();
|
|
|
|
|
let target = ret.get_mut(stratum_idx).unwrap();
|
|
|
|
|
target.insert(name.clone(), ruleset.clone());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(ret)
|
|
|
|
|
}
|
|
|
|
|