diff options
Diffstat (limited to 'src/main/scala')
9 files changed, 101 insertions, 419 deletions
diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala index eaacedc..f54884f 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala | |||
@@ -64,23 +64,35 @@ object RSAComb extends App { | |||
64 | case Some(query) => { | 64 | case Some(query) => { |
65 | val answers = ontology ask query | 65 | val answers = ontology ask query |
66 | Logger.print(s"$answers", Logger.QUIET) | 66 | Logger.print(s"$answers", Logger.QUIET) |
67 | Logger print s"Number of answer: ${answers.length}" | 67 | Logger print s"Number of answer: ${answers.length} (${answers.lengthWithMultiplicity})" |
68 | 68 | ||
69 | val unfiltered = ontology askUnfiltered query | 69 | val unfiltered = ontology askUnfiltered query |
70 | val percentage = unfiltered match { | 70 | unfiltered map { u => |
71 | case Some(u) => { | 71 | Logger.print( |
72 | s"Number of unfiltered answers: ${u.length} (${u.map(_._1).sum}).", | ||
73 | Logger.DEBUG | ||
74 | ) | ||
75 | //u foreach println | ||
76 | val spurious = { | ||
77 | val sp = | ||
78 | RDFoxUtil.buildDescriptionQuery("SP", query.variables.length) | ||
79 | ontology.queryDataStore(query, sp, RSA.Prefixes) | ||
80 | } | ||
81 | spurious map { s => | ||
72 | Logger.print( | 82 | Logger.print( |
73 | s"Number of spurious answers: ${u.length}.", | 83 | s"Number of spurious answers: ${s.length} (${s.map(_._1).sum})", |
84 | Logger.DEBUG | ||
85 | ) | ||
86 | //s foreach println | ||
87 | val perc = | ||
88 | if (u.length > 0) (s.length / u.length.toFloat) * 100 else 0 | ||
89 | Logger.print( | ||
90 | s"Percentage of spurious answers: $perc%", | ||
74 | Logger.DEBUG | 91 | Logger.DEBUG |
75 | ) | 92 | ) |
76 | if (u.length > 0) (1 - answers.length / u.length) * 100 else 0 | ||
77 | } | 93 | } |
78 | case None => 0 | 94 | |
79 | } | 95 | } |
80 | Logger.print( | ||
81 | s"Percentage of spurious answers: $percentage%", | ||
82 | Logger.DEBUG | ||
83 | ) | ||
84 | } | 96 | } |
85 | case None => | 97 | case None => |
86 | throw new RuntimeException("Submitted query is not conjunctive") | 98 | throw new RuntimeException("Submitted query is not conjunctive") |
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 0f1cd5e..4ac5a77 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala | |||
@@ -24,7 +24,11 @@ import org.semanticweb.owlapi.reasoner.structural.StructuralReasonerFactory | |||
24 | import org.semanticweb.owlapi.model.{IRI => OWLIRI} | 24 | import org.semanticweb.owlapi.model.{IRI => OWLIRI} |
25 | import uk.ac.manchester.cs.owl.owlapi.OWLObjectPropertyImpl | 25 | import uk.ac.manchester.cs.owl.owlapi.OWLObjectPropertyImpl |
26 | 26 | ||
27 | import tech.oxfordsemantic.jrdfox.client.{UpdateType, DataStoreConnection} | 27 | import tech.oxfordsemantic.jrdfox.client.{ |
28 | DataStoreConnection, | ||
29 | TransactionType, | ||
30 | UpdateType | ||
31 | } | ||
28 | import tech.oxfordsemantic.jrdfox.Prefixes | 32 | import tech.oxfordsemantic.jrdfox.Prefixes |
29 | import tech.oxfordsemantic.jrdfox.logic.datalog.{ | 33 | import tech.oxfordsemantic.jrdfox.logic.datalog.{ |
30 | Rule, | 34 | Rule, |
@@ -352,22 +356,31 @@ class RSAOntology(val ontology: OWLOntology) { | |||
352 | val canon = this.canonicalModel | 356 | val canon = this.canonicalModel |
353 | val filter = this.filteringProgram(query) | 357 | val filter = this.filteringProgram(query) |
354 | 358 | ||
359 | //data.beginTransaction(TransactionType.READ_WRITE) | ||
360 | |||
355 | Logger print s"Canonical model: ${canon.rules.length} rules" | 361 | Logger print s"Canonical model: ${canon.rules.length} rules" |
356 | RDFoxUtil.addRules(data, this.canonicalModel.rules) | 362 | RDFoxUtil.addRules(data, this.canonicalModel.rules) |
357 | 363 | ||
358 | Logger print s"Canonical model: ${canon.facts.length} facts" | 364 | Logger print s"Canonical model: ${canon.facts.length} facts" |
359 | RDFoxUtil.addFacts(data, this.canonicalModel.facts) | 365 | RDFoxUtil.addFacts(data, this.canonicalModel.facts) |
360 | 366 | ||
361 | RDFoxUtil printStatisticsFor data | 367 | Logger print s"Filtering program: ${filter.facts.length} facts" |
368 | RDFoxUtil.addFacts(data, filter.facts) | ||
362 | 369 | ||
363 | Logger print s"Filtering program: ${filter.rules.length} rules" | 370 | Logger print s"Filtering program: ${filter.rules.length} rules" |
364 | RDFoxUtil.addRules(data, filter.rules) | 371 | RDFoxUtil.addRules(data, filter.rules) |
365 | 372 | ||
366 | Logger print s"Filtering program: ${filter.facts.length} facts" | 373 | //data.commitTransaction() |
367 | RDFoxUtil.addFacts(data, filter.facts) | ||
368 | 374 | ||
369 | RDFoxUtil printStatisticsFor data | 375 | RDFoxUtil printStatisticsFor data |
370 | 376 | ||
377 | //{ | ||
378 | // import java.io.{FileOutputStream, File} | ||
379 | // val rules = new FileOutputStream(new File("rules2.dlog")) | ||
380 | // val facts = new FileOutputStream(new File("facts2.ttl")) | ||
381 | // RDFoxUtil.export(data, rules, facts) | ||
382 | //} | ||
383 | |||
371 | val answers = { | 384 | val answers = { |
372 | val ans = RDFoxUtil.buildDescriptionQuery("Ans", query.answer.size) | 385 | val ans = RDFoxUtil.buildDescriptionQuery("Ans", query.answer.size) |
373 | RDFoxUtil | 386 | RDFoxUtil |
diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/converter/RDFoxAxiomConverter.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/converter/RDFoxAxiomConverter.scala deleted file mode 100644 index 1fbf28a..0000000 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/converter/RDFoxAxiomConverter.scala +++ /dev/null | |||
@@ -1,134 +0,0 @@ | |||
1 | package uk.ac.ox.cs.rsacomb.converter | ||
2 | |||
3 | import org.semanticweb.owlapi.model.{ | ||
4 | OWLAxiom, | ||
5 | OWLSubClassOfAxiom, | ||
6 | OWLEquivalentClassesAxiom, | ||
7 | OWLObjectPropertyExpression, | ||
8 | OWLObjectPropertyDomainAxiom, | ||
9 | OWLObjectPropertyRangeAxiom, | ||
10 | OWLDataPropertyDomainAxiom, | ||
11 | OWLDataPropertyRangeAxiom, | ||
12 | OWLInverseObjectPropertiesAxiom | ||
13 | } | ||
14 | import org.semanticweb.owlapi.model.OWLAxiomVisitorEx | ||
15 | |||
16 | import tech.oxfordsemantic.jrdfox.logic.datalog.{ | ||
17 | Rule, | ||
18 | BodyFormula, | ||
19 | TupleTableAtom, | ||
20 | TupleTableName | ||
21 | } | ||
22 | import tech.oxfordsemantic.jrdfox.logic.expression.{ | ||
23 | Term, | ||
24 | IRI => RDFoxIRI, | ||
25 | Variable, | ||
26 | Literal | ||
27 | } | ||
28 | |||
29 | import scala.collection.JavaConverters._ | ||
30 | |||
31 | import org.semanticweb.owlapi.model.OWLSubObjectPropertyOfAxiom | ||
32 | import org.semanticweb.owlapi.model.OWLObjectProperty | ||
33 | import org.semanticweb.owlapi.model.OWLClassAssertionAxiom | ||
34 | |||
35 | import uk.ac.ox.cs.rsacomb.RSAOntology | ||
36 | import uk.ac.ox.cs.rsacomb.suffix.{RSASuffix, Empty} | ||
37 | import uk.ac.manchester.cs.owl.owlapi.OWLSubClassOfAxiomImpl | ||
38 | import uk.ac.manchester.cs.owl.owlapi.OWLObjectSomeValuesFromImpl | ||
39 | import uk.ac.manchester.cs.owl.owlapi.OWLClassImpl | ||
40 | import org.semanticweb.owlapi.model.IRI | ||
41 | |||
42 | object RDFoxAxiomConverter { | ||
43 | |||
44 | def apply( | ||
45 | term: Term, | ||
46 | unsafe: List[OWLObjectPropertyExpression], | ||
47 | skolem: SkolemStrategy = NoSkolem, | ||
48 | suffix: RSASuffix = Empty | ||
49 | ): RDFoxAxiomConverter = | ||
50 | new RDFoxAxiomConverter(term, unsafe, skolem, suffix) | ||
51 | |||
52 | } // object RDFoxAxiomConverter | ||
53 | |||
54 | class RDFoxAxiomConverter( | ||
55 | term: Term, | ||
56 | unsafe: List[OWLObjectPropertyExpression], | ||
57 | skolem: SkolemStrategy, | ||
58 | suffix: RSASuffix | ||
59 | ) extends OWLAxiomVisitorEx[List[Rule]] { | ||
60 | |||
61 | import uk.ac.ox.cs.rsacomb.implicits.JavaCollections._ | ||
62 | |||
63 | override def visit(axiom: OWLSubClassOfAxiom): List[Rule] = { | ||
64 | // Skolemization is needed only for the head of an axiom | ||
65 | val subVisitor = | ||
66 | new RDFoxClassExprConverter(term, unsafe, NoSkolem, suffix) | ||
67 | val superVisitor = new RDFoxClassExprConverter(term, unsafe, skolem, suffix) | ||
68 | // Each visitor returns a `RDFoxRuleShards`, a tuple (res,ext): | ||
69 | // - the `res` List is a list of atoms resulting from the conversion | ||
70 | // of the axiom. | ||
71 | // - for some Class Expressions appearing in the head of an Axiom, | ||
72 | // the conversion might produce atoms that need to appear in the | ||
73 | // body (and not in the head) of the rule. This is what the `ext` | ||
74 | // List is for. | ||
75 | val sub = axiom.getSubClass.accept(subVisitor) | ||
76 | val sup = axiom.getSuperClass.accept(superVisitor) | ||
77 | val head = sup.res.asJava | ||
78 | val body = (sub.res ++ sup.ext).asJava | ||
79 | List(Rule.create(head, body)) | ||
80 | } | ||
81 | |||
82 | override def visit(axiom: OWLEquivalentClassesAxiom): List[Rule] = { | ||
83 | for { | ||
84 | axiom1 <- axiom.asPairwiseAxioms.asScala.toList | ||
85 | axiom2 <- axiom1.asOWLSubClassOfAxioms.asScala.toList | ||
86 | rule <- axiom2.accept(this) | ||
87 | } yield rule | ||
88 | } | ||
89 | |||
90 | override def visit(axiom: OWLSubObjectPropertyOfAxiom): List[Rule] = { | ||
91 | val term1 = RSAOntology.genFreshVariable() | ||
92 | val subVisitor = | ||
93 | new RDFoxPropertyExprConverter(term, term1, suffix) | ||
94 | val superVisitor = new RDFoxPropertyExprConverter(term, term1, suffix) | ||
95 | val body: List[BodyFormula] = axiom.getSubProperty.accept(subVisitor) | ||
96 | val head: List[TupleTableAtom] = axiom.getSuperProperty.accept(superVisitor) | ||
97 | List(Rule.create(head.asJava, body.asJava)) | ||
98 | } | ||
99 | |||
100 | override def visit(axiom: OWLObjectPropertyDomainAxiom): List[Rule] = | ||
101 | axiom.asOWLSubClassOfAxiom.accept(this) | ||
102 | |||
103 | override def visit(axiom: OWLObjectPropertyRangeAxiom): List[Rule] = { | ||
104 | val term1 = RSAOntology.genFreshVariable() | ||
105 | val rangeVisitor = new RDFoxClassExprConverter(term, unsafe, skolem, suffix) | ||
106 | val range = axiom.getRange.accept(rangeVisitor) | ||
107 | val propertyVisitor = new RDFoxPropertyExprConverter(term1, term, suffix) | ||
108 | val prop = axiom.getProperty.accept(propertyVisitor) | ||
109 | List(Rule.create(range.res, range.ext ::: prop)) | ||
110 | } | ||
111 | |||
112 | override def visit(axiom: OWLDataPropertyDomainAxiom): List[Rule] = | ||
113 | axiom.asOWLSubClassOfAxiom.accept(this) | ||
114 | |||
115 | override def visit(axiom: OWLInverseObjectPropertiesAxiom): List[Rule] = | ||
116 | axiom.asSubObjectPropertyOfAxioms.asScala.toList.flatMap(_.accept(this)) | ||
117 | |||
118 | override def visit(axiom: OWLClassAssertionAxiom): List[Rule] = { | ||
119 | val ind = axiom.getIndividual | ||
120 | if (ind.isNamed) { | ||
121 | val term = RDFoxIRI.create(ind.asOWLNamedIndividual().getIRI.getIRIString) | ||
122 | val cls = axiom.getClassExpression | ||
123 | val visitor = | ||
124 | new RDFoxClassExprConverter(term, unsafe, NoSkolem, suffix) | ||
125 | val shard = cls.accept(visitor) | ||
126 | List(Rule.create(shard.res.asJava, shard.ext.asJava)) | ||
127 | } else { | ||
128 | List() | ||
129 | } | ||
130 | } | ||
131 | |||
132 | def doDefault(axiom: OWLAxiom): List[Rule] = List() | ||
133 | |||
134 | } // class RDFoxAxiomConverter | ||
diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/converter/RDFoxClassExprConverter.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/converter/RDFoxClassExprConverter.scala deleted file mode 100644 index 9551c61..0000000 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/converter/RDFoxClassExprConverter.scala +++ /dev/null | |||
@@ -1,191 +0,0 @@ | |||
1 | package uk.ac.ox.cs.rsacomb.converter | ||
2 | |||
3 | import scala.collection.JavaConverters._ | ||
4 | import java.util.stream.{Stream, Collectors} | ||
5 | |||
6 | import org.semanticweb.owlapi.model.{ | ||
7 | OWLClassExpression, | ||
8 | OWLClass, | ||
9 | OWLObjectSomeValuesFrom, | ||
10 | OWLDataSomeValuesFrom, | ||
11 | OWLObjectIntersectionOf, | ||
12 | OWLObjectOneOf, | ||
13 | OWLObjectMaxCardinality | ||
14 | } | ||
15 | import org.semanticweb.owlapi.model.OWLClassExpressionVisitorEx | ||
16 | import tech.oxfordsemantic.jrdfox.logic.Datatype | ||
17 | import tech.oxfordsemantic.jrdfox.logic.datalog.{ | ||
18 | BindAtom, | ||
19 | TupleTableName, | ||
20 | TupleTableAtom | ||
21 | } | ||
22 | import tech.oxfordsemantic.jrdfox.logic.expression.{ | ||
23 | Term, | ||
24 | Literal, | ||
25 | Variable, | ||
26 | FunctionCall, | ||
27 | IRI | ||
28 | } | ||
29 | |||
30 | import org.semanticweb.owlapi.model.OWLObjectPropertyExpression | ||
31 | import org.semanticweb.owlapi.model.OWLObjectProperty | ||
32 | |||
33 | import uk.ac.ox.cs.rsacomb.RSAOntology | ||
34 | import uk.ac.ox.cs.rsacomb.suffix.{RSASuffix, Empty} | ||
35 | import uk.ac.ox.cs.rsacomb.util.RSA | ||
36 | |||
37 | object RDFoxClassExprConverter { | ||
38 | |||
39 | def apply( | ||
40 | term: Term, | ||
41 | unsafe: List[OWLObjectPropertyExpression] = List(), | ||
42 | skolem: SkolemStrategy = NoSkolem, | ||
43 | suffix: RSASuffix = Empty | ||
44 | ): RDFoxClassExprConverter = | ||
45 | new RDFoxClassExprConverter(term, unsafe, skolem, suffix) | ||
46 | |||
47 | def merge(rules: List[RDFoxRuleShards]): RDFoxRuleShards = { | ||
48 | rules.foldLeft(RDFoxRuleShards(List(), List())) { (r1, r2) => | ||
49 | RDFoxRuleShards( | ||
50 | r1.res ++ r2.res, | ||
51 | r1.ext ++ r2.ext | ||
52 | ) | ||
53 | } | ||
54 | } | ||
55 | |||
56 | } // object RDFoxClassExprConverter | ||
57 | |||
58 | class RDFoxClassExprConverter( | ||
59 | term: Term, | ||
60 | unsafe: List[OWLObjectPropertyExpression], | ||
61 | skolem: SkolemStrategy = NoSkolem, | ||
62 | suffix: RSASuffix = Empty | ||
63 | ) extends OWLClassExpressionVisitorEx[RDFoxRuleShards] { | ||
64 | |||
65 | import uk.ac.ox.cs.rsacomb.implicits.RDFox._ | ||
66 | |||
67 | // OWLClass | ||
68 | override def visit(expr: OWLClass): RDFoxRuleShards = { | ||
69 | val iri: IRI = if (expr.isTopEntity()) IRI.THING else expr.getIRI() | ||
70 | val atom = List(TupleTableAtom.rdf(term, IRI.RDF_TYPE, iri)) | ||
71 | RDFoxRuleShards(atom, List()) | ||
72 | } | ||
73 | |||
74 | // OWLObjectIntersectionOf | ||
75 | override def visit(expr: OWLObjectIntersectionOf): RDFoxRuleShards = { | ||
76 | val visitor = new RDFoxClassExprConverter(term, unsafe, skolem, suffix) | ||
77 | // TODO: maybe using `flatMap` instead of `merge` + `map` works as well | ||
78 | RDFoxClassExprConverter.merge( | ||
79 | expr.asConjunctSet.asScala.toList | ||
80 | .map((e: OWLClassExpression) => e.accept(visitor)) | ||
81 | ) | ||
82 | } | ||
83 | |||
84 | // OWLObjectOneOf | ||
85 | override def visit(expr: OWLObjectOneOf): RDFoxRuleShards = { | ||
86 | // TODO: review nominal handling. Here we are taking "just" one | ||
87 | val ind = expr.individuals | ||
88 | .collect(Collectors.toList()) | ||
89 | .asScala | ||
90 | .filter(_.isOWLNamedIndividual) | ||
91 | .head // restricts to proper "nominals" | ||
92 | .asOWLNamedIndividual | ||
93 | .getIRI | ||
94 | val atom = List( | ||
95 | TupleTableAtom.rdf(term, IRI.SAME_AS, ind) | ||
96 | ) | ||
97 | RDFoxRuleShards(atom, List()) | ||
98 | } | ||
99 | |||
100 | // OWLObjectSomeValuesFrom | ||
101 | override def visit(expr: OWLObjectSomeValuesFrom): RDFoxRuleShards = { | ||
102 | val y = RSAOntology.genFreshVariable() | ||
103 | // Here we are assuming a role name | ||
104 | val prop = expr.getProperty() | ||
105 | // Computes the result of rule skolemization. Depending on the used | ||
106 | // technique it might involve the introduction of additional atoms, | ||
107 | // and/or fresh constants and variables. | ||
108 | val (head, body, term1) = skolem match { | ||
109 | case NoSkolem => (List(), List(), y) | ||
110 | case c: Constant => (List(), List(), c.iri) | ||
111 | case s: Standard => { | ||
112 | ( | ||
113 | List(), | ||
114 | List( | ||
115 | BindAtom.create(FunctionCall.create("SKOLEM", s.literal, term), y) | ||
116 | ), | ||
117 | y | ||
118 | ) | ||
119 | } | ||
120 | } | ||
121 | val classVisitor = | ||
122 | new RDFoxClassExprConverter(term1, unsafe, skolem, suffix) | ||
123 | val classResult = expr.getFiller.accept(classVisitor) | ||
124 | val propertyVisitor = new RDFoxPropertyExprConverter(term, term1, suffix) | ||
125 | val propertyResult = expr.getProperty.accept(propertyVisitor) | ||
126 | RDFoxRuleShards( | ||
127 | classResult.res ++ propertyResult ++ head, | ||
128 | classResult.ext ++ body | ||
129 | ) | ||
130 | } | ||
131 | |||
132 | /** Converts a [[org.semanticweb.owlapi.model.OWLDataSomeValuesFrom OWLDataSomeValuesFrom]] | ||
133 | * | ||
134 | * @note we assume the expression is "simple", meaning that the | ||
135 | * property involved is a role name or the inverse of a role name. | ||
136 | * This assumption will be lifted when we will deal with the | ||
137 | * normalization of the input ontology. | ||
138 | * | ||
139 | * @todo the "filler" of this OWL expression is currently ignored. We | ||
140 | * need to find a way (if any) to handle | ||
141 | * [[org.semanticweb.owlapi.model.OWLDataRange OWLDataRange]] | ||
142 | * in RDFox. | ||
143 | */ | ||
144 | override def visit(expr: OWLDataSomeValuesFrom): RDFoxRuleShards = { | ||
145 | val y = RSAOntology.genFreshVariable() | ||
146 | val prop = expr.getProperty() | ||
147 | // Computes the result of rule skolemization. Depending on the used | ||
148 | // technique it might involve the introduction of additional atoms, | ||
149 | // and/or fresh constants and variables. | ||
150 | val (head, body, term1) = skolem match { | ||
151 | case NoSkolem => (List(), List(), y) | ||
152 | case c: Constant => (List(), List(), c.iri) | ||
153 | case s: Standard => { | ||
154 | ( | ||
155 | List(), | ||
156 | List( | ||
157 | BindAtom.create(FunctionCall.create("SKOLEM", s.literal, term), y) | ||
158 | ), | ||
159 | y | ||
160 | ) | ||
161 | } | ||
162 | } | ||
163 | val propertyVisitor = new RDFoxPropertyExprConverter(term, term1, suffix) | ||
164 | val propertyResult = expr.getProperty.accept(propertyVisitor) | ||
165 | RDFoxRuleShards(head ::: propertyResult, body) | ||
166 | } | ||
167 | |||
168 | // OWLObjectMaxCardinality | ||
169 | override def visit(expr: OWLObjectMaxCardinality): RDFoxRuleShards = { | ||
170 | val vars = | ||
171 | List(RSAOntology.genFreshVariable(), RSAOntology.genFreshVariable()) | ||
172 | val classResult = RDFoxClassExprConverter.merge( | ||
173 | vars | ||
174 | .map(new RDFoxClassExprConverter(_, unsafe, skolem, suffix)) | ||
175 | .map(expr.getFiller.accept(_)) | ||
176 | ) | ||
177 | val propertyResult = | ||
178 | vars | ||
179 | .map(new RDFoxPropertyExprConverter(term, _, suffix)) | ||
180 | .map(expr.getProperty.accept(_)) | ||
181 | .flatten | ||
182 | RDFoxRuleShards( | ||
183 | List(TupleTableAtom.rdf(vars(0), IRI.SAME_AS, vars(1))), | ||
184 | classResult.res ++ propertyResult | ||
185 | ) | ||
186 | } | ||
187 | |||
188 | def doDefault(expr: OWLClassExpression): RDFoxRuleShards = | ||
189 | RDFoxRuleShards(List(), List()) | ||
190 | |||
191 | } // class RDFoxClassExprConverter | ||
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 b4f5adb..845ec1f 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 | |||
@@ -40,7 +40,7 @@ import tech.oxfordsemantic.jrdfox.logic.datalog.{ | |||
40 | import tech.oxfordsemantic.jrdfox.logic.expression.{Term, IRI, FunctionCall} | 40 | import tech.oxfordsemantic.jrdfox.logic.expression.{Term, IRI, FunctionCall} |
41 | import uk.ac.ox.cs.rsacomb.RSAOntology | 41 | import uk.ac.ox.cs.rsacomb.RSAOntology |
42 | import uk.ac.ox.cs.rsacomb.suffix.{Empty, Inverse, RSASuffix} | 42 | import uk.ac.ox.cs.rsacomb.suffix.{Empty, Inverse, RSASuffix} |
43 | import uk.ac.ox.cs.rsacomb.util.RSA | 43 | import uk.ac.ox.cs.rsacomb.util.{RSA, RDFoxUtil} |
44 | 44 | ||
45 | /** Horn-ALCHOIQ to RDFox axiom converter. | 45 | /** Horn-ALCHOIQ to RDFox axiom converter. |
46 | * | 46 | * |
@@ -328,10 +328,7 @@ trait RDFoxConverter { | |||
328 | val (bind, term1) = skolem match { | 328 | val (bind, term1) = skolem match { |
329 | case NoSkolem => (None, varX) | 329 | case NoSkolem => (None, varX) |
330 | case c: Constant => (None, c.iri) | 330 | case c: Constant => (None, c.iri) |
331 | case s: Standard => { | 331 | case s: Standard => (Some(RDFoxUtil.skolem(s.name, term, varX)), varX) |
332 | val func = FunctionCall.create("SKOLEM", s.literal, term) | ||
333 | (Some(BindAtom.create(func, varX)), varX) | ||
334 | } | ||
335 | } | 332 | } |
336 | val (res, ext) = convert(cls, term1, unsafe, skolem, suffix) | 333 | val (res, ext) = convert(cls, term1, unsafe, skolem, suffix) |
337 | val prop = convert(role, term, term1, suffix) | 334 | val prop = convert(role, term, term1, suffix) |
@@ -359,10 +356,7 @@ trait RDFoxConverter { | |||
359 | val (bind, term1) = skolem match { | 356 | val (bind, term1) = skolem match { |
360 | case NoSkolem => (None, varX) | 357 | case NoSkolem => (None, varX) |
361 | case c: Constant => (None, c.iri) | 358 | case c: Constant => (None, c.iri) |
362 | case s: Standard => { | 359 | case s: Standard => (Some(RDFoxUtil.skolem(s.name, term, varX)), varX) |
363 | val func = FunctionCall.create("SKOLEM", s.literal, term) | ||
364 | (Some(BindAtom.create(func, varX)), varX) | ||
365 | } | ||
366 | } | 360 | } |
367 | val prop = convert(role, term, term1, suffix) | 361 | val prop = convert(role, term, term1, suffix) |
368 | (List(prop), bind.toList) | 362 | (List(prop), bind.toList) |
diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/converter/RDFoxPropertyExprConverter.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/converter/RDFoxPropertyExprConverter.scala deleted file mode 100644 index 46a70fa..0000000 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/converter/RDFoxPropertyExprConverter.scala +++ /dev/null | |||
@@ -1,45 +0,0 @@ | |||
1 | package uk.ac.ox.cs.rsacomb.converter | ||
2 | |||
3 | import org.semanticweb.owlapi.model.{ | ||
4 | OWLAnnotationProperty, | ||
5 | OWLDataProperty, | ||
6 | OWLObjectInverseOf, | ||
7 | OWLObjectProperty, | ||
8 | OWLPropertyExpression | ||
9 | } | ||
10 | import org.semanticweb.owlapi.model.OWLPropertyExpressionVisitorEx | ||
11 | |||
12 | import tech.oxfordsemantic.jrdfox.logic.datalog.TupleTableAtom | ||
13 | import tech.oxfordsemantic.jrdfox.logic.expression.{Term, IRI, Literal} | ||
14 | |||
15 | import uk.ac.ox.cs.rsacomb.suffix.{RSASuffix, Inverse} | ||
16 | |||
17 | class RDFoxPropertyExprConverter( | ||
18 | term1: Term, | ||
19 | term2: Term, | ||
20 | suffix: RSASuffix | ||
21 | ) extends OWLPropertyExpressionVisitorEx[List[TupleTableAtom]] { | ||
22 | |||
23 | // Automatically converts OWLAPI types into RDFox equivalent types. | ||
24 | import uk.ac.ox.cs.rsacomb.implicits.RDFox._ | ||
25 | |||
26 | override def visit(expr: OWLObjectProperty): List[TupleTableAtom] = { | ||
27 | val base = expr.getIRI.getIRIString | ||
28 | val pred = IRI.create(base :: suffix) | ||
29 | List(TupleTableAtom.rdf(term1, pred, term2)) | ||
30 | } | ||
31 | |||
32 | override def visit(expr: OWLDataProperty): List[TupleTableAtom] = { | ||
33 | val base = expr.getIRI.getIRIString | ||
34 | val pred = IRI.create(base :: suffix) | ||
35 | List(TupleTableAtom.rdf(term1, pred, term2)) | ||
36 | } | ||
37 | |||
38 | override def visit(expr: OWLObjectInverseOf): List[TupleTableAtom] = { | ||
39 | val visitor = new RDFoxPropertyExprConverter(term1, term2, suffix + Inverse) | ||
40 | expr.getInverse.accept(visitor) | ||
41 | } | ||
42 | |||
43 | def doDefault(expr: OWLPropertyExpression): List[TupleTableAtom] = List() | ||
44 | |||
45 | } // class RDFoxPropertyExprConverter | ||
diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/converter/SkolemStrategy.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/converter/SkolemStrategy.scala index 2142ff3..426a327 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/converter/SkolemStrategy.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/converter/SkolemStrategy.scala | |||
@@ -53,8 +53,7 @@ case object NoSkolem extends SkolemStrategy { | |||
53 | case class Standard(axiom: OWLAxiom)(implicit toString: (OWLAxiom) => String) | 53 | case class Standard(axiom: OWLAxiom)(implicit toString: (OWLAxiom) => String) |
54 | extends SkolemStrategy { | 54 | extends SkolemStrategy { |
55 | def dup(_axiom: OWLAxiom): Standard = copy(axiom = _axiom)(toString) | 55 | def dup(_axiom: OWLAxiom): Standard = copy(axiom = _axiom)(toString) |
56 | lazy val literal = | 56 | lazy val name = s"f_${toString(axiom)}" |
57 | Literal.create(s"f_${toString(axiom)}", Datatype.XSD_STRING) | ||
58 | } | 57 | } |
59 | 58 | ||
60 | /** Constant skolemization | 59 | /** Constant skolemization |
diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/implicits/RSAAtom.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/implicits/RSAAtom.scala index 615722b..9b04f0e 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/implicits/RSAAtom.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/implicits/RSAAtom.scala | |||
@@ -1,17 +1,17 @@ | |||
1 | package uk.ac.ox.cs.rsacomb.implicits | 1 | package uk.ac.ox.cs.rsacomb.implicits |
2 | 2 | ||
3 | import tech.oxfordsemantic.jrdfox.logic.Datatype | 3 | import tech.oxfordsemantic.jrdfox.logic.Datatype |
4 | import tech.oxfordsemantic.jrdfox.logic.expression.{Literal, FunctionCall} | 4 | import tech.oxfordsemantic.jrdfox.logic.expression.{Literal, FunctionCall, Term} |
5 | import tech.oxfordsemantic.jrdfox.logic.datalog.{ | 5 | import tech.oxfordsemantic.jrdfox.logic.datalog.{ |
6 | BindAtom, | 6 | BindAtom, |
7 | TupleTableAtom, | 7 | TupleTableAtom, |
8 | TupleTableName | 8 | TupleTableName |
9 | } | 9 | } |
10 | import tech.oxfordsemantic.jrdfox.logic.expression.{IRI} | 10 | import tech.oxfordsemantic.jrdfox.logic.expression.{IRI} |
11 | import scala.collection.JavaConverters._ | ||
12 | 11 | ||
13 | import uk.ac.ox.cs.rsacomb.suffix.{RSASuffix, Nth} | ||
14 | import uk.ac.ox.cs.rsacomb.RSAOntology | 12 | import uk.ac.ox.cs.rsacomb.RSAOntology |
13 | import uk.ac.ox.cs.rsacomb.suffix.{RSASuffix, Nth} | ||
14 | import uk.ac.ox.cs.rsacomb.util.RDFoxUtil | ||
15 | 15 | ||
16 | /* Is this the best way to determine if an atom is an RDF triple? | 16 | /* Is this the best way to determine if an atom is an RDF triple? |
17 | * Note that we can't use `getNumberOfArguments()` because is not | 17 | * Note that we can't use `getNumberOfArguments()` because is not |
@@ -24,7 +24,7 @@ import uk.ac.ox.cs.rsacomb.RSAOntology | |||
24 | * This is probably because `Atom.rdf(...) is implemented as: | 24 | * This is probably because `Atom.rdf(...) is implemented as: |
25 | * ```scala | 25 | * ```scala |
26 | * def rdf(term1: Term, term2: Term, term3: Term): Atom = | 26 | * def rdf(term1: Term, term2: Term, term3: Term): Atom = |
27 | * Atom.create(TupleTableName.create("internal:triple"), term1, term2, term3) | 27 | * Atom.create(TupleTableName.create("rdfox:DefaultTriples"), term1, term2, term3) |
28 | * ``` | 28 | * ``` |
29 | */ | 29 | */ |
30 | 30 | ||
@@ -33,10 +33,14 @@ object RSAAtom { | |||
33 | implicit class RSAAtom(val atom: TupleTableAtom) { | 33 | implicit class RSAAtom(val atom: TupleTableAtom) { |
34 | 34 | ||
35 | import RDFox._ | 35 | import RDFox._ |
36 | import JavaCollections._ | ||
36 | 37 | ||
37 | val name: String = atom.getTupleTableName.getName | 38 | val name: String = atom.getTupleTableName.getName |
38 | 39 | ||
39 | val isRDF: Boolean = name == "internal:triple" | 40 | val args: List[Term] = atom.getArguments |
41 | |||
42 | val isRDF: Boolean = | ||
43 | name == "http://oxfordsemantic.tech/RDFox#DefaultTriples" | ||
40 | 44 | ||
41 | val isClassAssertion: Boolean = { | 45 | val isClassAssertion: Boolean = { |
42 | isRDF && { | 46 | isRDF && { |
@@ -70,19 +74,16 @@ object RSAAtom { | |||
70 | TupleTableAtom.create(ttname, atom.getArguments()) | 74 | TupleTableAtom.create(ttname, atom.getArguments()) |
71 | } | 75 | } |
72 | 76 | ||
73 | lazy val reified: (Option[BindAtom], List[TupleTableAtom]) = | 77 | lazy val reified: (Option[TupleTableAtom], List[TupleTableAtom]) = |
74 | if (isRDF) { | 78 | if (isRDF) { |
75 | (None, List(atom)) | 79 | (None, List(atom)) |
76 | } else { | 80 | } else { |
77 | val bvar = RSAOntology.genFreshVariable() | 81 | val varS = RSAOntology.genFreshVariable() |
78 | val str = Literal.create(name, Datatype.XSD_STRING) | 82 | val skolem = RDFoxUtil.skolem(name, (args :+ varS): _*) |
79 | val args = atom.getArguments.asScala.toList | 83 | val atom = TupleTableAtom.rdf(varS, IRI.RDF_TYPE, name) |
80 | val skolem = FunctionCall.create("SKOLEM", str :: args: _*) | ||
81 | val bind = BindAtom.create(skolem, bvar) | ||
82 | val atoms = args.zipWithIndex | 84 | val atoms = args.zipWithIndex |
83 | .map { case (t, i) => TupleTableAtom.rdf(bvar, name :: Nth(i), t) } | 85 | .map { case (a, i) => TupleTableAtom.rdf(varS, name :: Nth(i), a) } |
84 | (Some(bind), atoms) | 86 | (Some(skolem), atom :: atoms) |
85 | } | 87 | } |
86 | } | 88 | } |
87 | |||
88 | } | 89 | } |
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 76f720c..d072e48 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 | |||
@@ -1,7 +1,6 @@ | |||
1 | package uk.ac.ox.cs.rsacomb.util | 1 | package uk.ac.ox.cs.rsacomb.util |
2 | 2 | ||
3 | import java.io.File | 3 | import java.io.{OutputStream, File, StringReader} |
4 | import java.io.StringReader | ||
5 | import tech.oxfordsemantic.jrdfox.Prefixes | 4 | import tech.oxfordsemantic.jrdfox.Prefixes |
6 | import tech.oxfordsemantic.jrdfox.client.{ | 5 | import tech.oxfordsemantic.jrdfox.client.{ |
7 | ComponentInfo, | 6 | ComponentInfo, |
@@ -11,6 +10,7 @@ import tech.oxfordsemantic.jrdfox.client.{ | |||
11 | UpdateType | 10 | UpdateType |
12 | } | 11 | } |
13 | import tech.oxfordsemantic.jrdfox.formats.SPARQLParser | 12 | import tech.oxfordsemantic.jrdfox.formats.SPARQLParser |
13 | import tech.oxfordsemantic.jrdfox.logic.Datatype | ||
14 | import tech.oxfordsemantic.jrdfox.logic.datalog.{ | 14 | import tech.oxfordsemantic.jrdfox.logic.datalog.{ |
15 | Rule, | 15 | Rule, |
16 | BodyFormula, | 16 | BodyFormula, |
@@ -18,7 +18,12 @@ import tech.oxfordsemantic.jrdfox.logic.datalog.{ | |||
18 | TupleTableAtom, | 18 | TupleTableAtom, |
19 | TupleTableName | 19 | TupleTableName |
20 | } | 20 | } |
21 | import tech.oxfordsemantic.jrdfox.logic.expression.{Resource} | 21 | import tech.oxfordsemantic.jrdfox.logic.expression.{ |
22 | Literal, | ||
23 | Resource, | ||
24 | Variable, | ||
25 | Term | ||
26 | } | ||
22 | import tech.oxfordsemantic.jrdfox.logic.sparql.statement.SelectQuery | 27 | import tech.oxfordsemantic.jrdfox.logic.sparql.statement.SelectQuery |
23 | import uk.ac.ox.cs.rsacomb.suffix.Nth | 28 | import uk.ac.ox.cs.rsacomb.suffix.Nth |
24 | import uk.ac.ox.cs.rsacomb.util.Logger | 29 | import uk.ac.ox.cs.rsacomb.util.Logger |
@@ -69,6 +74,13 @@ object RDFoxUtil { | |||
69 | (server, data) | 74 | (server, data) |
70 | } | 75 | } |
71 | 76 | ||
77 | /** Create a built-in `rdfox:SKOLEM` TupleTableAtom. */ | ||
78 | def skolem(name: String, terms: Term*): TupleTableAtom = | ||
79 | TupleTableAtom.create( | ||
80 | TupleTableName.SKOLEM, | ||
81 | (Literal.create(name, Datatype.XSD_STRING) +: terms): _* | ||
82 | ) | ||
83 | |||
72 | /** Prints statistics from RDFox datastore. | 84 | /** Prints statistics from RDFox datastore. |
73 | * | 85 | * |
74 | * Prints something only when Logger level is set to DEBUG or more. | 86 | * Prints something only when Logger level is set to DEBUG or more. |
@@ -127,6 +139,26 @@ object RDFoxUtil { | |||
127 | query | 139 | query |
128 | } | 140 | } |
129 | 141 | ||
142 | /** Export data in `text/turtle`. | ||
143 | * | ||
144 | * @param data datastore connection from which to export data. | ||
145 | * @param rules output stream for rules | ||
146 | * @param facts output stream for facts | ||
147 | */ | ||
148 | def export( | ||
149 | data: DataStoreConnection, | ||
150 | rules: OutputStream, | ||
151 | facts: OutputStream | ||
152 | ): Unit = { | ||
153 | data.exportData(Prefixes.s_emptyPrefixes, facts, "text/turtle", RDFoxOpts()) | ||
154 | data.exportData( | ||
155 | Prefixes.s_emptyPrefixes, | ||
156 | rules, | ||
157 | "application/x.datalog", | ||
158 | RDFoxOpts() | ||
159 | ) | ||
160 | } | ||
161 | |||
130 | /** Parse a SELECT query from a string in SPARQL format. | 162 | /** Parse a SELECT query from a string in SPARQL format. |
131 | * | 163 | * |
132 | * @param query the string containing the SPARQL query | 164 | * @param query the string containing the SPARQL query |
@@ -218,7 +250,7 @@ object RDFoxUtil { | |||
218 | .map(i => s"?S rsa:${pred :: Nth(i)} ?X$i .") | 250 | .map(i => s"?S rsa:${pred :: Nth(i)} ?X$i .") |
219 | .mkString("WHERE {\n", "\n", "\n}") | 251 | .mkString("WHERE {\n", "\n", "\n}") |
220 | } else { | 252 | } else { |
221 | s"ASK { ?X a rsa:${pred :: Nth(0)} }" | 253 | s"ASK { ?X a rsa:$pred }" |
222 | } | 254 | } |
223 | } | 255 | } |
224 | 256 | ||
@@ -231,11 +263,11 @@ object RDFoxUtil { | |||
231 | * built-in `SKOLEM` funtion of RDFox. | 263 | * built-in `SKOLEM` funtion of RDFox. |
232 | */ | 264 | */ |
233 | def reify(rule: Rule): Rule = { | 265 | def reify(rule: Rule): Rule = { |
234 | val (bs, as) = rule.getHead.map(_.reified).unzip | 266 | val (sk, as) = rule.getHead.map(_.reified).unzip |
235 | val head: List[TupleTableAtom] = as.flatten | 267 | val head: List[TupleTableAtom] = as.flatten |
236 | val bind: List[BodyFormula] = bs.flatten | 268 | val skolem: List[BodyFormula] = sk.flatten |
237 | val body: List[BodyFormula] = rule.getBody.map(reify).flatten | 269 | val body: List[BodyFormula] = rule.getBody.map(reify).flatten |
238 | Rule.create(head, bind ::: body) | 270 | Rule.create(head, skolem ::: body) |
239 | } | 271 | } |
240 | 272 | ||
241 | /** Reify a [[tech.oxfordsemantic.jrdfox.logic.datalog.BodyFormula BodyFormula]]. | 273 | /** Reify a [[tech.oxfordsemantic.jrdfox.logic.datalog.BodyFormula BodyFormula]]. |
@@ -250,15 +282,16 @@ object RDFoxUtil { | |||
250 | formula match { | 282 | formula match { |
251 | case atom: TupleTableAtom => atom.reified._2 | 283 | case atom: TupleTableAtom => atom.reified._2 |
252 | case neg: Negation => { | 284 | case neg: Negation => { |
253 | val (bs, as) = neg.getNegatedAtoms | 285 | val (sk, as) = neg.getNegatedAtoms |
254 | .map({ | 286 | .map({ |
255 | case a: TupleTableAtom => a.reified | 287 | case a: TupleTableAtom => a.reified |
256 | case a => (None, List(a)) | 288 | case a => (None, List(a)) |
257 | }) | 289 | }) |
258 | .unzip | 290 | .unzip |
259 | val bind = bs.flatten.map(_.getBoundVariable) | 291 | val skolem = |
292 | sk.flatten.map(_.getArguments.last).collect { case v: Variable => v } | ||
260 | val atoms = as.flatten | 293 | val atoms = as.flatten |
261 | List(Negation.create(bind, atoms)) | 294 | List(Negation.create(skolem, atoms)) |
262 | } | 295 | } |
263 | case other => List(other) | 296 | case other => List(other) |
264 | } | 297 | } |