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 ~> |
