From 58b8d3c11a9deebb40e21c70d0b085d01cada745 Mon Sep 17 00:00:00 2001 From: Federico Igne Date: Wed, 15 Jul 2020 17:44:00 +0100 Subject: Add reworked code from previous repo --- src/main/scala/example/RDFoxAxiomConverter.scala | 58 +++++++++ .../scala/example/RDFoxClassExprConverter.scala | 119 ++++++++++++++++++ .../scala/example/RDFoxPropertyExprConverter.scala | 23 ++++ src/main/scala/example/RDFoxRuleShards.scala | 6 + src/main/scala/example/RSAComb.scala | 138 +++++++++++++++++++++ src/main/scala/example/SkolemStrategy.scala | 46 +++++++ 6 files changed, 390 insertions(+) create mode 100644 src/main/scala/example/RDFoxAxiomConverter.scala create mode 100644 src/main/scala/example/RDFoxClassExprConverter.scala create mode 100644 src/main/scala/example/RDFoxPropertyExprConverter.scala create mode 100644 src/main/scala/example/RDFoxRuleShards.scala create mode 100644 src/main/scala/example/RSAComb.scala create mode 100644 src/main/scala/example/SkolemStrategy.scala (limited to 'src/main/scala/example') diff --git a/src/main/scala/example/RDFoxAxiomConverter.scala b/src/main/scala/example/RDFoxAxiomConverter.scala new file mode 100644 index 0000000..0a6272f --- /dev/null +++ b/src/main/scala/example/RDFoxAxiomConverter.scala @@ -0,0 +1,58 @@ +package rsacomb + +import org.semanticweb.owlapi.model.{OWLAxiom, OWLSubClassOfAxiom, OWLEquivalentClassesAxiom} +import org.semanticweb.owlapi.model.OWLAxiomVisitorEx + +import tech.oxfordsemantic.jrdfox.logic.Rule +import tech.oxfordsemantic.jrdfox.logic.{Atom, Term, Literal, Individual} + +import scala.collection.JavaConverters._ + +import rsacomb.SkolemStrategy +import rsacomb.RDFoxRuleShards + +object RDFoxAxiomConverter { + + def apply(term : Term, skolem : SkolemStrategy) : RDFoxAxiomConverter = + new RDFoxAxiomConverter(term, skolem) + + def apply(term : Term) : RDFoxAxiomConverter = + new RDFoxAxiomConverter(term, SkolemStrategy.None) + +} // object RDFoxAxiomConverter + +class RDFoxAxiomConverter(term : Term, skolem : SkolemStrategy) + extends OWLAxiomVisitorEx[List[Rule]] +{ + + override + def visit(axiom : OWLSubClassOfAxiom) : List[Rule] = { + // Skolemization is needed only for the head of an axiom + val subVisitor = new RDFoxClassExprConverter(term,SkolemStrategy.None) + val superVisitor = new RDFoxClassExprConverter(term, skolem) + // Each visitor returns a `RDFoxRuleShards`, a tuple (res,ext): + // - the `res` List is a list of atoms resulting from the conversion + // of the axiom. + // - for some Class Expressions appearing in the head of an Axiom, + // the conversion might produce atoms that need to appear in the + // body (and not in the head) of the rule. This is what the `ext` + // List is for. + val sub = axiom.getSubClass.accept(subVisitor) + val sup = axiom.getSuperClass.accept(superVisitor) + val head = sup.res.asJava + val body = (sub.res ++ sup.ext).asJava + List(Rule.create(head,body)) + } + + override + def visit(axiom : OWLEquivalentClassesAxiom) : List[Rule] = { + for { + axiom1 <- axiom.asPairwiseAxioms.asScala.toList + axiom2 <- axiom1.asOWLSubClassOfAxioms.asScala.toList + rule <- axiom2.accept(this) + } yield rule + } + + def doDefault(axiom : OWLAxiom) : List[Rule] = List() + +} // class RDFoxAxiomConverter diff --git a/src/main/scala/example/RDFoxClassExprConverter.scala b/src/main/scala/example/RDFoxClassExprConverter.scala new file mode 100644 index 0000000..3e60461 --- /dev/null +++ b/src/main/scala/example/RDFoxClassExprConverter.scala @@ -0,0 +1,119 @@ +package rsacomb + +import scala.collection.JavaConverters._ +import java.util.stream.{Stream,Collectors} + +import org.semanticweb.owlapi.model.{OWLClassExpression, OWLClass, OWLObjectSomeValuesFrom, OWLObjectIntersectionOf, OWLObjectOneOf, OWLObjectMaxCardinality} +import org.semanticweb.owlapi.model.OWLClassExpressionVisitorEx +import tech.oxfordsemantic.jrdfox.logic.{AtomicFormula, Bind,BuiltinFunctionCall} +import tech.oxfordsemantic.jrdfox.logic.{Atom, Predicate, Term, Variable, Literal, Individual} + +import rsacomb.SkolemStrategy +import rsacomb.RDFoxRuleShards + +object RDFoxClassExprConverter { + + def apply(term : Term, skolem : SkolemStrategy) : RDFoxClassExprConverter = + new RDFoxClassExprConverter(term, skolem) + + def apply(term : Term) : RDFoxClassExprConverter = + new RDFoxClassExprConverter(term, SkolemStrategy.None) + + def merge(rules : List[RDFoxRuleShards]) : RDFoxRuleShards = { + rules.foldLeft(RDFoxRuleShards(List(),List())) { + (r1,r2) => + RDFoxRuleShards( + r1.res ++ r2.res, + r1.ext ++ r2.ext + ) + } + } + +} // object RDFoxClassExprConverter + +class RDFoxClassExprConverter(term : Term, skolem : SkolemStrategy) + extends OWLClassExpressionVisitorEx[RDFoxRuleShards] +{ + + // OWLClass + override + def visit(expr : OWLClass) : RDFoxRuleShards = { + val name = expr.getIRI.getIRIString + val atom = List(Atom.create(Predicate.create(name), term)) + RDFoxRuleShards(atom,List()) + } + + // OWLObjectIntersectionOf + override + def visit(expr : OWLObjectIntersectionOf) : RDFoxRuleShards = { + val visitor = new RDFoxClassExprConverter(term,skolem) + // TODO: maybe using `flatMap` instead of `merge` + `map` works as well + RDFoxClassExprConverter.merge ( + expr.asConjunctSet.asScala.toList + .map((e : OWLClassExpression) => e.accept(visitor)) + ) + } + + // OWLObjectOneOf + override + def visit(expr : OWLObjectOneOf) : RDFoxRuleShards = { + val visitor = RDFoxClassExprConverter(term,skolem) + // TODO: review nominal handling. Here we are taking "just" one + val ind = expr.individuals.collect(Collectors.toList()).asScala + .filter(_.isOWLNamedIndividual) + .head // restricts to proper "nominals" + .asOWLNamedIndividual.getIRI.getIRIString + val atom = List(Atom.create( + Predicate.create("owl:sameAs"), term, Individual.create(ind) + )) + RDFoxRuleShards(atom,List()) + } + + // OWLObjectSomeValuesFrom + override + def visit(expr : OWLObjectSomeValuesFrom) : RDFoxRuleShards = { + // TODO: variables needs to be handled at visitor level. Hardcoding + // the name of the varibles might lead to errors for complex cases. + val y = Variable.create("y") + val (fun,term1) = skolem match { + case SkolemStrategy.None => (List(),y) + case SkolemStrategy.Constant(c) => (List(), Individual.create(c)) + case SkolemStrategy.Standard(f) => + // At the time of writing the RDFox library does not have a + // particular class for the "SKOLEM" operator and it is instead + // a simple builtin function with a special name. + (List(Bind.create(BuiltinFunctionCall.create("SKOLEM",term),y)),y) + } + val classVisitor = new RDFoxClassExprConverter(term1,skolem) + val classResult = expr.getFiller.accept(classVisitor) + val propertyVisitor = new RDFoxPropertyExprConverter(term, term1, skolem) + val propertyResult = expr.getProperty.accept(propertyVisitor) + RDFoxRuleShards( + classResult.res ++ propertyResult, + fun ++ classResult.ext + ) + } + + // OWLObjectMaxCardinality + override + def visit(expr : OWLObjectMaxCardinality) : RDFoxRuleShards = { + // TODO: again, no hardcoded variables + val vars = List(Variable.create("y"),Variable.create("z")) + val classResult = RDFoxClassExprConverter.merge( + vars.map(new RDFoxClassExprConverter(_,skolem)) + .map(expr.getFiller.accept(_)) + ) + val propertyResult = + vars.map(new RDFoxPropertyExprConverter(term,_,skolem)) + .map(expr.getProperty.accept(_)) + .flatten + RDFoxRuleShards( + List(Atom.create(Predicate.create("owl:sameAs"),vars(0),vars(1))), + classResult.res ++ propertyResult + ) + } + + def doDefault(expr : OWLClassExpression) : RDFoxRuleShards = + RDFoxRuleShards(List(),List()) + +} // class RDFoxClassExprConverter diff --git a/src/main/scala/example/RDFoxPropertyExprConverter.scala b/src/main/scala/example/RDFoxPropertyExprConverter.scala new file mode 100644 index 0000000..2885ed9 --- /dev/null +++ b/src/main/scala/example/RDFoxPropertyExprConverter.scala @@ -0,0 +1,23 @@ +package rsacomb + +import org.semanticweb.owlapi.model.{OWLPropertyExpression, OWLObjectProperty} +import org.semanticweb.owlapi.model.OWLPropertyExpressionVisitorEx + +import tech.oxfordsemantic.jrdfox.logic.{Atom, Predicate, Term, Variable, Literal} + +import rsacomb.SkolemStrategy + +class RDFoxPropertyExprConverter(term1 : Term, term2 : Term, skolem : SkolemStrategy) + extends OWLPropertyExpressionVisitorEx[List[Atom]] +{ + + override + def visit(expr : OWLObjectProperty) : List[Atom] = { + val name = expr.getIRI.getIRIString + List(Atom.create(Predicate.create(name), term1, term2)) + } + + def doDefault(expr : OWLPropertyExpression) : List[Atom] = List() + +} // class RDFoxPropertyExprConverter + diff --git a/src/main/scala/example/RDFoxRuleShards.scala b/src/main/scala/example/RDFoxRuleShards.scala new file mode 100644 index 0000000..bce31d2 --- /dev/null +++ b/src/main/scala/example/RDFoxRuleShards.scala @@ -0,0 +1,6 @@ +package rsacomb + +import tech.oxfordsemantic.jrdfox.logic.{Rule, Atom, Literal} + +case class RDFoxRuleShards(res : List[Atom], ext : List[Literal]) + diff --git a/src/main/scala/example/RSAComb.scala b/src/main/scala/example/RSAComb.scala new file mode 100644 index 0000000..bc94a8d --- /dev/null +++ b/src/main/scala/example/RSAComb.scala @@ -0,0 +1,138 @@ +package rsacomb + +import java.io.File +import java.util.HashMap +import java.util.stream.{Stream,Collectors} + +import org.semanticweb.owlapi.apibinding.OWLManager +import org.semanticweb.owlapi.model.{AxiomType, ClassExpressionType, OWLObjectSomeValuesFrom} +import org.semanticweb.owlapi.model.{OWLAxiom, OWLSubClassOfAxiom, OWLEquivalentClassesAxiom} +import org.semanticweb.owlapi.model.OWLClassExpression +import org.semanticweb.owlapi.model.OWLOntology +import org.semanticweb.owlapi.model.OWLOntologyManager +import org.semanticweb.owlapi.model.parameters.Imports + +import tech.oxfordsemantic.jrdfox.Prefixes +import tech.oxfordsemantic.jrdfox.client.{ConnectionFactory, ServerConnection, DataStoreConnection} +import tech.oxfordsemantic.jrdfox.client.UpdateType +import tech.oxfordsemantic.jrdfox.logic.{Rule, Atom, Literal, Predicate, Term, Variable} +import tech.oxfordsemantic.jrdfox.logic.{Bind, BuiltinFunctionCall} + +import scala.collection.JavaConverters._ + +import rsacomb.SkolemStrategy + +class RSA(ontology : OWLOntology) { + + /* Alternative constructor(s) */ + def this(file : File) = this(RSA.loadOntology(file)) + + def getOntology : OWLOntology = ontology + +} // class RSA + +object RSA { + + def loadOntology( onto : File ) : OWLOntology = { + /* Retrieve ontology manager */ + val manager : OWLOntologyManager = OWLManager.createOWLOntologyManager() + /* Retrieve ontology */ + manager.loadOntologyFromOntologyDocument(onto) + } + + def isRSA( onto : OWLOntology ) : Boolean = { + /* TODO: Steps for RSA check + * 1) convert ontology axioms into LP rules + * 2) call RDFox on the onto and compute materialization + * 3) build graph from E(x,y) facts + * 4) check if the graph is tree-like + */ + + /* Ontology axiom convertion into LP rules */ + for { + axiom <- onto.tboxAxioms(Imports.EXCLUDED).collect(Collectors.toList()).asScala + visitor = new RDFoxAxiomConverter(Variable.create("x"), SkolemStrategy.Constant(axiom.toString)) + rule <- axiom.accept(visitor) + } yield println(rule) + + /* Return true for now... */ + true + } + +} // object RSA + +object RSAComb { + + val help : String = """ + rsacomb - combined approach for CQ answering for RSA ontologies. + + USAGE + rsacomb + + where + the ontology is expected to be an OWL file and the (single) + query a SPARQL query file. + """ + + def main( args : Array[String] ) : Unit = { + + /* Simple arguments handling + * + * TODO: use something better later on + */ + + if (args.length < 2) { + println(help) + return () + } + + val ontologyPath = new File(args(0)) + val queryPath = new File(args(1)) + + if (!ontologyPath.isFile || !queryPath.isFile) { + println("The provided arguments are not regular files.\n\n") + println(help) + return () + } + + /* Create RSA object from generic OWLOntology + * + * TODO: It might be required to check if the ontology in input is + * Horn-ALCHOIQ. At the moment we are assuming this is always the + * case. + */ + val rsa = new RSA(ontologyPath) + RSA.isRSA(rsa.getOntology) + + /* Build canonical model */ + //val tboxCanon = rsa.canonicalModel() + + /* Load query */ + //val query = ... + + /* Compute the filtering program from the given query */ + //val tboxFilter = rsa.filteringProgram(query) + + /* ... */ + + /* DEBUG ONLY */ + println("Ok!") + } +} + +/* Notes: + * + * To establish a connection with a local RDFox instance, do the + * following: + * + * ``` + * val serverConnection : ServerConnection = ConnectionFactory.newServerConnection("rdfox:local", "", "") + * serverConnection.createDataStore("test","seq",new HashMap()) + * val dataStoreConnection : DataStoreConnection = serverConnection.newDataStoreConnection("test") + * dataStoreConnection.importData( + * UpdateType.ADDITION, + * Prefixes.s_emptyPrefixes, + * new File("./path/to/file") + * ) + * ``` + */ diff --git a/src/main/scala/example/SkolemStrategy.scala b/src/main/scala/example/SkolemStrategy.scala new file mode 100644 index 0000000..9df167f --- /dev/null +++ b/src/main/scala/example/SkolemStrategy.scala @@ -0,0 +1,46 @@ +package rsacomb + +sealed trait SkolemStrategy + +object SkolemStrategy { + // TODO: might want to use something else other than `hashCode` as a + // function to generate a fresh function/constant + + /* No skolemization at all. + * + * From + * ∃R.A ⊑ B + * to + * R(x,y), B(y) -> B(x) + */ + case object None extends SkolemStrategy + + /* Functional skolemization + * + * From + * A ⊑ ∃R.B + * to + * A(y) -> R(x,f(x)), B(f(x)) + * for f, fresh function associated with the input axiom + */ + case class Standard(func : String) extends SkolemStrategy + object Standard { + def apply(axiom : String) = new Standard(genFunctionString(axiom)) + def genFunctionString(str : String) = "f_" ++ str.hashCode.toString + } + + /* Functional skolemization + * + * From + * A ⊑ ∃R.B + * to + * A(y) -> R(x,c), B(c) + * for c, fresh constant associated with the input axiom + */ + case class Constant(const : String) extends SkolemStrategy + object Constant { + def apply(axiom : String) = new Constant(genConstantString(axiom)) + def genConstantString(str : String) = "internal:c_" ++ str.hashCode.toString + } +} + -- cgit v1.2.3