diff options
author | Federico Igne <federico.igne@cs.ox.ac.uk> | 2022-05-19 15:08:24 +0100 |
---|---|---|
committer | Federico Igne <federico.igne@cs.ox.ac.uk> | 2022-05-19 15:21:37 +0100 |
commit | 3b21b25763dffd117488e99378d466fcf97c6d52 (patch) | |
tree | ca67179d57569d8d8fd16fb732a37fae84a4be61 | |
parent | 8af8f165f0a2e7ad06605daf89700073eb20c5ef (diff) | |
download | RSAComb-3b21b25763dffd117488e99378d466fcf97c6d52.tar.gz RSAComb-3b21b25763dffd117488e99378d466fcf97c6d52.zip |
fix(lowerbound): cycle detection in approximation algorithm
-rw-r--r-- | src/main/scala/uk/ac/ox/cs/rsacomb/approximation/Lowerbound.scala | 110 |
1 files changed, 99 insertions, 11 deletions
diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/approximation/Lowerbound.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/approximation/Lowerbound.scala index 3cc8fc3..1b72699 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/approximation/Lowerbound.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/approximation/Lowerbound.scala | |||
@@ -3,18 +3,18 @@ package uk.ac.ox.cs.rsacomb.approximation | |||
3 | import java.io.File | 3 | import java.io.File |
4 | 4 | ||
5 | import org.semanticweb.owlapi.apibinding.OWLManager | 5 | import org.semanticweb.owlapi.apibinding.OWLManager |
6 | import org.semanticweb.owlapi.model.{IRI => _, _} | 6 | import org.semanticweb.owlapi.model.{IRI => OWLIRI, _} |
7 | 7 | ||
8 | import tech.oxfordsemantic.jrdfox.logic.expression.{Resource, IRI} | 8 | import tech.oxfordsemantic.jrdfox.logic.expression.{Resource, IRI, Literal} |
9 | 9 | ||
10 | import scala.collection.mutable.{Set, Map} | 10 | import scala.collection.mutable.{Set, Map} |
11 | import scalax.collection.Graph | 11 | import scalax.collection.Graph |
12 | import scalax.collection.GraphPredef._, scalax.collection.GraphEdge._ | 12 | import scalax.collection.GraphPredef._, scalax.collection.GraphEdge._ |
13 | import scalax.collection.GraphTraversal._ | 13 | import scalax.collection.GraphTraversal._ |
14 | 14 | ||
15 | import uk.ac.ox.cs.rsacomb.RSAOntology | 15 | import uk.ac.ox.cs.rsacomb.ontology.RSAOntology |
16 | import uk.ac.ox.cs.rsacomb.ontology.Ontology | 16 | import uk.ac.ox.cs.rsacomb.ontology.Ontology |
17 | import uk.ac.ox.cs.rsacomb.util.DataFactory | 17 | import uk.ac.ox.cs.rsacomb.util.{DataFactory, RDFoxUtil, RSA} |
18 | 18 | ||
19 | object Lowerbound { | 19 | object Lowerbound { |
20 | 20 | ||
@@ -86,7 +86,7 @@ class Lowerbound(implicit fresh: DataFactory) | |||
86 | } | 86 | } |
87 | case a: OWLTransitiveObjectPropertyAxiom => false | 87 | case a: OWLTransitiveObjectPropertyAxiom => false |
88 | case a: OWLReflexiveObjectPropertyAxiom => false | 88 | case a: OWLReflexiveObjectPropertyAxiom => false |
89 | case a: OWLSubPropertyChainOfAxiom => false | 89 | case a: OWLSubPropertyChainOfAxiom => true /*TODO: should we leave it? */ |
90 | case a: OWLAsymmetricObjectPropertyAxiom => false | 90 | case a: OWLAsymmetricObjectPropertyAxiom => false |
91 | case a => true | 91 | case a => true |
92 | } | 92 | } |
@@ -172,8 +172,13 @@ class Lowerbound(implicit fresh: DataFactory) | |||
172 | * @return the approximated RSA ontology | 172 | * @return the approximated RSA ontology |
173 | */ | 173 | */ |
174 | private def toRSA(ontology: Ontology): RSAOntology = { | 174 | private def toRSA(ontology: Ontology): RSAOntology = { |
175 | /* Compute the dependency graph for the ontology */ | 175 | val factory = Lowerbound.factory |
176 | val (graph, nodemap) = ontology.dependencyGraph | 176 | val (graph,nodemap) = ontology.dependencyGraph |
177 | val (server,data) = RDFoxUtil.openConnection(Ontology.DataStore) | ||
178 | |||
179 | /* G is an oriented forest. | ||
180 | * This is a custom DFS visit on the dependency graph. | ||
181 | */ | ||
177 | 182 | ||
178 | /* Define node colors for the graph visit */ | 183 | /* Define node colors for the graph visit */ |
179 | sealed trait NodeColor | 184 | sealed trait NodeColor |
@@ -210,16 +215,99 @@ class Lowerbound(implicit fresh: DataFactory) | |||
210 | } | 215 | } |
211 | } | 216 | } |
212 | 217 | ||
213 | val toDelete = color.collect { case (resource: IRI, ToDelete) => | 218 | val delete: Set[OWLAxiom] = |
214 | nodemap(resource.getIRI) | 219 | Set.from(color.collect { |
215 | }.toList | 220 | case (resource: IRI, ToDelete) => nodemap(resource.getIRI) |
221 | }) | ||
222 | /* Equality safety: condition 1 */ | ||
223 | val answers2 = RDFoxUtil.submitQuery(data, s""" | ||
224 | SELECT ?a ?s ?b | ||
225 | WHERE { | ||
226 | graph ${Ontology.RSACheck} { ?w ${RSA.CONGRUENT} ?t } . | ||
227 | filter ( ?w != ?t ) . | ||
228 | graph ${Ontology.RSACheck} { ?t ?r [ a ${RSA.U} ] } . | ||
229 | graph ${Ontology.RBoxReasoning} { | ||
230 | ?r rdfs:subPropertyOf [ owl:inverseOf ?s ] . | ||
231 | ?x rdf:type owl:Restriction ; | ||
232 | owl:onProperty ?s ; | ||
233 | owl:maxQualifiedCardinality "1"^^xsd:nonNegativeInteger ; | ||
234 | owl:onClass ?b . | ||
235 | ?a rdfs:subClassOf ?b . | ||
236 | } . | ||
237 | } | ||
238 | """).get | ||
239 | // NOTE: there seems to be a bug that turns any [[Resource]] answer | ||
240 | // to a query into a [[Literal]] (while here we would expect them to | ||
241 | // be [[IRI]]). | ||
242 | answers2.foldLeft(delete)((d, res) => { | ||
243 | val a = res._2(0).asInstanceOf[Literal].getLexicalForm | ||
244 | val r = res._2(1).asInstanceOf[Literal].getLexicalForm | ||
245 | val b = res._2(1).asInstanceOf[Literal].getLexicalForm | ||
246 | val axiom = factory.getOWLSubClassOfAxiom( | ||
247 | factory.getOWLClass(a), | ||
248 | factory.getOWLObjectMaxCardinality(1, | ||
249 | factory.getOWLObjectProperty(r), | ||
250 | factory.getOWLClass(b) | ||
251 | ) | ||
252 | ) | ||
253 | d += axiom | ||
254 | }) | ||
255 | /* Equality safety: condition 2 */ | ||
256 | val answers3 = RDFoxUtil.submitQuery(data, s""" | ||
257 | SELECT ?r ?r1 ?s ?s1 | ||
258 | WHERE { | ||
259 | graph ${Ontology.RSACheck} { | ||
260 | ?u ?s ?a ; a ${RSA.U} . | ||
261 | ?a ?r ?u ; a ${RSA.NI} . | ||
262 | } . | ||
263 | graph ${Ontology.RBoxReasoning} { | ||
264 | ?r rdfs:subPropertyOf ?r1 . | ||
265 | ?r1 ${RSA("subPropertyOfTrans")} ?t . | ||
266 | ?t owl:inverseOf ?ti . | ||
267 | ?s rdfs:subPropertyOf ?s1 . | ||
268 | ?s1 ${RSA("subPropertyOfTrans")} ?ti . | ||
269 | } | ||
270 | } | ||
271 | """).get | ||
272 | // NOTE: there seems to be a bug that turns any [[Resource]] answer | ||
273 | // to a query into a [[Literal]] (while here we would expect them to | ||
274 | // be [[IRI]]). | ||
275 | println(s"Answers 3: ${answers3.length}") | ||
276 | answers3.foldLeft(delete)((d, res) => { | ||
277 | val r1 = res._2(0).asInstanceOf[Literal].getLexicalForm | ||
278 | val r2 = res._2(1).asInstanceOf[Literal].getLexicalForm | ||
279 | val s1 = res._2(2).asInstanceOf[Literal].getLexicalForm | ||
280 | val s2 = res._2(3).asInstanceOf[Literal].getLexicalForm | ||
281 | val axiom = if (r1 == r2) { | ||
282 | factory.getOWLSubObjectPropertyOfAxiom( | ||
283 | factory.getOWLObjectProperty(s1), | ||
284 | factory.getOWLObjectProperty(s2), | ||
285 | ) | ||
286 | } else { | ||
287 | factory.getOWLSubObjectPropertyOfAxiom( | ||
288 | factory.getOWLObjectProperty(r1), | ||
289 | factory.getOWLObjectProperty(r2), | ||
290 | ) | ||
291 | } | ||
292 | d += axiom | ||
293 | }) | ||
294 | |||
295 | println(s"To Delete (${delete.size}):") | ||
296 | delete foreach println | ||
297 | |||
298 | println(s"Before: ${ontology.axioms.length}") | ||
299 | println(s"After: ${(ontology.axioms diff delete.toList).length}") | ||
216 | 300 | ||
217 | /* Remove axioms from approximated ontology */ | 301 | /* Remove axioms from approximated ontology */ |
218 | RSAOntology( | 302 | RSAOntology( |
219 | ontology.origin, | 303 | ontology.origin, |
220 | ontology.axioms diff toDelete, | 304 | ontology.axioms diff delete.toList, |
221 | ontology.datafiles | 305 | ontology.datafiles |
222 | ) | 306 | ) |
307 | |||
308 | |||
309 | |||
310 | |||
223 | } | 311 | } |
224 | 312 | ||
225 | // val edges1 = Seq('A ~> 'B, 'B ~> 'C, 'C ~> 'D, 'D ~> 'H, 'H ~> | 313 | // val edges1 = Seq('A ~> 'B, 'B ~> 'C, 'C ~> 'D, 'D ~> 'H, 'H ~> |