use crate::ast::{Atom, BodyLiteral, Rule, Term};
use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct VertexId(pub usize);
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Vertex {
pub name: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Hyperedge {
pub predicate: String,
pub vertex_positions: Vec<Option<VertexId>>,
}
impl Hyperedge {
pub fn arity(&self) -> usize {
self.vertex_positions.len()
}
pub fn vertices(&self) -> Vec<VertexId> {
let mut seen = Vec::new();
for v in self.vertex_positions.iter().flatten() {
if !seen.contains(v) {
seen.push(*v);
}
}
seen
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HypergraphRule {
pub head_predicate: String,
pub vertices: Vec<Vertex>,
pub hyperedges: Vec<Hyperedge>,
pub head_has_aggregation: bool,
pub has_negation: bool,
pub has_is_expr: bool,
pub comparison_count: usize,
pub is_fact: bool,
}
impl HypergraphRule {
pub fn from_rule(rule: &Rule) -> Self {
let mut vertices: Vec<Vertex> = Vec::new();
let mut name_to_id: HashMap<String, VertexId> = HashMap::new();
let mut hyperedges = Vec::new();
let mut has_negation = false;
let mut has_is_expr = false;
let mut comparison_count = 0;
for literal in &rule.body {
match literal {
BodyLiteral::Positive(atom) => {
hyperedges.push(build_hyperedge(atom, &mut vertices, &mut name_to_id));
}
BodyLiteral::Negated(_) => {
has_negation = true;
}
BodyLiteral::Epistemic(_) => {
has_negation = true;
}
BodyLiteral::Comparison(_) => {
comparison_count += 1;
}
BodyLiteral::IsExpr(_) => {
has_is_expr = true;
}
BodyLiteral::Univ(_) => {
comparison_count += 1;
}
}
}
Self {
head_predicate: rule.head.predicate.clone(),
vertices,
hyperedges,
head_has_aggregation: rule.has_aggregation(),
has_negation,
has_is_expr,
comparison_count,
is_fact: rule.is_fact(),
}
}
pub fn vertex_count(&self) -> usize {
self.vertices.len()
}
pub fn hyperedge_count(&self) -> usize {
self.hyperedges.len()
}
pub fn vertex(&self, id: VertexId) -> &Vertex {
&self.vertices[id.0]
}
pub fn vertex_ids(&self) -> impl Iterator<Item = VertexId> + '_ {
(0..self.vertices.len()).map(VertexId)
}
}
fn build_hyperedge(
atom: &Atom,
vertices: &mut Vec<Vertex>,
name_to_id: &mut HashMap<String, VertexId>,
) -> Hyperedge {
let mut positions = Vec::with_capacity(atom.terms.len());
for term in &atom.terms {
let pos = match term {
Term::Variable(name) => {
if let Some(id) = name_to_id.get(name) {
Some(*id)
} else {
let id = VertexId(vertices.len());
vertices.push(Vertex { name: name.clone() });
name_to_id.insert(name.clone(), id);
Some(id)
}
}
Term::Anonymous => None,
Term::Integer(_)
| Term::Float(_)
| Term::String(_)
| Term::Symbol(_)
| Term::Aggregate(_)
| Term::List(_)
| Term::Cons { .. }
| Term::Compound { .. }
| Term::PredRef(_) => None,
};
positions.push(pos);
}
Hyperedge {
predicate: atom.predicate.clone(),
vertex_positions: positions,
}
}