asdi 0.2.5

Another Simplistic Datalog Implementation (in Rust)
Documentation
use asdi::edb::Predicate;
use asdi::idb::eval::{Evaluator, NaiveEvaluator};
use asdi::idb::query::{Query, Queryable};
use asdi::parse::parse_str;
use asdi::{Collection, ProgramCore};
use std::str::FromStr;

#[test]
fn test_socrates() {
    const PROGRAM_SOURCE: &str = r#"human("Socrates").
human("Plato").

mortal(X) <- human(X).

?- mortal("Socrates").
"#;

    print!("{}", PROGRAM_SOURCE);
    println!("-------------------------------------------------------------------------------");

    let program = parse_str(PROGRAM_SOURCE).unwrap().into_parsed();

    let p_human = program.predicates().fetch("human").unwrap();
    let p_mortal = program.predicates().fetch("mortal").unwrap();

    assert_eq!(
        program
            .extensional()
            .get(&Predicate::from_str("human").unwrap())
            .unwrap()
            .len(),
        2
    );
    assert_eq!(
        program
            .intensional()
            .get(&Predicate::from_str("mortal").unwrap())
            .unwrap()
            .len(),
        0
    );

    print!("{}", program);

    println!("-------------------------------------------------------------------------------");

    let evaluator = NaiveEvaluator::default();

    let new_intensional = evaluator.inference(&program).unwrap();

    assert_eq!(program.extensional().get(&p_human).unwrap().len(), 2);
    assert_eq!(new_intensional.get(&p_mortal).unwrap().len(), 2);

    print!("{}", program);
    println!("-------------------------------------------------------------------------------");

    let query = program.queries().iter().next().unwrap();

    let results = new_intensional.query(query).unwrap().unwrap();
    println!("{}", results);
    assert_eq!(results.schema().len(), 1);
    assert_eq!(results.len(), 1);

    print!("{} ==>\n{}", query, results);
    println!("-------------------------------------------------------------------------------");

    let query = Query::new(p_mortal, [program.variables().fetch("X").unwrap().into()]);

    let results = new_intensional.query(&query).unwrap().unwrap();
    assert_eq!(results.schema().len(), 1);
    assert_eq!(results.len(), 2);

    print!("{} ==>\n{}", query, results);
}

#[test]
fn test_wikipedia_example() {
    const PROGRAM_SOURCE: &str = r#"parent(xerces, brooke).
parent(brooke, damocles).

ancestor(X, Y) ⟵ parent(X, Y).
ancestor(X, Y) ⟵ parent(X, Z) ∧ parent(Z, Y).

?- ancestor(xerces, X).
"#;

    print!("{}", PROGRAM_SOURCE);
    println!("-------------------------------------------------------------------------------");

    let mut program = parse_str(PROGRAM_SOURCE).unwrap().into_parsed();

    let p_parent = program.predicates().fetch("parent").unwrap();
    let p_ancestor = program.predicates().fetch("ancestor").unwrap();

    assert_eq!(program.extensional().get(&p_parent).unwrap().len(), 2);
    assert_eq!(program.intensional().get(&p_ancestor).unwrap().len(), 0);

    print!("{}", program);
    println!("-------------------------------------------------------------------------------");

    let evaluator = NaiveEvaluator::default();
    let results = evaluator.inference(&program);
    program
        .intensional_mut()
        .merge_from(results.unwrap())
        .unwrap();

    assert_eq!(program.extensional().get(&p_parent).unwrap().len(), 2);
    assert_eq!(program.intensional().get(&p_ancestor).unwrap().len(), 3);

    print!("{}", program);
    println!("-------------------------------------------------------------------------------");

    let query = program.queries().iter().next().unwrap();

    let results = program.intensional().query(query).unwrap().unwrap();
    println!("{}", results);
    assert_eq!(results.schema().len(), 2);
    assert_eq!(results.len(), 2);

    print!("{} ==>\n{}", query, results);
}

#[test]
fn test_sourceforge_example() {
    const PROGRAM_SOURCE: &str = r#".assert edge(string, string).

edge(a, b).
edge(c, d).
edge(d, a).
edge(b, c).

path(X, Y) ⟵ edge(X, Y).
path(X, Y) ⟵ edge(X, Z) ∧ path(Z, Y).

?- path(X, Y).
"#;

    print!("{}", PROGRAM_SOURCE);
    println!("-------------------------------------------------------------------------------");

    let program = parse_str(PROGRAM_SOURCE).unwrap().into_parsed();

    let p_edge = program.predicates().fetch("edge").unwrap();
    let p_path = program.predicates().fetch("path").unwrap();

    assert_eq!(program.extensional().get(&p_edge).unwrap().len(), 4);
    assert_eq!(program.intensional().get(&p_path).unwrap().len(), 0);

    print!("{}", program);
    println!("-------------------------------------------------------------------------------");

    let evaluator = NaiveEvaluator::default();

    let results = evaluator.inference(&program).unwrap();

    assert_eq!(program.extensional().get(&p_edge).unwrap().len(), 4);
    assert_eq!(results.get(&p_path).unwrap().len(), 16);

    print!("{}", program);
    println!("-------------------------------------------------------------------------------");

    let query = program.queries().iter().next().unwrap();

    let results = results.query(query).unwrap().unwrap();
    assert_eq!(results.schema().len(), 2);
    assert_eq!(results.len(), 16);

    print!("{} ==>\n{}", query, results);
}

#[test]
fn test_rdfs() {
    const PROGRAM_SOURCE: &str = r#".assert triple(subject: string, predicate: string, object: string).

% Section 2.1: Resoure
triple(rdfs:Resource, rdf:type, rdfs:Class).

% Section 2.2: Class
triple(rdfs:Class, rdf:type, rdfs:Class).

% Section 2.3: Literal
triple(rdfs:Literal, rdf:type, rdfs:Class).
triple(rdfs:Literal, rdfs:subClassOf, rdfs:Resource).

% Section 2.4: Datatype
triple(rdfs:Datatype, rdf:type, rdfs:Class).
triple(rdfs:Datatype, rdfs:subClassOf, rdfs:Class).

% Section 2.5: langString
triple(rdf:langString, rdf:type, rdfs:Datatype).
triple(rdf:langString, rdfs:subClassOf, rdfs:Literal).

% Section 2.6: HTML
triple(rdf:HTML, rdf:type, rdfs:Datatype).
triple(rdf:HTML, rdfs:subClassOf, rdfs:Literal).

% Section 2.7: XMLLiteral
triple(rdf:XMLLiteral, rdf:type, rdfs:Datatype).
triple(rdf:XMLLiteral, rdfs:subClassOf, rdfs:Literal).

% Section 2.8: Property
triple(rdf:Property, rdf:type, rdfs:Class).

% Section 3.1: range
triple(rdfs:range, rdfs:type, rdfs:Property).
triple(rdfs:range, rdfs:domain, rdfs:Property).
triple(rdfs:range, rdfs:range, rdfs:Class).

% Section 3.2: domain
triple(rdfs:domain, rdf:type, rdfs:Property).
triple(rdfs:domain, rdfs:domain, rdfs:Property).
triple(rdfs:domain, rdfs:range, rdfs:Class).

% Section 3.3: type
triple(rdf:type, rdf:type, rdfs:Property).
triple(rdfs:type, rdfs:domain, rdfs:Resource).
triple(rdfs:type, rdfs:range, rdfs:Class).

% Section 3.4: subClassOf
triple(rdfs:subClassOf, rdf:type, rdfs:Property).
triple(rdfs:subClassOf, rdfs:domain, rdfs:Class).
triple(rdfs:subClassOf, rdfs:range, rdfs:Class).

% Section 3.5: subPropertyOf
triple(rdfs:subPropertyOf, rdf:type, rdfs:Property).
triple(rdfs:subPropertyOf, rdfs:domain, rdfs:Property).
triple(rdfs:subPropertyOf, rdfs:range, rdfs:Property).

% Section 3.6: label
triple(rdfs:label, rdf:type, rdfs:Property).
triple(rdfs:label, rdfs:domain, rdf:Resource).
triple(rdfs:label, rdfs:range, rdfs:Literal).

% Section 3.7: comment
triple(rdfs:comment, rdf:type, rdfs:Property).
triple(rdfs:comment, rdfs:domain, rdf:Resource).
triple(rdfs:comment, rdfs:range, rdfs:Literal).


% -------------------------------------------------------------------------------

triple(ex:Car, rdfs:subClassOf, ex:Vehicle).

triple(ex:name, rdfs:subPropertyOf, rdfs:label).
triple(ex:name, rdfs:domain, ex:Car).
triple(ex:name, rdfs:range, xsd:string).

triple(ex:my_car, ex:name, "the clown car").

% -------------------------------------------------------------------------------

% Section: 2.1: Resource -- basically uninteresing as all things are resources.
% resource(R) :- triple(R, _, _).
% resource(R) :- triple(_, R, _).
% resource(R) :- triple(_, _, R).

% Section 2.2: Class
class(C) :- triple(_, rdf:type, C).

% Section 2.4: Datatype
subClass(R, rdfs:Literal) :- instanceOf(R, rdfs:Datatype).

% Section 2.8: Property
property(P) :- triple(_, P, _).
property(P) :- triple(P, rdf:type, rdfs:Property).

% Section 3.1: range
range(P, C) :- triple(P, rdfs:range, C).

% Section 3.2: domain
domain(P, C) :- triple(P, rdfs:domain, C).

% Section 3.3: rdf:type
instanceOf(R, C) :- triple(R, rdf:type, C).
instanceOf(R, C) :- triple(R, P, _), triple(P, rdfs:domain, C).
instanceOf(R, C) :- triple(_, P, R), triple(P, rdfs:range, C).

% Section 3.4: subClassOf
subClass(C, P) :- triple(C, rdfs:subClassOf, P).
class(C) :- subClass(C, _).
class(C) :- subClass(_, C).
% ----- NEGATION: subClass(C, rdfs:Class) :- class(C) AND NOT class(rdfs:Class).
instanceOf(C, C2) :- instanceOf(C, C1), subClass(C1, C2).

% Section 3.5: subPropertyOf
subProperty(C, P) :- triple(C, rdfs:subPropertyOf, P).
property(P) :- subProperty(P, _).
property(P) :- subProperty(_, P).
% ----- NEGATION: subProperty(P, rdfs:Property) :- property(P) AND NOT property(rdfs:Property).
instanceOf(P, C2) :- instanceOf(P, C1), subProperty(C1, C2).
"#;
    let mut program = parse_str(PROGRAM_SOURCE).unwrap().into_parsed();

    let evaluator = NaiveEvaluator::default();

    let results = evaluator.inference(&program);
    assert!(results.is_ok());
    let new_relations = results.unwrap();

    program.intensional_mut().merge_from(new_relations).unwrap();

    print!("{}", program);

    print!("{}", PROGRAM_SOURCE);
    println!("-------------------------------------------------------------------------------");

    let program = parse_str(PROGRAM_SOURCE).unwrap().into_parsed();

    let p_triple = program.predicates().fetch("triple").unwrap();
    let p_instance_of = program.predicates().fetch("instanceOf").unwrap();

    assert_eq!(program.extensional().get(&p_triple).unwrap().len(), 39);
    assert_eq!(program.intensional().get(&p_instance_of).unwrap().len(), 0);

    print!("{}", program);
    println!("-------------------------------------------------------------------------------");

    let evaluator = NaiveEvaluator::default();
    let results = evaluator.inference(&program).unwrap();

    assert_eq!(program.extensional().get(&p_triple).unwrap().len(), 39);
    assert_eq!(results.get(&p_instance_of).unwrap().len(), 29);

    print!("{}", program);
}