diff options
author | Federico Igne <federico.igne@cs.ox.ac.uk> | 2020-09-07 13:08:31 +0200 |
---|---|---|
committer | Federico Igne <federico.igne@cs.ox.ac.uk> | 2020-09-07 13:08:31 +0200 |
commit | 2b661f3ac6fdb5156168b6775ede24e4c7b53758 (patch) | |
tree | 0e3514c38fedf41febff82e9bf93aea2ab4d7a7f | |
parent | 7e126824e9a6cb456295d2f1535aef975bb63237 (diff) | |
download | RSAComb-2b661f3ac6fdb5156168b6775ede24e4c7b53758.tar.gz RSAComb-2b661f3ac6fdb5156168b6775ede24e4c7b53758.zip |
Add setup code to compute the RSA filtering program
Not all rules of the filtering program have been implemented, but the
code for the generation and reification of the rules seems to work.
-rw-r--r-- | src/main/scala/rsacomb/Main.scala | 18 | ||||
-rw-r--r-- | src/main/scala/rsacomb/RDFoxClassExprConverter.scala | 4 | ||||
-rw-r--r-- | src/main/scala/rsacomb/RDFoxUtil.scala | 6 | ||||
-rw-r--r-- | src/main/scala/rsacomb/RSA.scala | 24 | ||||
-rw-r--r-- | src/main/scala/rsacomb/RSAAxiom.scala | 92 | ||||
-rw-r--r-- | src/main/scala/rsacomb/RSAOntology.scala | 165 |
6 files changed, 261 insertions, 48 deletions
diff --git a/src/main/scala/rsacomb/Main.scala b/src/main/scala/rsacomb/Main.scala index 9cd6680..d2bc2a8 100644 --- a/src/main/scala/rsacomb/Main.scala +++ b/src/main/scala/rsacomb/Main.scala | |||
@@ -48,18 +48,20 @@ object RSAComb { | |||
48 | */ | 48 | */ |
49 | 49 | ||
50 | val ontology = RSA.loadOntology(ontoPath) | 50 | val ontology = RSA.loadOntology(ontoPath) |
51 | ontology.isRSA | 51 | if (ontology.isRSA) { |
52 | 52 | ||
53 | /* Build canonical model */ | 53 | /* Build canonical model */ |
54 | //val tboxCanon = rsa.canonicalModel() | 54 | //val tboxCanon = rsa.canonicalModel() |
55 | 55 | ||
56 | /* Load query */ | 56 | /* Load query */ |
57 | //val query = ... | 57 | val query = RSA.test_query |
58 | 58 | ||
59 | /* Compute the filtering program from the given query */ | 59 | /* Compute the filtering program from the given query */ |
60 | //val tboxFilter = rsa.filteringProgram(query) | 60 | val filter = ontology.getFilteringProgram(query) |
61 | 61 | ||
62 | /* ... */ | 62 | /* ... */ |
63 | |||
64 | } | ||
63 | 65 | ||
64 | /* DEBUG ONLY */ | 66 | /* DEBUG ONLY */ |
65 | println("Ok!") | 67 | println("Ok!") |
diff --git a/src/main/scala/rsacomb/RDFoxClassExprConverter.scala b/src/main/scala/rsacomb/RDFoxClassExprConverter.scala index b9d3ea2..467b3f1 100644 --- a/src/main/scala/rsacomb/RDFoxClassExprConverter.scala +++ b/src/main/scala/rsacomb/RDFoxClassExprConverter.scala | |||
@@ -61,7 +61,8 @@ class RDFoxClassExprConverter( | |||
61 | 61 | ||
62 | // OWLClass | 62 | // OWLClass |
63 | override def visit(expr: OWLClass): RDFoxRuleShards = { | 63 | override def visit(expr: OWLClass): RDFoxRuleShards = { |
64 | val atom = List(Atom.rdf(term, IRI.RDF_TYPE, expr.getIRI())) | 64 | val iri: IRI = if (expr.isTopEntity()) IRI.THING else expr.getIRI() |
65 | val atom = List(Atom.rdf(term, IRI.RDF_TYPE, iri)) | ||
65 | RDFoxRuleShards(atom, List()) | 66 | RDFoxRuleShards(atom, List()) |
66 | } | 67 | } |
67 | 68 | ||
@@ -97,6 +98,7 @@ class RDFoxClassExprConverter( | |||
97 | // TODO: variables needs to be handled at visitor level. Hardcoding | 98 | // TODO: variables needs to be handled at visitor level. Hardcoding |
98 | // the name of the varibles might lead to errors for complex cases. | 99 | // the name of the varibles might lead to errors for complex cases. |
99 | val y = Variable.create("y") | 100 | val y = Variable.create("y") |
101 | // Here we are assuming a role name | ||
100 | val prop = expr.getProperty() | 102 | val prop = expr.getProperty() |
101 | // Computes the result of rule skolemization. Depending on the used | 103 | // Computes the result of rule skolemization. Depending on the used |
102 | // technique it might involve the introduction of additional atoms, | 104 | // technique it might involve the introduction of additional atoms, |
diff --git a/src/main/scala/rsacomb/RDFoxUtil.scala b/src/main/scala/rsacomb/RDFoxUtil.scala index 4cefd83..9699fb4 100644 --- a/src/main/scala/rsacomb/RDFoxUtil.scala +++ b/src/main/scala/rsacomb/RDFoxUtil.scala | |||
@@ -15,6 +15,10 @@ object RDFoxUtil { | |||
15 | IRI.create(iri.getIRIString()) | 15 | IRI.create(iri.getIRIString()) |
16 | } | 16 | } |
17 | 17 | ||
18 | implicit def owlapi2rdfox(iri: String): IRI = { | ||
19 | IRI.create(iri) | ||
20 | } | ||
21 | |||
18 | def openConnection( | 22 | def openConnection( |
19 | dataStore: String | 23 | dataStore: String |
20 | ): (ServerConnection, DataStoreConnection) = { | 24 | ): (ServerConnection, DataStoreConnection) = { |
@@ -37,7 +41,7 @@ object RDFoxUtil { | |||
37 | (server, data) | 41 | (server, data) |
38 | } | 42 | } |
39 | 43 | ||
40 | def query( | 44 | def submitQuery( |
41 | data: DataStoreConnection, | 45 | data: DataStoreConnection, |
42 | prefixes: Prefixes, | 46 | prefixes: Prefixes, |
43 | query: String | 47 | query: String |
diff --git a/src/main/scala/rsacomb/RSA.scala b/src/main/scala/rsacomb/RSA.scala index 79617c7..c0d4e51 100644 --- a/src/main/scala/rsacomb/RSA.scala +++ b/src/main/scala/rsacomb/RSA.scala | |||
@@ -8,6 +8,18 @@ import tech.oxfordsemantic.jrdfox.Prefixes | |||
8 | import tech.oxfordsemantic.jrdfox.logic.IRI | 8 | import tech.oxfordsemantic.jrdfox.logic.IRI |
9 | import org.semanticweb.owlapi.apibinding.OWLManager | 9 | import org.semanticweb.owlapi.apibinding.OWLManager |
10 | import org.semanticweb.owlapi.model.OWLOntology | 10 | import org.semanticweb.owlapi.model.OWLOntology |
11 | import rsacomb.RSAOntology | ||
12 | |||
13 | // Debug only | ||
14 | import tech.oxfordsemantic.jrdfox.logic.{ | ||
15 | Formula, | ||
16 | Atom, | ||
17 | Variable, | ||
18 | Query, | ||
19 | QueryType, | ||
20 | Conjunction | ||
21 | } | ||
22 | import scala.collection.JavaConverters._ | ||
11 | 23 | ||
12 | object RSA extends RSAOntology { | 24 | object RSA extends RSAOntology { |
13 | 25 | ||
@@ -18,6 +30,18 @@ object RSA extends RSAOntology { | |||
18 | Prefixes.declarePrefix("rdfs:", "http://www.w3.org/2000/01/rdf-schema#") | 30 | Prefixes.declarePrefix("rdfs:", "http://www.w3.org/2000/01/rdf-schema#") |
19 | Prefixes.declarePrefix("owl:", "http://www.w3.org/2002/07/owl#") | 31 | Prefixes.declarePrefix("owl:", "http://www.w3.org/2002/07/owl#") |
20 | 32 | ||
33 | val varX = Variable.create("X") | ||
34 | val varY = Variable.create("Y") | ||
35 | val varZ = Variable.create("Z") | ||
36 | val testAnswerVars = List[Variable](varX, varY, varZ).asJava; | ||
37 | val testFormula: Formula = | ||
38 | Conjunction.create( | ||
39 | Atom.rdf(varX, IRI.TOP_OBJECT_PROPERTY, varY), | ||
40 | Atom.rdf(varY, IRI.TOP_OBJECT_PROPERTY, varZ) | ||
41 | ) | ||
42 | val test_query = | ||
43 | Query.create(QueryType.SELECT, false, testAnswerVars, testFormula) | ||
44 | |||
21 | def internal(name: String): IRI = | 45 | def internal(name: String): IRI = |
22 | IRI.create( | 46 | IRI.create( |
23 | Prefixes.getPrefixIRIsByPrefixName.get("internal:").getIRI + name | 47 | Prefixes.getPrefixIRIsByPrefixName.get("internal:").getIRI + name |
diff --git a/src/main/scala/rsacomb/RSAAxiom.scala b/src/main/scala/rsacomb/RSAAxiom.scala index 9e9a016..50dc37e 100644 --- a/src/main/scala/rsacomb/RSAAxiom.scala +++ b/src/main/scala/rsacomb/RSAAxiom.scala | |||
@@ -1,10 +1,23 @@ | |||
1 | package rsacomb | 1 | package rsacomb |
2 | 2 | ||
3 | /* Java imports */ | 3 | /* Java imports */ |
4 | import org.semanticweb.owlapi.model.{OWLAxiom,OWLSubClassOfAxiom, OWLEquivalentClassesAxiom} | 4 | import org.semanticweb.owlapi.model.{ |
5 | import org.semanticweb.owlapi.model.{OWLObjectPropertyExpression,OWLClass,OWLClassExpression,OWLObjectSomeValuesFrom,OWLObjectMaxCardinality} | 5 | OWLAxiom, |
6 | OWLSubClassOfAxiom, | ||
7 | OWLEquivalentClassesAxiom | ||
8 | } | ||
9 | import org.semanticweb.owlapi.model.{ | ||
10 | OWLObjectPropertyExpression, | ||
11 | OWLClass, | ||
12 | OWLClassExpression, | ||
13 | OWLObjectSomeValuesFrom, | ||
14 | OWLObjectMaxCardinality | ||
15 | } | ||
6 | import org.semanticweb.owlapi.model.ClassExpressionType | 16 | import org.semanticweb.owlapi.model.ClassExpressionType |
7 | import org.semanticweb.owlapi.model.{OWLAxiomVisitorEx,OWLClassExpressionVisitorEx} | 17 | import org.semanticweb.owlapi.model.{ |
18 | OWLAxiomVisitorEx, | ||
19 | OWLClassExpressionVisitorEx | ||
20 | } | ||
8 | 21 | ||
9 | /* Wrapper trait for the implicit class `RSAAxiom`. | 22 | /* Wrapper trait for the implicit class `RSAAxiom`. |
10 | */ | 23 | */ |
@@ -16,10 +29,10 @@ trait RSAAxiom { | |||
16 | */ | 29 | */ |
17 | private sealed trait RSAAxiomType | 30 | private sealed trait RSAAxiomType |
18 | private object RSAAxiomType { | 31 | private object RSAAxiomType { |
19 | case object T3 extends RSAAxiomType // ∃R.A ⊑ B | 32 | case object T3 extends RSAAxiomType // ∃R.A ⊑ B |
20 | case object T3top extends RSAAxiomType // ∃R.⊤ ⊑ B | 33 | case object T3top extends RSAAxiomType // ∃R.⊤ ⊑ B |
21 | case object T4 extends RSAAxiomType // A ⊑ ≤1R.B | 34 | case object T4 extends RSAAxiomType // A ⊑ ≤1R.B |
22 | case object T5 extends RSAAxiomType // A ⊑ ∃R.B | 35 | case object T5 extends RSAAxiomType // A ⊑ ∃R.B |
23 | } | 36 | } |
24 | 37 | ||
25 | /* Implements additional features on top of `OWLAxiom` from | 38 | /* Implements additional features on top of `OWLAxiom` from |
@@ -32,20 +45,22 @@ trait RSAAxiom { | |||
32 | * In order to reason about role unsafety in Horn-ALCHOIQ | 45 | * In order to reason about role unsafety in Horn-ALCHOIQ |
33 | * ontologies we need to detect and filter axioms by their | 46 | * ontologies we need to detect and filter axioms by their |
34 | * "type". | 47 | * "type". |
35 | * | 48 | * |
36 | * This is a simple implementation following the Visitor | 49 | * This is a simple implementation following the Visitor |
37 | * pattern imposed by the OWLAPI. | 50 | * pattern imposed by the OWLAPI. |
38 | */ | 51 | */ |
39 | private class RSAAxiomTypeDetector(t: RSAAxiomType) | 52 | private class RSAAxiomTypeDetector(t: RSAAxiomType) |
40 | extends OWLAxiomVisitorEx[Boolean] | 53 | extends OWLAxiomVisitorEx[Boolean] { |
41 | { | 54 | override def visit(axiom: OWLSubClassOfAxiom): Boolean = { |
42 | override | ||
43 | def visit(axiom: OWLSubClassOfAxiom): Boolean = { | ||
44 | val sub = axiom.getSubClass().getClassExpressionType() | 55 | val sub = axiom.getSubClass().getClassExpressionType() |
45 | val sup = axiom.getSuperClass().getClassExpressionType() | 56 | val sup = axiom.getSuperClass().getClassExpressionType() |
46 | t match { | 57 | t match { |
47 | case RSAAxiomType.T3top => // ∃R.⊤ ⊑ B | 58 | case RSAAxiomType.T3top => // ∃R.⊤ ⊑ B |
48 | axiom.isT5 && axiom.getSubClass().asInstanceOf[OWLObjectSomeValuesFrom].getFiller.isOWLThing | 59 | axiom.isT3 && axiom |
60 | .getSubClass() | ||
61 | .asInstanceOf[OWLObjectSomeValuesFrom] | ||
62 | .getFiller | ||
63 | .isOWLThing | ||
49 | case RSAAxiomType.T3 => // ∃R.A ⊑ B | 64 | case RSAAxiomType.T3 => // ∃R.A ⊑ B |
50 | sub == ClassExpressionType.OBJECT_SOME_VALUES_FROM && sup == ClassExpressionType.OWL_CLASS | 65 | sub == ClassExpressionType.OBJECT_SOME_VALUES_FROM && sup == ClassExpressionType.OWL_CLASS |
51 | case RSAAxiomType.T4 => // A ⊑ ≤1R.B | 66 | case RSAAxiomType.T4 => // A ⊑ ≤1R.B |
@@ -55,13 +70,12 @@ trait RSAAxiom { | |||
55 | } | 70 | } |
56 | } | 71 | } |
57 | 72 | ||
58 | override | 73 | override def visit(axiom: OWLEquivalentClassesAxiom): Boolean = { |
59 | def visit(axiom: OWLEquivalentClassesAxiom): Boolean = { | ||
60 | // TODO | 74 | // TODO |
61 | false | 75 | false |
62 | } | 76 | } |
63 | 77 | ||
64 | def doDefault(axiom : OWLAxiom): Boolean = false | 78 | def doDefault(axiom: OWLAxiom): Boolean = false |
65 | } | 79 | } |
66 | 80 | ||
67 | private def isOfType(t: RSAAxiomType): Boolean = { | 81 | private def isOfType(t: RSAAxiomType): Boolean = { |
@@ -74,13 +88,13 @@ trait RSAAxiom { | |||
74 | def isT3: Boolean = isOfType(RSAAxiomType.T3) | 88 | def isT3: Boolean = isOfType(RSAAxiomType.T3) |
75 | def isT4: Boolean = isOfType(RSAAxiomType.T4) | 89 | def isT4: Boolean = isOfType(RSAAxiomType.T4) |
76 | def isT5: Boolean = isOfType(RSAAxiomType.T5) | 90 | def isT5: Boolean = isOfType(RSAAxiomType.T5) |
77 | 91 | ||
78 | /* Extracting ObjectPropertyExpressions from axioms | 92 | /* Extracting ObjectPropertyExpressions from axioms |
79 | * | 93 | * |
80 | * This extracts all ObjectPropertyExpressions from a given | 94 | * This extracts all ObjectPropertyExpressions from a given |
81 | * axiom. While the implementation is generic we use it on axioms | 95 | * axiom. While the implementation is generic we use it on axioms |
82 | * of specific types (see above). | 96 | * of specific types (see above). |
83 | * | 97 | * |
84 | * NOTE: it is not possible to use the `objectPropertyInSignature` | 98 | * NOTE: it is not possible to use the `objectPropertyInSignature` |
85 | * method of `OWLAxiom` because it returns all "role names" involved | 99 | * method of `OWLAxiom` because it returns all "role names" involved |
86 | * in the signature of an axiom. In particular we won't get the inverse | 100 | * in the signature of an axiom. In particular we won't get the inverse |
@@ -88,55 +102,61 @@ trait RSAAxiom { | |||
88 | * itself instead). | 102 | * itself instead). |
89 | */ | 103 | */ |
90 | private class RSAAxiomRoleExtractor() | 104 | private class RSAAxiomRoleExtractor() |
91 | extends OWLAxiomVisitorEx[List[OWLObjectPropertyExpression]] | 105 | extends OWLAxiomVisitorEx[List[OWLObjectPropertyExpression]] { |
92 | { | ||
93 | 106 | ||
94 | private class RSAExprRoleExtractor() | 107 | private class RSAExprRoleExtractor() |
95 | extends OWLClassExpressionVisitorEx[List[OWLObjectPropertyExpression]] | 108 | extends OWLClassExpressionVisitorEx[ |
96 | { | 109 | List[OWLObjectPropertyExpression] |
97 | override | 110 | ] { |
98 | def visit(expr: OWLObjectSomeValuesFrom): List[OWLObjectPropertyExpression] = | 111 | override def visit( |
112 | expr: OWLObjectSomeValuesFrom | ||
113 | ): List[OWLObjectPropertyExpression] = | ||
99 | List(expr.getProperty) | 114 | List(expr.getProperty) |
100 | 115 | ||
101 | override | 116 | override def visit( |
102 | def visit(expr: OWLObjectMaxCardinality): List[OWLObjectPropertyExpression] = | 117 | expr: OWLObjectMaxCardinality |
118 | ): List[OWLObjectPropertyExpression] = | ||
103 | List(expr.getProperty) | 119 | List(expr.getProperty) |
104 | 120 | ||
105 | /* NOTE: this instance of `visit` for `OWLClass` shouldn't be necessary. However | 121 | /* NOTE: this instance of `visit` for `OWLClass` shouldn't be necessary. However |
106 | * if missing, the code throws a `NullPointerException`. It seems like, for some | 122 | * if missing, the code throws a `NullPointerException`. It seems like, for some |
107 | * reason, `OWLClass` is not really a subinterface of `OWLClassExpression`, as | 123 | * reason, `OWLClass` is not really a subinterface of `OWLClassExpression`, as |
108 | * stated in the JavaDocs. | 124 | * stated in the JavaDocs. |
109 | */ | 125 | */ |
110 | override | 126 | override def visit(expr: OWLClass): List[OWLObjectPropertyExpression] = |
111 | def visit(expr: OWLClass): List[OWLObjectPropertyExpression] = | ||
112 | List() | 127 | List() |
113 | 128 | ||
114 | def doDefault(expr: OWLClassExpression): List[OWLObjectPropertyExpression] = | 129 | def doDefault( |
130 | expr: OWLClassExpression | ||
131 | ): List[OWLObjectPropertyExpression] = | ||
115 | List() | 132 | List() |
116 | } | 133 | } |
117 | 134 | ||
118 | override | 135 | override def visit( |
119 | def visit(axiom: OWLSubClassOfAxiom): List[OWLObjectPropertyExpression] = { | 136 | axiom: OWLSubClassOfAxiom |
137 | ): List[OWLObjectPropertyExpression] = { | ||
120 | val visitor = new RSAExprRoleExtractor() | 138 | val visitor = new RSAExprRoleExtractor() |
121 | val sub = axiom.getSubClass.accept(visitor) | 139 | val sub = axiom.getSubClass.accept(visitor) |
122 | val sup = axiom.getSuperClass.accept(visitor) | 140 | val sup = axiom.getSuperClass.accept(visitor) |
123 | sub ++ sup | 141 | sub ++ sup |
124 | } | 142 | } |
125 | 143 | ||
126 | override | 144 | override def visit( |
127 | def visit(axiom: OWLEquivalentClassesAxiom): List[OWLObjectPropertyExpression] = { | 145 | axiom: OWLEquivalentClassesAxiom |
146 | ): List[OWLObjectPropertyExpression] = { | ||
128 | // TODO | 147 | // TODO |
129 | List() | 148 | List() |
130 | } | 149 | } |
131 | 150 | ||
132 | def doDefault(axiom : OWLAxiom): List[OWLObjectPropertyExpression] = List() | 151 | def doDefault(axiom: OWLAxiom): List[OWLObjectPropertyExpression] = List() |
133 | } | 152 | } |
134 | 153 | ||
135 | /* Exposed methods */ | 154 | /* Exposed methods */ |
136 | def objectPropertyExpressionsInSignature: List[OWLObjectPropertyExpression] = { | 155 | def objectPropertyExpressionsInSignature |
156 | : List[OWLObjectPropertyExpression] = { | ||
137 | val visitor = new RSAAxiomRoleExtractor() | 157 | val visitor = new RSAAxiomRoleExtractor() |
138 | axiom.accept(visitor) | 158 | axiom.accept(visitor) |
139 | } | 159 | } |
140 | } | 160 | } |
141 | 161 | ||
142 | } // trait RSAAxiom \ No newline at end of file | 162 | } // trait RSAAxiom |
diff --git a/src/main/scala/rsacomb/RSAOntology.scala b/src/main/scala/rsacomb/RSAOntology.scala index 3d9210e..9d52612 100644 --- a/src/main/scala/rsacomb/RSAOntology.scala +++ b/src/main/scala/rsacomb/RSAOntology.scala | |||
@@ -19,6 +19,7 @@ import scalax.collection.GraphEdge.UnDiEdge | |||
19 | 19 | ||
20 | /* Debug only */ | 20 | /* Debug only */ |
21 | import org.semanticweb.owlapi.dlsyntax.renderer.DLSyntaxObjectRenderer | 21 | import org.semanticweb.owlapi.dlsyntax.renderer.DLSyntaxObjectRenderer |
22 | import tech.oxfordsemantic.jrdfox.logic._ | ||
22 | 23 | ||
23 | /* Wrapper trait for the implicit class `RSAOntology`. | 24 | /* Wrapper trait for the implicit class `RSAOntology`. |
24 | */ | 25 | */ |
@@ -163,8 +164,9 @@ trait RSAOntology { | |||
163 | } yield role1 | 164 | } yield role1 |
164 | 165 | ||
165 | /* TODO: We should be able to avoid this last conversion to List. | 166 | /* TODO: We should be able to avoid this last conversion to List. |
166 | * Maybe we should just move everything to Sets instead of Lists, since | 167 | * Maybe we should just move everything to Sets instead of Lists, |
167 | * they have a more straightforward conversion from Java collections. | 168 | * since they have a more straightforward conversion from Java |
169 | * collections. | ||
168 | */ | 170 | */ |
169 | (unsafe1 ++ unsafe2).toList | 171 | (unsafe1 ++ unsafe2).toList |
170 | } | 172 | } |
@@ -184,6 +186,165 @@ trait RSAOntology { | |||
184 | Graph(edges: _*) | 186 | Graph(edges: _*) |
185 | } | 187 | } |
186 | 188 | ||
189 | def getFilteringProgram(query: Query): List[Rule] = { | ||
190 | |||
191 | // Import implicit conversion to RDFox IRI | ||
192 | import RDFoxUtil._ | ||
193 | |||
194 | sealed trait Reified; | ||
195 | case class ReifiedHead(bind: BindAtom, atoms: List[Atom]) extends Reified | ||
196 | case class ReifiedBody(atoms: List[Atom]) extends Reified | ||
197 | case class Unaltered(formula: BodyFormula) extends Reified | ||
198 | |||
199 | def getBindAtom(atom: Atom): BindAtom = { | ||
200 | // TODO: We need to implement another way to introduce fresh | ||
201 | // variables. | ||
202 | val varA = Variable.create("A") | ||
203 | val args = atom | ||
204 | .getArguments() | ||
205 | .asScala | ||
206 | .toSeq | ||
207 | //.prepended(atom.getTupleTableName.getIRI) | ||
208 | BindAtom.create( | ||
209 | BuiltinFunctionCall | ||
210 | .create("SKOLEM", args: _*), | ||
211 | varA | ||
212 | ) | ||
213 | } | ||
214 | |||
215 | def reifyAtom(atom: Atom, variable: Variable): List[Atom] = { | ||
216 | def iri(i: Int) = atom.getTupleTableName().getIRI() ++ s"_$i" | ||
217 | atom | ||
218 | .getArguments() | ||
219 | .asScala | ||
220 | .zipWithIndex | ||
221 | .map { case (t, i) => Atom.rdf(variable, iri(i), t) } | ||
222 | .toList | ||
223 | } | ||
224 | |||
225 | // Is this the best way to determine if an atom is an RDF triple? | ||
226 | // Note that we can't use `getNumberOfArguments()` because is not | ||
227 | // "consistent": | ||
228 | // - for an atom created with `rdf(<term1>, <term2>, <term3>)`, | ||
229 | // `getNumberOfArguments` returns 3 | ||
230 | // - for an atom created with `Atom.create(<tupletablename>, <term1>, | ||
231 | // <term2>, <term3>)`, `getNumberOfArguments()` returns 3 | ||
232 | // | ||
233 | // This is probably because `Atom.rdf(...) is implemented as: | ||
234 | // ```scala | ||
235 | // def rdf(term1: Term, term2: Term, term3: Term): Atom = | ||
236 | // Atom.create(TupleTableName.create("internal:triple"), term1, term2, term3) | ||
237 | // ``` | ||
238 | def isRdfTriple(atom: Atom): Boolean = | ||
239 | atom.getTupleTableName.getIRI.equals("internal:triple") | ||
240 | |||
241 | def reify( | ||
242 | formula: BodyFormula, | ||
243 | head: Boolean | ||
244 | ): Reified = { | ||
245 | def default[A <: BodyFormula](x: A) = Unaltered(x) | ||
246 | formula match { | ||
247 | case a: Atom => { | ||
248 | if (!isRdfTriple(a)) { | ||
249 | if (head) { | ||
250 | val b = getBindAtom(a) | ||
251 | ReifiedHead(b, reifyAtom(a, b.getBoundVariable)) | ||
252 | } else { | ||
253 | val varA = Variable.create("A") | ||
254 | ReifiedBody(reifyAtom(a, varA)) | ||
255 | } | ||
256 | } else { | ||
257 | default(a) | ||
258 | } | ||
259 | } | ||
260 | case a => default(a) | ||
261 | } | ||
262 | } | ||
263 | |||
264 | def skolemizeRule(rule: Rule): Rule = { | ||
265 | // Rule body | ||
266 | val body = | ||
267 | rule.getBody.asScala.map(reify(_, false)).flatMap { | ||
268 | case ReifiedHead(_, _) => List(); /* handle impossible case */ | ||
269 | case ReifiedBody(x) => x; | ||
270 | case Unaltered(x) => List(x) | ||
271 | } | ||
272 | // Rule head | ||
273 | val reified = rule.getHead.asScala.map(reify(_, true)) | ||
274 | val skols = reified.flatMap { | ||
275 | case ReifiedHead(x, _) => Some(x); | ||
276 | case ReifiedBody(_) => None; /* handle impossible case */ | ||
277 | case Unaltered(_) => None | ||
278 | } | ||
279 | val head = reified.flatMap { | ||
280 | case ReifiedHead(_, x) => x; | ||
281 | case ReifiedBody(_) => List(); /* handle impossible case */ | ||
282 | case Unaltered(x) => | ||
283 | List(x.asInstanceOf[Atom]) /* Can we do better that a cast? */ | ||
284 | } | ||
285 | Rule.create(head.asJava, (skols ++ body).asJava) | ||
286 | } | ||
287 | |||
288 | def formulaToRuleBody(body: Formula): List[BodyFormula] = { | ||
289 | body match { | ||
290 | case a: BodyFormula => List(a); | ||
291 | case a: Conjunction => | ||
292 | a.getConjuncts().asScala.toList.flatMap(formulaToRuleBody(_)); | ||
293 | case _ => List() /* We don't handle this for now */ | ||
294 | } | ||
295 | } | ||
296 | |||
297 | val body = formulaToRuleBody(query.getQueryFormula) | ||
298 | val vars: List[Term] = query.getAnswerVariables.asScala.toList | ||
299 | def id(t1: Term, t2: Term) = | ||
300 | Atom.create( | ||
301 | TupleTableName.create("http://127.0.0.1/ID"), | ||
302 | vars.appendedAll(List(t1, t2)).asJava | ||
303 | ) | ||
304 | val qm = Atom.create(TupleTableName.create("QM"), vars.asJava) | ||
305 | |||
306 | /* Filtering program */ | ||
307 | val rule1 = Rule.create(qm, body.asJava) | ||
308 | val rule3a = | ||
309 | for ((v, i) <- vars.zipWithIndex) | ||
310 | yield Rule.create( | ||
311 | id( | ||
312 | IRI.create(s"http://127.0.0.1/$i"), | ||
313 | IRI.create(s"http://127.0.0.1/$i") | ||
314 | ), | ||
315 | List( | ||
316 | qm, | ||
317 | Negation.create( | ||
318 | Atom.rdf(v, IRI.RDF_TYPE, IRI.create("http://127.0.0.1/NI")) | ||
319 | ) | ||
320 | ).asJava | ||
321 | ) | ||
322 | val rule3b = Rule.create( | ||
323 | id(Variable.create("V"), Variable.create("U")), | ||
324 | id(Variable.create("U"), Variable.create("V")) | ||
325 | ) | ||
326 | val rule3c = Rule.create( | ||
327 | id(Variable.create("U"), Variable.create("W")), | ||
328 | List[BodyFormula]( | ||
329 | id(Variable.create("U"), Variable.create("V")), | ||
330 | id(Variable.create("V"), Variable.create("W")) | ||
331 | ).asJava | ||
332 | ) | ||
333 | |||
334 | var rules: List[Rule] = | ||
335 | List.empty | ||
336 | .prepended(rule3c) | ||
337 | .prepended(rule3b) | ||
338 | .prependedAll(rule3a) | ||
339 | .prepended(rule1) | ||
340 | |||
341 | // DEBUG | ||
342 | println("FILTERING PROGRAM:") | ||
343 | rules.map(skolemizeRule(_)).foreach(println(_)) | ||
344 | |||
345 | List() | ||
346 | } | ||
347 | |||
187 | } // implicit class RSAOntology | 348 | } // implicit class RSAOntology |
188 | 349 | ||
189 | } // trait RSAOntology | 350 | } // trait RSAOntology |