From 7d6021c6c706c108b5b11d52071acd104c7d4ff8 Mon Sep 17 00:00:00 2001 From: Federico Igne Date: Sat, 30 Jan 2021 10:59:38 +0000 Subject: Delay import of data files (#7) This should partially solve the issue with data import through OWLAPI being too slow. --- .../scala/uk/ac/ox/cs/rsacomb/CanonicalModel.scala | 82 +----- .../uk/ac/ox/cs/rsacomb/FilteringProgram.scala | 15 +- .../scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala | 297 ++++++++++++++------- .../ox/cs/rsacomb/converter/RDFoxConverter.scala | 36 ++- .../scala/uk/ac/ox/cs/rsacomb/util/RDFoxUtil.scala | 16 ++ src/main/scala/uk/ac/ox/cs/rsacomb/util/RSA.scala | 4 + 6 files changed, 256 insertions(+), 194 deletions(-) (limited to 'src/main/scala') diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/CanonicalModel.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/CanonicalModel.scala index 96a953f..3777c6b 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/CanonicalModel.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/CanonicalModel.scala @@ -12,17 +12,12 @@ import org.semanticweb.owlapi.model.{ } import tech.oxfordsemantic.jrdfox.logic.datalog.{ - Rule, BodyFormula, - TupleTableAtom, - Negation -} -import tech.oxfordsemantic.jrdfox.logic.expression.{ - Term, - Variable, - // Resource, - IRI + Negation, + Rule, + TupleTableAtom } +import tech.oxfordsemantic.jrdfox.logic.expression.{IRI, Term, Variable} import implicits.JavaCollections._ @@ -78,73 +73,6 @@ class CanonicalModel(val ontology: RSAOntology) { }) } - /** Top axiomatization - * - * Corresponding to the following rules: - * - * ``` - * [?a, rdf:type, owl:Thing] :- [?a, rdf:type, ?b] . - * [?a, rdf:type, owl:Thing], [?b, rdf:type, owl:Thing] :- [?a, ?r, ?b], FILTER(?r != rdf:type). - * ``` - * - * @note this is a naïve implementation of top axiomatization and - * might change in the future. The ideal solution would be for RDFox - * to take care of this, but at the time of writing this is not - * compatible with the way we are using the tool. - */ - private val topAxioms: List[Rule] = { - val varA = Variable.create("A") - val varR = Variable.create("R") - val varB = Variable.create("B") - List( - Rule.create( - RSA.Thing(varA), - TupleTableAtom.rdf(varA, IRI.RDF_TYPE, varB) - ), - Rule.create( - List(RSA.Thing(varA), RSA.Thing(varB)), - List( - TupleTableAtom.rdf(varA, varR, varB), - FilterAtom.create(FunctionCall.notEqual(varR, IRI.RDF_TYPE)) - ) - ) - ) - } - - /** Equality axiomatization - * - * Introduce reflexivity, simmetry and transitivity rules for a naïve - * equality axiomatization. - * - * @note that we are using a custom `congruent` predicate to indicate - * equality. This is to avoid interfering with the standard - * `owl:sameAs`. - * - * @note RDFox is able to handle equality in a "smart" way, but this - * behaviour is incompatible with other needed features like - * negation-as-failure and aggregates. - * - * @todo to complete the equality axiomatization we need to introduce - * substitution rules to explicate a complete "equality" semantics. - */ - private val equalityAxioms: List[Rule] = { - val varX = Variable.create("X") - val varY = Variable.create("Y") - val varZ = Variable.create("Z") - List( - // Reflexivity - Rule.create(RSA.Congruent(varX, varX), RSA.Thing(varX)), - // Simmetry - Rule.create(RSA.Congruent(varY, varX), RSA.Congruent(varX, varY)), - // Transitivity - Rule.create( - RSA.Congruent(varX, varZ), - RSA.Congruent(varX, varY), - RSA.Congruent(varY, varZ) - ) - ) - } - val (facts, rules): (List[TupleTableAtom], List[Rule]) = { // Compute rules from ontology axioms val (facts, rules) = { @@ -156,7 +84,7 @@ class CanonicalModel(val ontology: RSAOntology) { } ( facts.flatten, - rolesAdditionalRules ::: topAxioms ::: equalityAxioms ::: rules.flatten + rolesAdditionalRules ::: rules.flatten ) } diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/FilteringProgram.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/FilteringProgram.scala index 9427735..06224e7 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/FilteringProgram.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/FilteringProgram.scala @@ -22,8 +22,8 @@ object FilteringProgram { * @param constants constants in the original ontology. They will be * used to initialize predicate `rsa:Named`. */ - def apply(query: ConjunctiveQuery, constants: List[Term]): FilteringProgram = - new FilteringProgram(query, constants) + def apply(query: ConjunctiveQuery): FilteringProgram = + new FilteringProgram(query) } @@ -34,7 +34,7 @@ object FilteringProgram { * * Instances can be created using the companion object. */ -class FilteringProgram(query: ConjunctiveQuery, constants: List[Term]) { +class FilteringProgram(query: ConjunctiveQuery) { /** Extends capabilities of * [[tech.oxfordsemantic.jrdfox.logic.datalog.TupleTableAtom TupleTableAtom]] @@ -76,15 +76,6 @@ class FilteringProgram(query: ConjunctiveQuery, constants: List[Term]) { val nis: Rule = Rule.create(RSA.NI(varX), RSA.Congruent(varX, varY), RSA.Named(varY)) - /** Initializes instances of `rsa:Named`. - * - * They represent the set of constants appearing in the original - * ontology. - * - * @note corresponds to rules 2 in Table 3. - */ - val facts = constants map RSA.Named - /** Collection of filtering program rules. */ val rules: List[Rule] = nis :: { diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala index 8d5bf4c..0f1552a 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala @@ -46,6 +46,7 @@ import tech.oxfordsemantic.jrdfox.logic.expression.{ import tech.oxfordsemantic.jrdfox.logic.sparql.statement.SelectQuery /* Scala imports */ +import scala.util.{Try, Success, Failure} import scala.collection.JavaConverters._ import scala.collection.mutable.Set import scalax.collection.immutable.Graph @@ -70,56 +71,75 @@ object RSAOntology { /** Name of the RDFox data store used for CQ answering */ private val DataStore = "answer_computation" - def apply(ontology: OWLOntology): RSAOntology = new RSAOntology(ontology) - - def apply(ontologies: File*): RSAOntology = - new RSAOntology(loadOntology(ontologies: _*)) + def apply(ontology: File, data: File*): RSAOntology = + new RSAOntology(ontology, data: _*) def genFreshVariable(): Variable = { counter += 1 Variable.create(f"I$counter%03d") } - private def loadOntology(ontologies: File*): OWLOntology = { - val manager = OWLManager.createOWLOntologyManager() - ontologies.foreach { manager.loadOntologyFromOntologyDocument(_) } - val merger = new OWLOntologyMerger(manager) - merger.createMergedOntology(manager, OWLIRI.create("_:merged")) - } } -class RSAOntology(val ontology: OWLOntology) { +class RSAOntology(_ontology: File, val datafiles: File*) { + /** Simplify conversion between OWLAPI and RDFox concepts */ + import implicits.RDFox._ import uk.ac.ox.cs.rsacomb.implicits.RSAAxiom._ import uk.ac.ox.cs.rsacomb.implicits.JavaCollections._ - // Gather TBox/RBox/ABox from original ontology + /** Manager instance to interface with OWLAPI */ + private val manager = OWLManager.createOWLOntologyManager() + + /** TBox + RBox of the input knowledge base. */ + val ontology: OWLOntology = + manager.loadOntologyFromOntologyDocument(_ontology) + + /** OWLAPI internal reasoner some preliminary reasoning task. */ + private val reasoner = + (new StructuralReasonerFactory()).createReasoner(ontology) + + /** Imported knowledge base. */ + //lazy val kbase: OWLOntology = { + // val merger = new OWLOntologyMerger(manager) + // _data.foreach { manager.loadOntologyFromOntologyDocument(_) } + // merger.createMergedOntology(manager, OWLIRI.create("_:merged")) + //} + + /** TBox axioms */ val tbox: List[OWLLogicalAxiom] = ontology .tboxAxioms(Imports.INCLUDED) .collect(Collectors.toList()) .collect { case a: OWLLogicalAxiom => a } + Logger.print(s"Original TBox: ${tbox.length}", Logger.DEBUG) + /** RBox axioms */ val rbox: List[OWLLogicalAxiom] = ontology .rboxAxioms(Imports.INCLUDED) .collect(Collectors.toList()) .collect { case a: OWLLogicalAxiom => a } + Logger.print(s"Original RBox: ${rbox.length}", Logger.DEBUG) + /** ABox axioms + * + * @note this represents only the set of assertions contained in the + * ontology file. Data files specified in `datafiles` are directly + * imported in RDFox due to performance issues when trying to import + * large data files via OWLAPI. + */ val abox: List[OWLLogicalAxiom] = ontology .aboxAxioms(Imports.INCLUDED) .collect(Collectors.toList()) .collect { case a: OWLLogicalAxiom => a } + Logger.print(s"Original RBox: ${abox.length}", Logger.DEBUG) - val axioms: List[OWLLogicalAxiom] = abox ::: tbox ::: rbox - - Logger.print(s"Original TBox: ${tbox.length}", Logger.DEBUG) - Logger.print(s"Original RBox: ${rbox.length}", Logger.DEBUG) - Logger.print(s"Original ABox: ${abox.length}", Logger.DEBUG) + /** Collection of logical axioms in the input ontology */ + lazy val axioms: List[OWLLogicalAxiom] = abox ::: tbox ::: rbox - /* Retrieve individuals in the original ontology - */ + /* Retrieve individuals in the original ontology */ val individuals: List[IRI] = ontology .getIndividualsInSignature() @@ -129,7 +149,7 @@ class RSAOntology(val ontology: OWLOntology) { .toList val literals: List[Literal] = - abox + axioms .collect { case a: OWLDataPropertyAssertionAxiom => a } .map(_.getObject) .map(implicits.RDFox.owlapiToRdfoxLiteral) @@ -137,18 +157,13 @@ class RSAOntology(val ontology: OWLOntology) { val concepts: List[OWLClass] = ontology.getClassesInSignature().asScala.toList + // This is needed in the computation of rules in the canonical model. + // Can we avoid this using RDFox built-in functions? val roles: List[OWLObjectPropertyExpression] = - axioms + (tbox ++ rbox) .flatMap(_.objectPropertyExpressionsInSignature) .distinct - /** OWLAPI reasoner - * - * Used to carry out some preliminary reasoning task. - */ - private val reasoner = - (new StructuralReasonerFactory()).createReasoner(ontology) - /* Steps for RSA check * 1) convert ontology axioms into LP rules * 2) call RDFox on the onto and compute materialization @@ -157,19 +172,16 @@ class RSAOntology(val ontology: OWLOntology) { * ideally this annotates the graph with info about the reasons * why the ontology might not be RSA. This could help a second * step of approximation of an Horn-ALCHOIQ to RSA + * + * TODO: Implement additional checks (taking into account equality) + * + * To check if the graph is tree-like we check for acyclicity in a + * undirected graph. */ lazy val isRSA: Boolean = Logger.timed( { val unsafe = this.unsafeRoles - // val renderer = new DLSyntaxObjectRenderer() - // println() - // println("Unsafe roles:") - // println(unsafe) - // println() - // println("DL rules:") - // tbox.foreach(x => println(renderer.render(x))) - object RSAConverter extends RDFoxConverter { override def convert( @@ -195,66 +207,78 @@ class RSAOntology(val ontology: OWLOntology) { case _ => super.convert(expr, term, unsafe, skolem, suffix) } - } /* Ontology convertion into LP rules */ val term = RSAOntology.genFreshVariable() - val datalog = axioms - .map(a => RSAConverter.convert(a, term, unsafe, new Constant(a), Empty)) - .unzip - val facts = datalog._1.flatten - val rules = datalog._2.flatten - - //println("Datalog rules:") - //rules foreach println - - // Open connection with RDFox - val (server, data) = RDFoxUtil.openConnection("RSACheck") - - /* Add built-in rules - * TODO: substitute with RDFoxUtil.addRules - */ - data.importData( - UpdateType.ADDITION, - RSA.Prefixes, - "rsa:E[?X,?Y] :- rsa:PE[?X,?Y], rsa:U[?X], rsa:U[?Y] ." + val conversion = Try( + axioms.map(a => + RSAConverter.convert(a, term, unsafe, new Constant(a), Empty) + ) ) - /* Add ontology facts and rules */ - RDFoxUtil.addFacts(data, facts) - RDFoxUtil.addRules(data, rules) - - /* Build graph */ - val graph = this.rsaGraph(data); - //println("Graph:") - //println(graph) - - // Close connection to RDFox - RDFoxUtil.closeConnection(server, data) - - /* To check if the graph is tree-like we check for acyclicity in a - * undirected graph. - * - * TODO: Implement additional checks (taking into account equality) - */ - graph.isAcyclic + conversion match { + case Success(result) => { + val datalog = result.unzip + val facts = datalog._1.flatten + var rules = datalog._2.flatten + + /* Open connection with RDFox */ + val (server, data) = RDFoxUtil.openConnection("RSACheck") + + /* Add additional built-in rules */ + val varX = Variable.create("X") + val varY = Variable.create("Y") + rules = Rule.create( + RSA.E(varX, varY), + RSA.PE(varX, varY), + RSA.U(varX), + RSA.U(varY) + ) :: rules + + /* Load facts and rules from ontology */ + RDFoxUtil.addFacts(data, facts) + RDFoxUtil.addRules(data, rules) + /* Load data files */ + RDFoxUtil.addData(data, datafiles: _*) + + /* Build graph */ + val graph = this.rsaGraph(data); + + /* Close connection to RDFox */ + RDFoxUtil.closeConnection(server, data) + + /* Acyclicity test */ + graph.isAcyclic + } + case Failure(e) => { + Logger print s"Unsupported axiom: $e" + false + } + } }, "RSA check", Logger.DEBUG ) + /** Unsafe roles of a given ontology. + * + * Unsafety conditions are the following: + * + * 1) For all roles r1 appearing in an axiom of type T5, r1 is unsafe + * if there exists a role r2 (different from top) appearing in an + * axiom of type T3 and r1 is a subproperty of the inverse of r2. + * + * 2) For all roles p1 appearing in an axiom of type T5, p1 is unsafe + * if there exists a role p2 appearing in an axiom of type T4 and + * p1 is a subproperty of either p2 or the inverse of p2. + */ lazy val unsafeRoles: List[OWLObjectPropertyExpression] = { /* DEBUG: print rules in DL syntax */ //val renderer = new DLSyntaxObjectRenderer() - /* Checking for (1) unsafety condition: - * - * For all roles r1 appearing in an axiom of type T5, r1 is unsafe - * if there exists a role r2 (different from top) appearing in an axiom - * of type T3 and r1 is a subproperty of the inverse of r2. - */ + /* Checking for unsafety condition (1) */ val unsafe1 = for { axiom <- tbox if axiom.isT5 @@ -271,13 +295,7 @@ class RSAOntology(val ontology: OWLOntology) { if roleSuperInv.contains(role2) } yield role1 - /* Checking for (2) unsafety condition: - * - * For all roles p1 appearing in an axiom of type T5, p1 is unsafe if - * there exists a role p2 appearing in an axiom of type T4 and p1 is a - * subproperty of either p2 or the inverse of p2. - * - */ + /* Checking for unsafety condition (2) */ val unsafe2 = for { axiom <- tbox if axiom.isT5 @@ -307,12 +325,76 @@ class RSAOntology(val ontology: OWLOntology) { Graph(edges: _*) } - def filteringProgram(query: ConjunctiveQuery): FilteringProgram = - Logger.timed( - new FilteringProgram(query, individuals ++ literals), - "Generating filtering program", - Logger.DEBUG + /** Top axiomatization rules + * + * For each concept/role *in the ontology file* introduce a rule to + * derive `owl:Thing`. + * + * @note this might not be enough in cases where data files contain + * concept/roles that are not in the ontology file. While this is + * non-standard, it is not forbidden either and may cause problems + * since not all individuals are considered part of `owl:Thing`. + * + * @note this is a naïve implementation of top axiomatization and + * might change in the future. The ideal solution would be for RDFox + * to take care of this, but at the time of writing this is not + * compatible with the way we are using the tool. + */ + private val topAxioms: List[Rule] = { + val varX = Variable.create("X") + val varY = Variable.create("Y") + concepts + .map(c => { + Rule.create( + RSA.Thing(varX), + TupleTableAtom.rdf(varX, IRI.RDF_TYPE, c.getIRI) + ) + }) ++ roles.map(r => { + val name = r match { + case x: OWLObjectProperty => x.getIRI.getIRIString + case x: OWLObjectInverseOf => + x.getInverse.getNamedProperty.getIRI.getIRIString :: Inverse + } + Rule.create( + List(RSA.Thing(varX), RSA.Thing(varY)), + List(TupleTableAtom.rdf(varX, name, varY)) + ) + }) + } + + /** Equality axiomatization rules + * + * Introduce reflexivity, simmetry and transitivity rules for a naïve + * equality axiomatization. + * + * @note that we are using a custom `congruent` predicate to indicate + * equality. This is to avoid interfering with the standard + * `owl:sameAs`. + * + * @note RDFox is able to handle equality in a "smart" way, but this + * behaviour is incompatible with other needed features like + * negation-as-failure and aggregates. + * + * @todo to complete the equality axiomatization we need to introduce + * substitution rules to explicate a complete "equality" semantics. + */ + private val equalityAxioms: List[Rule] = { + val varX = Variable.create("X") + val varY = Variable.create("Y") + val varZ = Variable.create("Z") + List( + // Reflexivity + Rule.create(RSA.Congruent(varX, varX), RSA.Thing(varX)), + // Simmetry + Rule.create(RSA.Congruent(varY, varX), RSA.Congruent(varX, varY)), + // Transitivity + Rule.create( + RSA.Congruent(varX, varZ), + RSA.Congruent(varX, varY), + RSA.Congruent(varY, varZ) + ) ) + } lazy val canonicalModel = Logger.timed( new CanonicalModel(this), @@ -320,6 +402,13 @@ class RSAOntology(val ontology: OWLOntology) { Logger.DEBUG ) + def filteringProgram(query: ConjunctiveQuery): FilteringProgram = + Logger.timed( + new FilteringProgram(query), + "Generating filtering program", + Logger.DEBUG + ) + // TODO: the following functions needs testing def confl( role: OWLObjectPropertyExpression @@ -356,18 +445,30 @@ class RSAOntology(val ontology: OWLOntology) { val canon = this.canonicalModel val filter = this.filteringProgram(query) - //data.beginTransaction(TransactionType.READ_WRITE) + /* Upload data from data file */ + RDFoxUtil.addData(data, datafiles: _*) + + /* Top / equality axiomatization */ + RDFoxUtil.addRules(data, topAxioms ++ equalityAxioms) + + /* Generate `named` predicates */ + RDFoxUtil.addFacts(data, (individuals ++ literals) map RSA.Named) + data.evaluateUpdate( + RSA.Prefixes, + "INSERT { ?X a rsa:Named } WHERE { ?X a owl:Thing }", + new java.util.HashMap[String, String] + ) Logger print s"Canonical model rules: ${canon.rules.length}" - RDFoxUtil.addRules(data, this.canonicalModel.rules) + RDFoxUtil.addRules(data, canon.rules) Logger print s"Canonical model facts: ${canon.facts.length}" - RDFoxUtil.addFacts(data, this.canonicalModel.facts) + RDFoxUtil.addFacts(data, canon.facts) - RDFoxUtil printStatisticsFor data + //canon.facts.foreach(println) + //canon.rules.foreach(println) - Logger print s"Filtering program facts: ${filter.facts.length}" - RDFoxUtil.addFacts(data, filter.facts) + RDFoxUtil printStatisticsFor data Logger print s"Filtering program rules: ${filter.rules.length}" RDFoxUtil.addRules(data, filter.rules) diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/converter/RDFoxConverter.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/converter/RDFoxConverter.scala index abb4815..c9bed35 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/converter/RDFoxConverter.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/converter/RDFoxConverter.scala @@ -121,10 +121,12 @@ trait RDFoxConverter { axiom match { case a: OWLSubClassOfAxiom => { + val subcls = a.getSubClass + val supcls = a.getSuperClass val (sub, _) = - convert(a.getSubClass, term, unsafe, NoSkolem, suffix) + convert(subcls, term, unsafe, NoSkolem, suffix) val (sup, ext) = - convert(a.getSuperClass, term, unsafe, skolem, suffix) + convert(supcls, term, unsafe, skolem, suffix) val rule = Rule.create(sup, ext ::: sub) ResultR(List(rule)) } @@ -241,14 +243,19 @@ trait RDFoxConverter { case a: OWLDataPropertyRangeAxiom => Result() // ignored - /** Catch-all case for all unhandled axiom types. */ - case a => - throw new RuntimeException( - s"Axiom '$a' is not supported (yet?)" - ) + case a: OWLFunctionalDataPropertyAxiom => + Result() + case a: OWLTransitiveObjectPropertyAxiom => + Result() + + /** Catch-all case for all unhandled axiom types. */ + case a => default(axiom) } + protected def default(axiom: OWLLogicalAxiom): Result = + throw new RuntimeException(s"Axiom '$axiom' is not supported (yet?)") + /** Converts a class expression into a collection of atoms. * * @note not all possible class expressions are handled correctly. @@ -431,6 +438,16 @@ trait RDFoxConverter { convert(expr, term, unsafe, skolem, suffix) } + //case (_, sup: OWLObjectExactCardinality) => { + // println(s"Ignored: $a") + // return Result() + //} + + //case (_, sup: OWLDataExactCardinality) => { + // println(s"Ignored: $a") + // return Result() + //} + /** Existential quantification with singleton filler * * @see @@ -451,6 +468,11 @@ trait RDFoxConverter { */ case e: OWLDataHasValue => (List(convert(e.getProperty, term, e.getFiller, suffix)), List()) + + case e: OWLObjectUnionOf => { + (List(), List()) + } + /** Catch-all case for all unhandled class expressions. */ case e => throw new RuntimeException( diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/util/RDFoxUtil.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/util/RDFoxUtil.scala index d072e48..61a8b79 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/util/RDFoxUtil.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/util/RDFoxUtil.scala @@ -123,6 +123,22 @@ object RDFoxUtil { facts.map(_.toString(Prefixes.s_emptyPrefixes)).mkString("", ".\n", ".") ), "Loading facts", + + /** Imports a sequence of files directly into a datastore. + * + * @param data datastore connection. + * @param files sequence of files to upload. + */ + def addData(data: DataStoreConnection, files: File*): Unit = + Logger.timed( + files.foreach { + data.importData( + UpdateType.ADDITION, + RSA.Prefixes, + _ + ) + }, + "Loading data files", Logger.DEBUG ) diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/util/RSA.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/util/RSA.scala index 4b04c40..ee2fdc1 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/util/RSA.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/util/RSA.scala @@ -28,10 +28,14 @@ object RSA { val Prefixes: Prefixes = new Prefixes() Prefixes.declarePrefix("rsa:", "http://www.cs.ox.ac.uk/isg/rsa/") + Prefixes.declarePrefix("owl:", "http://www.w3.org/2002/07/owl#") private def atom(name: IRI, vars: List[Term]): TupleTableAtom = TupleTableAtom.create(TupleTableName.create(name.getIRI), vars: _*) + def E(t1: Term, t2: Term) = + TupleTableAtom.rdf(t1, RSA("E"), t2) + def PE(t1: Term, t2: Term) = TupleTableAtom.rdf(t1, RSA("PE"), t2) -- cgit v1.2.3