disjunction

main
Ziyang Hu 2 years ago
parent 439b237723
commit 2aa470d0bb

@ -9,7 +9,7 @@ authors = ["Ziyang Hu"]
[dependencies]
casey = "0.3.3"
uuid = { version = "0.8", features = ["v1", "v4", "serde"] }
uuid = { version = "1.1.2", features = ["v1", "v4", "serde"] }
rand = "0.8.5"
anyhow = "1.0"
lazy_static = "1.4.0"

@ -2,7 +2,7 @@
* [x] predicates
* [x] negation
* [ ] disjunction
* [x] disjunction
* [ ] aggregation
* [ ] stratum
* [ ] magic sets

@ -28,6 +28,9 @@ pub enum Expr {
}
impl Expr {
pub(crate) fn negate(self) -> Self {
Expr::Apply(&OP_NOT, Box::new([self]))
}
pub(crate) fn fill_binding_indices(&mut self, binding_map: &BTreeMap<Keyword, usize>) {
match self {
Expr::Binding(k, idx) => {
@ -55,9 +58,9 @@ impl Expr {
}
// nested not's can accumulate during conversion to normal form
if let Expr::Apply(op1, arg1) = self {
if op1.name == "OP_NOT" {
if op1.name == OP_NOT.name {
if let Some(Expr::Apply(op2, arg2)) = arg1.first() {
if op2.name == "OP_NOT" {
if op2.name == OP_NOT.name {
let mut new_self = arg2[0].clone();
mem::swap(self, &mut new_self);
}

@ -13,8 +13,8 @@ use crate::data::keyword::{Keyword, PROG_ENTRY};
use crate::data::value::DataValue;
use crate::parse::triple::TxError;
use crate::query::compile::{
Atom, AttrTripleAtom, BindingHeadTerm, DatalogProgram, LogicalAtom, QueryCompilationError,
Rule, RuleApplyAtom, RuleSet, Term,
Atom, AttrTripleAtom, BindingHeadTerm, DatalogProgram, QueryCompilationError, Rule,
RuleApplyAtom, RuleSet, Term,
};
use crate::runtime::transact::SessionTx;
use crate::{EntityId, Validity};
@ -308,15 +308,19 @@ impl SessionTx {
)
.into());
}
Ok(iter::once((
rule_name,
Rule {
head: rule_head,
body: rule_body,
vld,
},
)))
Ok(Atom::Conjunction(rule_body)
.disjunctive_normal_form()
.into_iter()
.map(move |rule_body| {
(
rule_name.clone(),
Rule {
head: rule_head.clone(),
body: rule_body,
vld,
},
)
}))
}
fn reorder_rule_body_for_negations(clauses: Vec<Atom>) -> Result<Vec<Atom>> {
@ -328,10 +332,11 @@ impl SessionTx {
let mut negations_with_meta = negations
.into_iter()
.map(|p| {
let p = p.into_negation().unwrap();
let p = p.into_negated().unwrap();
let mut bindings = Default::default();
p.collect_bindings(&mut bindings);
let valid_bindings: BTreeSet<_> = bindings.intersection(&seen_bindings).cloned().collect();
let valid_bindings: BTreeSet<_> =
bindings.intersection(&seen_bindings).cloned().collect();
(Some(p), valid_bindings)
})
.collect_vec();
@ -346,7 +351,7 @@ impl SessionTx {
}
if seen_bindings.is_superset(pred_bindings) {
let negated = negated.take().unwrap();
ret.push(Atom::Logical(LogicalAtom::Negation(Box::new(negated))));
ret.push(Atom::Negation(Box::new(negated)));
}
}
}
@ -432,7 +437,7 @@ impl SessionTx {
Ok(match k as &str {
"not_exists" => {
let arg = self.parse_atom(v, vld)?;
Atom::Logical(LogicalAtom::Negation(Box::new(arg)))
Atom::Negation(Box::new(arg))
}
"conj" | "disj" => {
let args = v
@ -444,9 +449,9 @@ impl SessionTx {
.map(|a| self.parse_atom(a, vld))
.try_collect()?;
if k == "conj" {
Atom::Logical(LogicalAtom::Conjunction(args))
Atom::Conjunction(args)
} else {
Atom::Logical(LogicalAtom::Disjunction(args))
Atom::Disjunction(args)
}
}
_ => unreachable!(),

@ -111,26 +111,14 @@ pub struct RuleApplyAtom {
pub(crate) args: Vec<Term<DataValue>>,
}
#[derive(Clone, Debug)]
pub enum LogicalAtom {
Negation(Box<Atom>),
Conjunction(Vec<Atom>),
Disjunction(Vec<Atom>),
}
#[derive(Clone, Debug)]
pub struct BindUnification {
left: Term<DataValue>,
right: Expr,
}
#[derive(Clone, Debug)]
pub enum Atom {
AttrTriple(AttrTripleAtom),
Rule(RuleApplyAtom),
Predicate(Expr),
Logical(LogicalAtom),
BindUnify(BindUnification),
Negation(Box<Atom>),
Conjunction(Vec<Atom>),
Disjunction(Vec<Atom>),
}
impl Atom {
@ -138,7 +126,7 @@ impl Atom {
matches!(self, Atom::Predicate(_))
}
pub(crate) fn is_negation(&self) -> bool {
matches!(self, Atom::Logical(LogicalAtom::Negation(_)))
matches!(self, Atom::Negation(_))
}
pub(crate) fn into_predicate(self) -> Option<Expr> {
match self {
@ -146,10 +134,10 @@ impl Atom {
_ => None,
}
}
pub(crate) fn into_negation(self) -> Option<Atom> {
pub(crate) fn into_negated(self) -> Option<Atom> {
match self {
Atom::Logical(LogicalAtom::Negation(a)) => Some(*a),
_ => None
Atom::Negation(a) => Some(*a),
_ => None,
}
}
pub(crate) fn collect_bindings(&self, coll: &mut BTreeSet<Keyword>) {
@ -166,12 +154,14 @@ impl Atom {
Atom::Predicate(p) => {
p.collect_bindings(coll);
}
Atom::Logical(_) => {
// logical operators introduce no new bindings
}
Atom::BindUnify(_) => {
todo!()
// negations introduce no new bindings
Atom::Negation(_) => {}
Atom::Conjunction(args) => {
for a in args {
a.collect_bindings(coll);
}
}
Atom::Disjunction(_) => unreachable!(),
}
}
}
@ -427,198 +417,212 @@ impl SessionTx {
ret = ret.join(right, prev_joiner_vars, right_joiner_vars);
}
Atom::Predicate(p) => ret = ret.filter(p.clone()),
Atom::Logical(l) => match l {
LogicalAtom::Negation(r) => match r as &Atom {
Atom::AttrTriple(a_triple) => {
match (&a_triple.entity, &a_triple.value) {
(Term::Const(eid), Term::Var(v_kw)) => {
let temp_join_key_left = gen_temp_kw();
let temp_join_key_right = gen_temp_kw();
let const_rel = Relation::singlet(
vec![temp_join_key_left.clone()],
vec![DataValue::EnId(*eid)],
);
if ret.is_unit() {
ret = const_rel;
Atom::Negation(r) => match r as &Atom {
Atom::AttrTriple(a_triple) => {
match (&a_triple.entity, &a_triple.value) {
(Term::Const(eid), Term::Var(v_kw)) => {
let temp_join_key_left = gen_temp_kw();
let temp_join_key_right = gen_temp_kw();
let const_rel = Relation::singlet(
vec![temp_join_key_left.clone()],
vec![DataValue::EnId(*eid)],
);
if ret.is_unit() {
ret = const_rel;
} else {
ret = ret.cartesian_join(const_rel);
}
let mut join_left_keys = vec![temp_join_key_left];
let mut join_right_keys = vec![temp_join_key_right.clone()];
let v_kw = {
if seen_variables.contains(v_kw) {
let ret = gen_temp_kw();
// to_eliminate.insert(ret.clone());
join_left_keys.push(v_kw.clone());
join_right_keys.push(ret.clone());
ret
} else {
ret = ret.cartesian_join(const_rel);
seen_variables.insert(v_kw.clone());
v_kw.clone()
}
let mut join_left_keys = vec![temp_join_key_left];
let mut join_right_keys = vec![temp_join_key_right.clone()];
let v_kw = {
if seen_variables.contains(v_kw) {
let ret = gen_temp_kw();
// to_eliminate.insert(ret.clone());
join_left_keys.push(v_kw.clone());
join_right_keys.push(ret.clone());
ret
} else {
seen_variables.insert(v_kw.clone());
v_kw.clone()
}
};
let right =
Relation::triple(a_triple.attr.clone(), vld, temp_join_key_right, v_kw);
debug_assert_eq!(join_left_keys.len(), join_right_keys.len());
ret = ret.neg_join(right, join_left_keys, join_right_keys);
};
let right = Relation::triple(
a_triple.attr.clone(),
vld,
temp_join_key_right,
v_kw,
);
debug_assert_eq!(join_left_keys.len(), join_right_keys.len());
ret = ret.neg_join(right, join_left_keys, join_right_keys);
}
(Term::Var(e_kw), Term::Const(val)) => {
let temp_join_key_left = gen_temp_kw();
let temp_join_key_right = gen_temp_kw();
let const_rel = Relation::singlet(
vec![temp_join_key_left.clone()],
vec![val.clone()],
);
if ret.is_unit() {
ret = const_rel;
} else {
ret = ret.cartesian_join(const_rel);
}
(Term::Var(e_kw), Term::Const(val)) => {
let temp_join_key_left = gen_temp_kw();
let temp_join_key_right = gen_temp_kw();
let const_rel =
Relation::singlet(vec![temp_join_key_left.clone()], vec![val.clone()]);
if ret.is_unit() {
ret = const_rel;
let mut join_left_keys = vec![temp_join_key_left];
let mut join_right_keys = vec![temp_join_key_right.clone()];
let e_kw = {
if seen_variables.contains(&e_kw) {
let ret = gen_temp_kw();
join_left_keys.push(e_kw.clone());
join_right_keys.push(ret.clone());
ret
} else {
ret = ret.cartesian_join(const_rel);
seen_variables.insert(e_kw.clone());
e_kw.clone()
}
let mut join_left_keys = vec![temp_join_key_left];
let mut join_right_keys = vec![temp_join_key_right.clone()];
let e_kw = {
if seen_variables.contains(&e_kw) {
let ret = gen_temp_kw();
join_left_keys.push(e_kw.clone());
join_right_keys.push(ret.clone());
ret
} else {
seen_variables.insert(e_kw.clone());
e_kw.clone()
}
};
let right =
Relation::triple(a_triple.attr.clone(), vld, e_kw, temp_join_key_right);
debug_assert_eq!(join_left_keys.len(), join_right_keys.len());
ret = ret.neg_join(right, join_left_keys, join_right_keys);
};
let right = Relation::triple(
a_triple.attr.clone(),
vld,
e_kw,
temp_join_key_right,
);
debug_assert_eq!(join_left_keys.len(), join_right_keys.len());
ret = ret.neg_join(right, join_left_keys, join_right_keys);
}
(Term::Var(e_kw), Term::Var(v_kw)) => {
let mut join_left_keys = vec![];
let mut join_right_keys = vec![];
if e_kw == v_kw {
unimplemented!();
}
(Term::Var(e_kw), Term::Var(v_kw)) => {
let mut join_left_keys = vec![];
let mut join_right_keys = vec![];
if e_kw == v_kw {
unimplemented!();
}
let e_kw = {
if seen_variables.contains(&e_kw) {
let ret = gen_temp_kw();
join_left_keys.push(e_kw.clone());
join_right_keys.push(ret.clone());
ret
} else {
seen_variables.insert(e_kw.clone());
e_kw.clone()
}
};
let v_kw = {
if seen_variables.contains(v_kw) {
let ret = gen_temp_kw();
join_left_keys.push(v_kw.clone());
join_right_keys.push(ret.clone());
ret
} else {
seen_variables.insert(v_kw.clone());
v_kw.clone()
}
};
if join_right_keys.is_empty() {
return Err(QueryCompilationError::NegationSafety(vec![e_kw.clone(), v_kw.clone()]).into());
}
let right = Relation::triple(a_triple.attr.clone(), vld, e_kw, v_kw);
if ret.is_unit() {
ret = right;
let e_kw = {
if seen_variables.contains(&e_kw) {
let ret = gen_temp_kw();
join_left_keys.push(e_kw.clone());
join_right_keys.push(ret.clone());
ret
} else {
debug_assert_eq!(join_left_keys.len(), join_right_keys.len());
ret = ret.neg_join(right, join_left_keys, join_right_keys);
seen_variables.insert(e_kw.clone());
e_kw.clone()
}
}
(Term::Const(eid), Term::Const(val)) => {
let (left_var_1, left_var_2) = (gen_temp_kw(), gen_temp_kw());
let const_rel = Relation::singlet(
vec![left_var_1.clone(), left_var_2.clone()],
vec![DataValue::EnId(*eid), val.clone()],
);
if ret.is_unit() {
ret = const_rel;
};
let v_kw = {
if seen_variables.contains(v_kw) {
let ret = gen_temp_kw();
join_left_keys.push(v_kw.clone());
join_right_keys.push(ret.clone());
ret
} else {
ret = ret.cartesian_join(const_rel);
seen_variables.insert(v_kw.clone());
v_kw.clone()
}
let (right_var_1, right_var_2) = (gen_temp_kw(), gen_temp_kw());
let right = Relation::triple(
a_triple.attr.clone(),
vld,
right_var_1.clone(),
right_var_2.clone(),
);
ret = ret.neg_join(
right,
vec![left_var_1.clone(), left_var_2.clone()],
vec![right_var_1.clone(), right_var_2.clone()],
);
};
if join_right_keys.is_empty() {
return Err(QueryCompilationError::NegationSafety(vec![
e_kw.clone(),
v_kw.clone(),
])
.into());
}
let right =
Relation::triple(a_triple.attr.clone(), vld, e_kw, v_kw);
if ret.is_unit() {
ret = right;
} else {
debug_assert_eq!(join_left_keys.len(), join_right_keys.len());
ret = ret.neg_join(right, join_left_keys, join_right_keys);
}
}
}
Atom::Rule(rule_app) => {
let (store, arity) = stores
.get(&rule_app.name)
.ok_or_else(|| QueryCompilationError::UndefinedRule(rule_app.name.clone()))?
.clone();
if arity != rule_app.args.len() {
return Err(
QueryCompilationError::ArityMismatch(rule_app.name.clone()).into()
(Term::Const(eid), Term::Const(val)) => {
let (left_var_1, left_var_2) = (gen_temp_kw(), gen_temp_kw());
let const_rel = Relation::singlet(
vec![left_var_1.clone(), left_var_2.clone()],
vec![DataValue::EnId(*eid), val.clone()],
);
if ret.is_unit() {
ret = const_rel;
} else {
ret = ret.cartesian_join(const_rel);
}
let (right_var_1, right_var_2) = (gen_temp_kw(), gen_temp_kw());
let right = Relation::triple(
a_triple.attr.clone(),
vld,
right_var_1.clone(),
right_var_2.clone(),
);
ret = ret.neg_join(
right,
vec![left_var_1.clone(), left_var_2.clone()],
vec![right_var_1.clone(), right_var_2.clone()],
);
}
}
}
Atom::Rule(rule_app) => {
let (store, arity) = stores
.get(&rule_app.name)
.ok_or_else(|| {
QueryCompilationError::UndefinedRule(rule_app.name.clone())
})?
.clone();
if arity != rule_app.args.len() {
return Err(QueryCompilationError::ArityMismatch(
rule_app.name.clone(),
)
.into());
}
let mut prev_joiner_vars = vec![];
let mut temp_left_bindings = vec![];
let mut temp_left_joiner_vals = vec![];
let mut right_joiner_vars = vec![];
let mut right_vars = vec![];
for term in &rule_app.args {
match term {
Term::Var(var) => {
if seen_variables.contains(var) {
prev_joiner_vars.push(var.clone());
let rk = gen_temp_kw();
right_vars.push(rk.clone());
right_joiner_vars.push(rk);
} else {
seen_variables.insert(var.clone());
right_vars.push(var.clone());
}
}
Term::Const(constant) => {
temp_left_joiner_vals.push(constant.clone());
let left_kw = gen_temp_kw();
prev_joiner_vars.push(left_kw.clone());
temp_left_bindings.push(left_kw);
let right_kw = gen_temp_kw();
right_joiner_vars.push(right_kw.clone());
right_vars.push(right_kw);
let mut prev_joiner_vars = vec![];
let mut temp_left_bindings = vec![];
let mut temp_left_joiner_vals = vec![];
let mut right_joiner_vars = vec![];
let mut right_vars = vec![];
for term in &rule_app.args {
match term {
Term::Var(var) => {
if seen_variables.contains(var) {
prev_joiner_vars.push(var.clone());
let rk = gen_temp_kw();
right_vars.push(rk.clone());
right_joiner_vars.push(rk);
} else {
seen_variables.insert(var.clone());
right_vars.push(var.clone());
}
}
Term::Const(constant) => {
temp_left_joiner_vals.push(constant.clone());
let left_kw = gen_temp_kw();
prev_joiner_vars.push(left_kw.clone());
temp_left_bindings.push(left_kw);
let right_kw = gen_temp_kw();
right_joiner_vars.push(right_kw.clone());
right_vars.push(right_kw);
}
}
}
if !temp_left_joiner_vals.is_empty() {
let const_joiner =
Relation::singlet(temp_left_bindings, temp_left_joiner_vals);
ret = ret.cartesian_join(const_joiner);
}
let right = Relation::derived(right_vars, store);
debug_assert_eq!(prev_joiner_vars.len(), right_joiner_vars.len());
ret = ret.neg_join(right, prev_joiner_vars, right_joiner_vars);
if !temp_left_joiner_vals.is_empty() {
let const_joiner =
Relation::singlet(temp_left_bindings, temp_left_joiner_vals);
ret = ret.cartesian_join(const_joiner);
}
_ => unreachable!(),
},
let right = Relation::derived(right_vars, store);
debug_assert_eq!(prev_joiner_vars.len(), right_joiner_vars.len());
ret = ret.neg_join(right, prev_joiner_vars, right_joiner_vars);
}
_ => unreachable!(),
},
Atom::BindUnify(_) => {
todo!()
}
Atom::Conjunction(_) => unreachable!(),
Atom::Disjunction(_) => unreachable!()
}
}

@ -0,0 +1,137 @@
use anyhow::Result;
use itertools::Itertools;
use crate::data::expr::{get_op, Expr};
use crate::query::compile::Atom;
impl Atom {
pub(crate) fn is_disjunction(&self) -> bool {
matches!(self, Atom::Disjunction(_))
}
pub(crate) fn is_conjunction(&self) -> bool {
matches!(self, Atom::Conjunction(_))
}
pub(crate) fn negation_normal_form(self) -> Self {
match self {
a @ (Atom::AttrTriple(_) | Atom::Rule(_) | Atom::Predicate(_)) => a,
Atom::Conjunction(args) => {
Atom::Conjunction(args.into_iter().map(|a| a.negation_normal_form()).collect())
}
Atom::Disjunction(args) => {
Atom::Disjunction(args.into_iter().map(|a| a.negation_normal_form()).collect())
}
Atom::Negation(arg) => match *arg {
a @ (Atom::AttrTriple(_) | Atom::Rule(_)) => Atom::Negation(Box::new(a)),
Atom::Predicate(p) => Atom::Predicate(p.negate()),
Atom::Negation(inner) => inner.negation_normal_form(),
Atom::Conjunction(args) => Atom::Disjunction(
args.into_iter()
.map(|a| Atom::Negation(Box::new(a)).negation_normal_form())
.collect(),
),
Atom::Disjunction(args) => Atom::Conjunction(
args.into_iter()
.map(|a| Atom::Negation(Box::new(a)).negation_normal_form())
.collect(),
),
},
}
}
pub(crate) fn disjunctive_normal_form(self) -> Vec<Vec<Self>> {
match self.negation_normal_form().do_disjunctive_normal_form() {
Atom::Disjunction(atoms) => atoms
.into_iter()
.map(|a| match a {
Atom::Conjunction(v) => v,
_ => unreachable!(),
})
.collect_vec(),
_ => unreachable!(),
}
}
fn get_disjunctive_args(self) -> Option<Vec<Self>> {
match self {
Atom::Disjunction(v) => Some(v),
_ => None,
}
}
fn get_conjunctive_args(self) -> Option<Vec<Self>> {
match self {
Atom::Conjunction(v) => Some(v),
_ => None,
}
}
fn do_disjunctive_normal_form(self) -> Self {
// invariants: the input is already in negation normal form
// the return value is a disjunction of conjunctions, with no nesting
match self {
// invariant: results is disjunction of conjunctions
a @ (Atom::AttrTriple(_) | Atom::Rule(_) | Atom::Predicate(_) | Atom::Negation(_)) => {
Atom::Disjunction(vec![Atom::Conjunction(vec![a])])
}
Atom::Disjunction(args) => {
let mut ret = vec![];
for arg in args {
for a in arg
.do_disjunctive_normal_form()
.get_disjunctive_args()
.unwrap()
{
ret.push(a);
}
}
Atom::Disjunction(ret)
}
Atom::Conjunction(args) => {
let mut args = args.into_iter().map(|a| a.do_disjunctive_normal_form());
let mut result = args.next().unwrap();
for a in args {
result = result.conjunctive_to_disjunctive_de_morgen(a)
}
result
}
}
}
fn conjunctive_to_disjunctive_de_morgen(self, other: Self) -> Self {
// invariants: self and other are both already in disjunctive normal form, which are to be conjuncted together
// the return value must be in disjunctive normal form
let mut ret = vec![];
let right_args = other
.get_disjunctive_args()
.unwrap()
.into_iter()
.map(|a| a.get_conjunctive_args().unwrap())
.collect_vec();
for left in self.get_disjunctive_args().unwrap() {
let left = left.get_conjunctive_args().unwrap();
for right in &right_args {
let mut current = left.clone();
current.extend_from_slice(right);
ret.push(Atom::Conjunction(current))
}
}
Atom::Disjunction(ret)
}
}
#[cfg(test)]
mod tests {
use crate::data::expr::Expr;
use crate::data::value::DataValue;
use crate::query::compile::Atom;
#[test]
fn normal_forms() {
let a = Atom::Conjunction(vec![
Atom::Disjunction(vec![
Atom::Negation(Box::new(Atom::Conjunction(vec![
Atom::Negation(Box::new(Atom::Predicate(Expr::Const(DataValue::Int(1))))),
Atom::Predicate(Expr::Const(DataValue::Int(2))),
]))),
Atom::Predicate(Expr::Const(DataValue::Int(3))),
]),
Atom::Predicate(Expr::Const(DataValue::Int(4))),
]);
dbg!(a.disjunctive_normal_form());
}
}

@ -17,6 +17,7 @@ pub(crate) mod compile;
pub(crate) mod eval;
pub(crate) mod pull;
pub(crate) mod relation;
pub(crate) mod logical;
impl SessionTx {
pub fn run_query(&mut self, payload: &JsonValue) -> Result<QueryResult> {

Loading…
Cancel
Save