aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFederico Igne <git@federicoigne.com>2021-07-22 08:29:11 +0100
committerFederico Igne <git@federicoigne.com>2021-07-22 08:29:11 +0100
commit18ddefc7c9e9cfaca027a054495325737ae6b9e6 (patch)
tree5c06d23c6462d90d3755fb544d36a88c76302a1d
parent53646646f924887768688794aee46874ed194673 (diff)
downloadRSAComb-18ddefc7c9e9cfaca027a054495325737ae6b9e6.tar.gz
RSAComb-18ddefc7c9e9cfaca027a054495325737ae6b9e6.zip
Move some generic commands from RSAOntology to Ontology
-rw-r--r--src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala445
-rw-r--r--src/main/scala/uk/ac/ox/cs/rsacomb/approximation/approximation.scala4
-rw-r--r--src/main/scala/uk/ac/ox/cs/rsacomb/ontologies/Ontology.scala58
3 files changed, 248 insertions, 259 deletions
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 9902fcd..630d2a0 100644
--- a/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala
+++ b/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala
@@ -63,6 +63,7 @@ import uk.ac.ox.cs.rsacomb.suffix._
63import uk.ac.ox.cs.rsacomb.sparql._ 63import uk.ac.ox.cs.rsacomb.sparql._
64import uk.ac.ox.cs.rsacomb.util.{RDFoxUtil, RSA} 64import uk.ac.ox.cs.rsacomb.util.{RDFoxUtil, RSA}
65import uk.ac.ox.cs.rsacomb.util.Logger 65import uk.ac.ox.cs.rsacomb.util.Logger
66import uk.ac.ox.cs.rsacomb.ontology.Ontology
66 67
67object RSAUtil { 68object RSAUtil {
68 69
@@ -94,82 +95,94 @@ object RSAOntology {
94 95
95 /** Manager instance to interface with OWLAPI */ 96 /** Manager instance to interface with OWLAPI */
96 val manager = OWLManager.createOWLOntologyManager() 97 val manager = OWLManager.createOWLOntologyManager()
97 val factory = manager.getOWLDataFactory()
98 98
99 /** Name of the RDFox data store used for CQ answering */ 99 /** Name of the RDFox data store used for CQ answering */
100 private val DataStore = "answer_computation" 100 private val DataStore = "answer_computation"
101 101
102 /** Filtering program for a given query
103 *
104 * @param query the query to derive the filtering program
105 * @return the filtering program for the given query
106 */
107 def filteringProgram(query: ConjunctiveQuery): FilteringProgram =
108 Logger.timed(
109 FilteringProgram(FilterType.REVISED)(query),
110 "Generating filtering program",
111 Logger.DEBUG
112 )
113
102 def apply( 114 def apply(
103 axioms: List[OWLLogicalAxiom], 115 axioms: List[OWLLogicalAxiom],
104 datafiles: List[File] 116 datafiles: List[File]
105 ): RSAOntology = new RSAOntology(axioms, datafiles: _*) 117 ): RSAOntology = new RSAOntology(axioms, datafiles)
106 118
107 def apply( 119 // def apply(
108 ontofile: File, 120 // ontofile: File,
109 datafiles: List[File], 121 // datafiles: List[File],
110 approx: Option[Approximation] 122 // approx: Option[Approximation]
111 ): RSAOntology = { 123 // ): RSAOntology = {
112 val ontology = manager.loadOntologyFromOntologyDocument(ontofile) 124 // val ontology = manager.loadOntologyFromOntologyDocument(ontofile)
113 RSAOntology(ontology, datafiles, approx) 125 // RSAOntology(ontology, datafiles, approx)
114 } 126 // }
115 127
116 def apply( 128 // def apply(
117 ontology: OWLOntology, 129 // ontology: OWLOntology,
118 datafiles: List[File], 130 // datafiles: List[File],
119 approx: Option[Approximation] 131 // approx: Option[Approximation]
120 ): RSAOntology = { 132 // ): RSAOntology = {
121 val normalizer = new Normalizer() 133 // val normalizer = new Normalizer()
122 134
123 /** TBox axioms */ 135 // /** TBox axioms */
124 var tbox: List[OWLLogicalAxiom] = 136 // var tbox: List[OWLLogicalAxiom] =
125 ontology 137 // ontology
126 .tboxAxioms(Imports.INCLUDED) 138 // .tboxAxioms(Imports.INCLUDED)
127 .collect(Collectors.toList()) 139 // .collect(Collectors.toList())
128 .collect { case a: OWLLogicalAxiom => a } 140 // .collect { case a: OWLLogicalAxiom => a }
129 .flatMap(normalizer.normalize) 141 // .flatMap(normalizer.normalize)
130 142
131 /** RBox axioms */ 143 // /** RBox axioms */
132 var rbox: List[OWLLogicalAxiom] = 144 // var rbox: List[OWLLogicalAxiom] =
133 ontology 145 // ontology
134 .rboxAxioms(Imports.INCLUDED) 146 // .rboxAxioms(Imports.INCLUDED)
135 .collect(Collectors.toList()) 147 // .collect(Collectors.toList())
136 .collect { case a: OWLLogicalAxiom => a } 148 // .collect { case a: OWLLogicalAxiom => a }
137 .flatMap(normalizer.normalize) 149 // .flatMap(normalizer.normalize)
138 150
139 /** ABox axioms 151 // /** ABox axioms
140 * 152 // *
141 * @note this represents only the set of assertions contained in the 153 // * @note this represents only the set of assertions contained in the
142 * ontology file. Data files specified in `datafiles` are directly 154 // * ontology file. Data files specified in `datafiles` are directly
143 * imported in RDFox due to performance issues when trying to import 155 // * imported in RDFox due to performance issues when trying to import
144 * large data files via OWLAPI. 156 // * large data files via OWLAPI.
145 */ 157 // */
146 var abox: List[OWLLogicalAxiom] = 158 // var abox: List[OWLLogicalAxiom] =
147 ontology 159 // ontology
148 .aboxAxioms(Imports.INCLUDED) 160 // .aboxAxioms(Imports.INCLUDED)
149 .collect(Collectors.toList()) 161 // .collect(Collectors.toList())
150 .collect { case a: OWLLogicalAxiom => a } 162 // .collect { case a: OWLLogicalAxiom => a }
151 .flatMap(normalizer.normalize) 163 // .flatMap(normalizer.normalize)
152 164
153 /** Collection of logical axioms in the input ontology */ 165 // /** Collection of logical axioms in the input ontology */
154 var axioms: List[OWLLogicalAxiom] = abox ::: tbox ::: rbox 166 // var axioms: List[OWLLogicalAxiom] = abox ::: tbox ::: rbox
155 167
156 new RSAOntology( 168 // new RSAOntology(
157 approx match { 169 // approx match {
158 case Some(a) => a.approximate(axioms, datafiles) 170 // case Some(a) => a.approximate(axioms, datafiles)
159 case None => axioms 171 // case None => axioms
160 }, 172 // },
161 datafiles: _* 173 // datafiles: _*
162 ) 174 // )
163 } 175 // }
164 176
165} 177}
166 178
167/** Wrapper class for an ontology in RSA 179/** A wrapper for an RSA ontology
168 * 180 *
169 * @param ontology the input OWL2 ontology. 181 * @param ontology the input OWL2 ontology.
170 * @param datafiles additinal data (treated as part of the ABox) 182 * @param datafiles additinal data (treated as part of the ABox)
171 */ 183 */
172class RSAOntology(val axioms: List[OWLLogicalAxiom], val datafiles: File*) { 184class RSAOntology(axioms: List[OWLLogicalAxiom], datafiles: List[File])
185 extends Ontology(axioms, datafiles) {
173 186
174 /** Simplify conversion between OWLAPI and RDFox concepts */ 187 /** Simplify conversion between OWLAPI and RDFox concepts */
175 import implicits.RDFox._ 188 import implicits.RDFox._
@@ -179,34 +192,26 @@ class RSAOntology(val axioms: List[OWLLogicalAxiom], val datafiles: File*) {
179 import uk.ac.ox.cs.rsacomb.implicits.JavaCollections._ 192 import uk.ac.ox.cs.rsacomb.implicits.JavaCollections._
180 193
181 /** Set of axioms removed during the approximation to RSA */ 194 /** Set of axioms removed during the approximation to RSA */
182 private var removed: Seq[OWLAxiom] = Seq.empty 195 //private var removed: Seq[OWLAxiom] = Seq.empty
183
184 /** Normalized Horn-ALCHOIQ ontology */
185 val ontology =
186 RSAOntology.manager.createOntology((axioms: List[OWLAxiom]).asJava)
187
188 /** OWLAPI internal reasoner instantiated over the approximated ontology */
189 private val reasoner =
190 (new StructuralReasonerFactory()).createReasoner(ontology)
191 196
192 /** Retrieve individuals/literals in the ontology */ 197 /** Retrieve individuals/literals in the ontology */
193 val individuals: List[IRI] = 198 private val individuals: List[IRI] =
194 ontology 199 ontology
195 .getIndividualsInSignature() 200 .getIndividualsInSignature()
196 .asScala 201 .asScala
197 .map(_.getIRI) 202 .map(_.getIRI)
198 .map(implicits.RDFox.owlapiToRdfoxIri) 203 .map(implicits.RDFox.owlapiToRdfoxIri)
199 .toList 204 .toList
200 val literals: List[Literal] = 205 private val literals: List[Literal] =
201 axioms 206 axioms
202 .collect { case a: OWLDataPropertyAssertionAxiom => a } 207 .collect { case a: OWLDataPropertyAssertionAxiom => a }
203 .map(_.getObject) 208 .map(_.getObject)
204 .map(implicits.RDFox.owlapiToRdfoxLiteral) 209 .map(implicits.RDFox.owlapiToRdfoxLiteral)
205 210
206 /** Retrieve concepts/roles in the ontology */ 211 /** Retrieve concepts/roles in the ontology */
207 val concepts: List[OWLClass] = 212 private val concepts: List[OWLClass] =
208 ontology.getClassesInSignature().asScala.toList 213 ontology.getClassesInSignature().asScala.toList
209 val roles: List[OWLObjectPropertyExpression] = 214 private val roles: List[OWLObjectPropertyExpression] =
210 axioms 215 axioms
211 .flatMap(_.objectPropertyExpressionsInSignature) 216 .flatMap(_.objectPropertyExpressionsInSignature)
212 .distinct 217 .distinct
@@ -223,36 +228,36 @@ class RSAOntology(val axioms: List[OWLLogicalAxiom], val datafiles: File*) {
223 * if there exists a role p2 appearing in an axiom of type T4 and 228 * if there exists a role p2 appearing in an axiom of type T4 and
224 * p1 is a subproperty of either p2 or the inverse of p2. 229 * p1 is a subproperty of either p2 or the inverse of p2.
225 */ 230 */
226 val unsafeRoles: List[OWLObjectPropertyExpression] = { 231 // val unsafeRoles: List[OWLObjectPropertyExpression] = {
227 232
228 /* Checking for unsafety condition (1) */ 233 // /* Checking for unsafety condition (1) */
229 val unsafe1 = for { 234 // val unsafe1 = for {
230 axiom <- axioms 235 // axiom <- axioms
231 if axiom.isT5 236 // if axiom.isT5
232 role1 <- axiom.objectPropertyExpressionsInSignature 237 // role1 <- axiom.objectPropertyExpressionsInSignature
233 roleSuper = role1 +: reasoner.superObjectProperties(role1) 238 // roleSuper = role1 +: reasoner.superObjectProperties(role1)
234 roleSuperInv = roleSuper.map(_.getInverseProperty) 239 // roleSuperInv = roleSuper.map(_.getInverseProperty)
235 axiom <- axioms 240 // axiom <- axioms
236 if axiom.isT3 && !axiom.isT3top 241 // if axiom.isT3 && !axiom.isT3top
237 role2 <- axiom.objectPropertyExpressionsInSignature 242 // role2 <- axiom.objectPropertyExpressionsInSignature
238 if roleSuperInv contains role2 243 // if roleSuperInv contains role2
239 } yield role1 244 // } yield role1
240 245
241 /* Checking for unsafety condition (2) */ 246 // /* Checking for unsafety condition (2) */
242 val unsafe2 = for { 247 // val unsafe2 = for {
243 axiom <- axioms 248 // axiom <- axioms
244 if axiom.isT5 249 // if axiom.isT5
245 role1 <- axiom.objectPropertyExpressionsInSignature 250 // role1 <- axiom.objectPropertyExpressionsInSignature
246 roleSuper = role1 +: reasoner.superObjectProperties(role1) 251 // roleSuper = role1 +: reasoner.superObjectProperties(role1)
247 roleSuperInv = roleSuper.map(_.getInverseProperty) 252 // roleSuperInv = roleSuper.map(_.getInverseProperty)
248 axiom <- axioms 253 // axiom <- axioms
249 if axiom.isT4 254 // if axiom.isT4
250 role2 <- axiom.objectPropertyExpressionsInSignature 255 // role2 <- axiom.objectPropertyExpressionsInSignature
251 if roleSuper.contains(role2) || roleSuperInv.contains(role2) 256 // if roleSuper.contains(role2) || roleSuperInv.contains(role2)
252 } yield role1 257 // } yield role1
253 258
254 unsafe1 ++ unsafe2 259 // unsafe1 ++ unsafe2
255 } 260 // }
256 261
257 /** Approximate a Horn-ALCHOIQ ontology to RSA 262 /** Approximate a Horn-ALCHOIQ ontology to RSA
258 * 263 *
@@ -396,31 +401,27 @@ class RSAOntology(val axioms: List[OWLLogicalAxiom], val datafiles: File*) {
396 ) 401 )
397 } 402 }
398 403
404 /** Canonical model of the ontology */
399 lazy val canonicalModel = Logger.timed( 405 lazy val canonicalModel = Logger.timed(
400 new CanonicalModel(this), 406 new CanonicalModel(this),
401 "Generating canonical model program", 407 "Generating canonical model program",
402 Logger.DEBUG 408 Logger.DEBUG
403 ) 409 )
404 410
405 def filteringProgram(query: ConjunctiveQuery): FilteringProgram = 411 /** Computes all roles conflicting with a given role
406 Logger.timed( 412 *
407 FilteringProgram(FilterType.REVISED)(query), 413 * @param role a role (object property expression).
408 "Generating filtering program", 414 * @return a set of roles conflicting with `role`.
409 Logger.DEBUG 415 */
410 )
411
412 def confl( 416 def confl(
413 role: OWLObjectPropertyExpression 417 role: OWLObjectPropertyExpression
414 ): Set[OWLObjectPropertyExpression] = { 418 ): Set[OWLObjectPropertyExpression] = {
415 419 reasoner
416 val invSuperRoles = reasoner
417 .superObjectProperties(role) 420 .superObjectProperties(role)
418 .collect(Collectors.toSet()) 421 .collect(Collectors.toSet())
419 .asScala 422 .asScala
420 .addOne(role) 423 .addOne(role)
421 .map(_.getInverseProperty) 424 .map(_.getInverseProperty)
422
423 invSuperRoles
424 .flatMap(x => 425 .flatMap(x =>
425 reasoner 426 reasoner
426 .subObjectProperties(x) 427 .subObjectProperties(x)
@@ -432,6 +433,77 @@ class RSAOntology(val axioms: List[OWLLogicalAxiom], val datafiles: File*) {
432 .filterNot(_.getInverseProperty.isOWLTopObjectProperty()) 433 .filterNot(_.getInverseProperty.isOWLTopObjectProperty())
433 } 434 }
434 435
436 /** Selfloop detection for a given axiom
437 *
438 * @param axiom an axiom of type [[OWLSubClassOfAxiom]]
439 * @return unfold set for the axiom
440 */
441 def self(axiom: OWLSubClassOfAxiom): Set[Term] = {
442 val role = axiom.objectPropertyExpressionsInSignature(0)
443 if (this.confl(role).contains(role)) {
444 Set(RSA("v0_" ++ axiom.hashed), RSA("v1_" ++ axiom.hashed))
445 } else {
446 Set()
447 }
448 }
449
450 /** Cycle detection for a give axiom
451 *
452 * @param axiom an axiom of type [[OWLSubClassOfAxiom]]
453 * @return unfold set for the axiom
454 *
455 * @todo we can actually use `toTriple` from `RSAAxiom` to get the
456 * classes and the role for a given axiom
457 */
458 def cycle(axiom: OWLSubClassOfAxiom): Set[Term] = {
459 val classes =
460 axiom.classesInSignature.collect(Collectors.toList()).asScala
461 val classA = classes(0)
462 val roleR = axiom
463 .objectPropertyExpressionsInSignature(0)
464 .asInstanceOf[OWLObjectProperty]
465 val classB = classes(1)
466 cycle_aux(classA, roleR, classB)
467 }
468
469 /** Auxiliary function for [[RSAOntology.cycle]] */
470 private def cycle_aux(
471 classA: OWLClass,
472 roleR: OWLObjectProperty,
473 classB: OWLClass
474 ): Set[Term] = {
475 val conflR = this.confl(roleR)
476 // TODO: technically we just need the TBox here
477 val terms = for {
478 axiom1 <- axioms
479 if axiom1.isT5
480 // We expect only one role coming out of a T5 axiom
481 roleS <- axiom1.objectPropertyExpressionsInSignature
482 // Triples ordering is among triples involving safe roles.
483 if !unsafe.contains(roleS)
484 if conflR.contains(roleS)
485 tripleARB = RSAAxiom.hashed(classA, roleR, classB)
486 tripleDSC = axiom1.hashed
487 individual =
488 if (tripleARB > tripleDSC) {
489 RSA("v1_" ++ tripleDSC)
490 } else {
491 // Note that this is also the case for
492 // `tripleARB == tripleDSC`
493 RSA("v0_" ++ tripleDSC)
494 }
495 } yield individual
496 terms to Set
497 }
498
499 /** Returns unfold set for self-loop and cycle for the input axiom
500 *
501 * @param axiom an axiom of type [[OWLSubClassOfAxiom]]
502 * @return unfold set for the axiom
503 */
504 def unfold(axiom: OWLSubClassOfAxiom): Set[Term] =
505 this.self(axiom) | this.cycle(axiom)
506
435 /** Returns the answers to a query 507 /** Returns the answers to a query
436 * 508 *
437 * @param query query to execute 509 * @param query query to execute
@@ -439,10 +511,9 @@ class RSAOntology(val axioms: List[OWLLogicalAxiom], val datafiles: File*) {
439 */ 511 */
440 def ask(query: ConjunctiveQuery): ConjunctiveQueryAnswers = Logger.timed( 512 def ask(query: ConjunctiveQuery): ConjunctiveQueryAnswers = Logger.timed(
441 { 513 {
442 import implicits.JavaCollections._
443 val (server, data) = RDFoxUtil.openConnection(RSAOntology.DataStore) 514 val (server, data) = RDFoxUtil.openConnection(RSAOntology.DataStore)
444 val canon = this.canonicalModel 515 val canon = this.canonicalModel
445 val filter = this.filteringProgram(query) 516 val filter = RSAOntology.filteringProgram(query)
446 517
447 /* Upload data from data file */ 518 /* Upload data from data file */
448 RDFoxUtil.addData(data, datafiles: _*) 519 RDFoxUtil.addData(data, datafiles: _*)
@@ -460,12 +531,15 @@ class RSAOntology(val axioms: List[OWLLogicalAxiom], val datafiles: File*) {
460 new java.util.HashMap[String, String] 531 new java.util.HashMap[String, String]
461 ) 532 )
462 533
534 /* Add canonical model */
463 Logger print s"Canonical model rules: ${canon.rules.length}" 535 Logger print s"Canonical model rules: ${canon.rules.length}"
464 RDFoxUtil.addRules(data, canon.rules) 536 RDFoxUtil.addRules(data, canon.rules)
465 537
466 Logger print s"Canonical model facts: ${canon.facts.length}" 538 Logger print s"Canonical model facts: ${canon.facts.length}"
467 RDFoxUtil.addFacts(data, canon.facts) 539 RDFoxUtil.addFacts(data, canon.facts)
468 540
541 RDFoxUtil printStatisticsFor data
542
469 //{ 543 //{
470 // import java.io.{PrintStream, FileOutputStream, File} 544 // import java.io.{PrintStream, FileOutputStream, File}
471 // val rules1 = new FileOutputStream(new File("rules1-lubm200.dlog")) 545 // val rules1 = new FileOutputStream(new File("rules1-lubm200.dlog"))
@@ -475,16 +549,13 @@ class RSAOntology(val axioms: List[OWLLogicalAxiom], val datafiles: File*) {
475 // rules2.print(filter.rules.mkString("\n")) 549 // rules2.print(filter.rules.mkString("\n"))
476 //} 550 //}
477 551
478 //canon.facts.foreach(println) 552 /* Add filtering program */
479 //filter.rules.foreach(println)
480
481 RDFoxUtil printStatisticsFor data
482
483 Logger print s"Filtering program rules: ${filter.rules.length}" 553 Logger print s"Filtering program rules: ${filter.rules.length}"
484 RDFoxUtil.addRules(data, filter.rules) 554 RDFoxUtil.addRules(data, filter.rules)
485 555
486 RDFoxUtil printStatisticsFor data 556 RDFoxUtil printStatisticsFor data
487 557
558 /* Gather answers to the query */
488 val answers = { 559 val answers = {
489 val ans = filter.answerQuery 560 val ans = filter.answerQuery
490 RDFoxUtil 561 RDFoxUtil
@@ -492,7 +563,9 @@ class RSAOntology(val axioms: List[OWLLogicalAxiom], val datafiles: File*) {
492 .map(new ConjunctiveQueryAnswers(query.bcq, query.variables, _)) 563 .map(new ConjunctiveQueryAnswers(query.bcq, query.variables, _))
493 .get 564 .get
494 } 565 }
566
495 RDFoxUtil.closeConnection(server, data) 567 RDFoxUtil.closeConnection(server, data)
568
496 answers 569 answers
497 }, 570 },
498 "Answers computation", 571 "Answers computation",
@@ -503,14 +576,15 @@ class RSAOntology(val axioms: List[OWLLogicalAxiom], val datafiles: File*) {
503 * 576 *
504 * @note This method does not add any facts or rules to the data 577 * @note This method does not add any facts or rules to the data
505 * store. It is most useful after the execution of a query using 578 * store. It is most useful after the execution of a query using
506 * [[uk.ac.ox.cs.rsacomb.RSAOntology.ask RSAOntology.ask]]. 579 * [[RSAOntology.ask]].
507 * @note This method has been introduced mostly for debugging purposes.
508 * 580 *
509 * @param query query to be executed against the environment 581 * @param query query to be executed against the environment
510 * @param prefixes additional prefixes for the query. It defaults to 582 * @param prefixes additional prefixes for the query. It defaults to
511 * an empty set. 583 * an empty set.
512 * @param opts additional options to RDFox. 584 * @param opts additional options to RDFox.
513 * @return a collection of answers to the input query. 585 * @return a collection of answers to the input query.
586 *
587 * @note This method has been introduced mostly for debugging purposes.
514 */ 588 */
515 def queryDataStore( 589 def queryDataStore(
516 query: String, 590 query: String,
@@ -532,122 +606,11 @@ class RSAOntology(val axioms: List[OWLLogicalAxiom], val datafiles: File*) {
532 * [[uk.ac.ox.cs.rsacomb.RSAOntology.ask RSAOntology.ask]] 606 * [[uk.ac.ox.cs.rsacomb.RSAOntology.ask RSAOntology.ask]]
533 * for the corresponding query has been called. 607 * for the corresponding query has been called.
534 */ 608 */
535 def askUnfiltered( 609 // def askUnfiltered(
536 cq: ConjunctiveQuery 610 // cq: ConjunctiveQuery
537 ): Option[Seq[(Long, Seq[Resource])]] = { 611 // ): Option[Seq[(Long, Seq[Resource])]] = {
538 val query = RDFoxUtil.buildDescriptionQuery("QM", cq.variables.length) 612 // val query = RDFoxUtil.buildDescriptionQuery("QM", cq.variables.length)
539 queryDataStore(query, RSA.Prefixes) 613 // queryDataStore(query, RSA.Prefixes)
540 }
541
542 def self(axiom: OWLSubClassOfAxiom): Set[Term] = {
543 // Assuming just one role in the signature of a T5 axiom
544 val role = axiom.objectPropertyExpressionsInSignature(0)
545 if (this.confl(role).contains(role)) {
546 Set(
547 RSA("v0_" ++ axiom.hashed),
548 RSA("v1_" ++ axiom.hashed)
549 )
550 } else {
551 Set()
552 }
553 }
554
555 def cycle(axiom: OWLSubClassOfAxiom): Set[Term] = {
556 // TODO: we can actually use `toTriple` from `RSAAxiom`
557 val classes =
558 axiom.classesInSignature.collect(Collectors.toList()).asScala
559 val classA = classes(0)
560 val roleR = axiom
561 .objectPropertyExpressionsInSignature(0)
562 .asInstanceOf[OWLObjectProperty]
563 val classB = classes(1)
564 cycle_aux1(classA, roleR, classB)
565 }
566
567 def cycle_aux0(
568 classA: OWLClass,
569 roleR: OWLObjectProperty,
570 classB: OWLClass
571 ): Set[Term] = {
572 val conflR = this.confl(roleR)
573 val classes = ontology
574 .classesInSignature(Imports.INCLUDED)
575 .collect(Collectors.toSet())
576 .asScala
577 for {
578 classD <- classes
579 roleS <- conflR
580 classC <- classes
581 // Keeping this check for now
582 if !unsafeRoles.contains(roleS)
583 tripleARB = RSAAxiom.hashed(classA, roleR, classB)
584 tripleDSC = RSAAxiom.hashed(classD, roleS, classC)
585 individual =
586 if (tripleARB > tripleDSC) {
587 RSA("v1_" ++ tripleDSC)
588 } else {
589 // Note that this is also the case for
590 // `tripleARB == tripleDSC`
591 RSA("v0_" ++ tripleDSC)
592 }
593 } yield individual
594 }
595
596 def cycle_aux1(
597 classA: OWLClass,
598 roleR: OWLObjectProperty,
599 classB: OWLClass
600 ): Set[Term] = {
601 val conflR = this.confl(roleR)
602 // We just need the TBox to find
603 val terms = for {
604 axiom1 <- axioms
605 if axiom1.isT5
606 // We expect only one role coming out of a T5 axiom
607 roleS <- axiom1.objectPropertyExpressionsInSignature
608 // Triples ordering is among triples involving safe roles.
609 if !unsafeRoles.contains(roleS)
610 if conflR.contains(roleS)
611 tripleARB = RSAAxiom.hashed(classA, roleR, classB)
612 tripleDSC = axiom1.hashed
613 individual =
614 if (tripleARB > tripleDSC) {
615 RSA("v1_" ++ tripleDSC)
616 } else {
617 // Note that this is also the case for
618 // `tripleARB == tripleDSC`
619 RSA("v0_" ++ tripleDSC)
620 }
621 } yield individual
622 terms to Set
623 }
624
625 def unfold(axiom: OWLSubClassOfAxiom): Set[Term] =
626 this.self(axiom) | this.cycle(axiom)
627
628 /** Log normalization/approximation statistics */
629 // def statistics(level: Logger.Level = Logger.DEBUG): Unit = {
630 // Logger.print(
631 // s"Logical axioms in original input ontology: ${original.getLogicalAxiomCount(true)}",
632 // level
633 // )
634 // Logger.print(
635 // s"Logical axioms discarded in Horn-ALCHOIQ approximation: ${normalizer.discarded}",
636 // level
637 // )
638 // Logger.print(
639 // s"Logical axioms shifted in Horn-ALCHOIQ approximation: ${normalizer.shifted}",
640 // level
641 // )
642 // Logger.print(
643 // s"Logical axioms in Horn-ALCHOIQ ontology: ${ontology
644 // .getLogicalAxiomCount(true)} (${axioms.length}/${axioms.length}/${axioms.length})",
645 // level
646 // )
647 // Logger.print(
648 // s"Logical axioms discarded in RSA approximation: ${removed.length}",
649 // level
650 // )
651 // } 614 // }
652 615
653} // class RSAOntology 616}
diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/approximation/approximation.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/approximation/approximation.scala
index 1b49413..db2118f 100644
--- a/src/main/scala/uk/ac/ox/cs/rsacomb/approximation/approximation.scala
+++ b/src/main/scala/uk/ac/ox/cs/rsacomb/approximation/approximation.scala
@@ -8,8 +8,8 @@ trait Approximation {
8 8
9 /** Approximate an ontology. 9 /** Approximate an ontology.
10 * 10 *
11 * @param ontology input ontology 11 * @param ontology input ontology as a list of axioms
12 * @return a new approximated ontology 12 * @return the approximated ontology
13 */ 13 */
14 def approximate( 14 def approximate(
15 ontology: List[OWLLogicalAxiom], 15 ontology: List[OWLLogicalAxiom],
diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/ontologies/Ontology.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/ontologies/Ontology.scala
index 7856b3a..4a23ec7 100644
--- a/src/main/scala/uk/ac/ox/cs/rsacomb/ontologies/Ontology.scala
+++ b/src/main/scala/uk/ac/ox/cs/rsacomb/ontologies/Ontology.scala
@@ -27,7 +27,15 @@ import org.semanticweb.owlapi.apibinding.OWLManager
27import org.semanticweb.owlapi.model.{OWLOntology, OWLAxiom, OWLLogicalAxiom} 27import org.semanticweb.owlapi.model.{OWLOntology, OWLAxiom, OWLLogicalAxiom}
28import org.semanticweb.owlapi.model.{OWLObjectPropertyExpression} 28import org.semanticweb.owlapi.model.{OWLObjectPropertyExpression}
29import org.semanticweb.owlapi.reasoner.structural.StructuralReasonerFactory 29import org.semanticweb.owlapi.reasoner.structural.StructuralReasonerFactory
30import tech.oxfordsemantic.jrdfox.logic.expression.Resource 30import tech.oxfordsemantic.jrdfox.logic.datalog.Rule
31import tech.oxfordsemantic.jrdfox.logic.expression.{Resource, Term, Variable}
32
33import uk.ac.ox.cs.rsacomb.approximation.Approximation
34import uk.ac.ox.cs.rsacomb.converter._
35import uk.ac.ox.cs.rsacomb.suffix._
36import uk.ac.ox.cs.rsacomb.util.{RDFoxUtil, RSA}
37
38import uk.ac.ox.cs.rsacomb.RSAUtil
31 39
32object Ontology { 40object Ontology {
33 41
@@ -44,7 +52,6 @@ object Ontology {
44 * TODO: turn this into an implicit class parameter. 52 * TODO: turn this into an implicit class parameter.
45 */ 53 */
46 val manager = OWLManager.createOWLOntologyManager() 54 val manager = OWLManager.createOWLOntologyManager()
47 //val factory = manager.getOWLDataFactory()
48 55
49 /** Compute the RSA dependency graph for a set of axioms 56 /** Compute the RSA dependency graph for a set of axioms
50 * 57 *
@@ -66,22 +73,17 @@ object Ontology {
66 unsafe: List[OWLObjectPropertyExpression] 73 unsafe: List[OWLObjectPropertyExpression]
67 ): DependencyGraph = { 74 ): DependencyGraph = {
68 75
69 import org.semanticweb.owlapi.model.{
70 OWLClassExpression,
71 OWLObjectSomeValuesFrom,
72 OWLDataSomeValuesFrom
73 }
74 import tech.oxfordsemantic.jrdfox.logic.datalog.Rule
75 import tech.oxfordsemantic.jrdfox.logic.expression.{Term, Variable}
76 import uk.ac.ox.cs.rsacomb.suffix._
77 import uk.ac.ox.cs.rsacomb.converter._
78 import uk.ac.ox.cs.rsacomb.util.{RDFoxUtil, RSA}
79 import uk.ac.ox.cs.rsacomb.RSAUtil
80
81 var nodemap = Map.empty[String, OWLAxiom] 76 var nodemap = Map.empty[String, OWLAxiom]
82 77
78 /* Create custom converter */
83 object RSAConverter extends RDFoxConverter { 79 object RSAConverter extends RDFoxConverter {
84 80
81 import org.semanticweb.owlapi.model.{
82 OWLClassExpression,
83 OWLObjectSomeValuesFrom,
84 OWLDataSomeValuesFrom
85 }
86
85 override def convert( 87 override def convert(
86 expr: OWLClassExpression, 88 expr: OWLClassExpression,
87 term: Term, 89 term: Term,
@@ -156,6 +158,9 @@ object Ontology {
156} 158}
157 159
158/** A wrapper for a generic OWL2 ontology 160/** A wrapper for a generic OWL2 ontology
161 *
162 * @param axioms list of axioms (roughly) corresponding to the TBox.
163 * @param datafiles files containing ABox data.
159 */ 164 */
160class Ontology(val axioms: List[OWLLogicalAxiom], val datafiles: List[File]) { 165class Ontology(val axioms: List[OWLLogicalAxiom], val datafiles: List[File]) {
161 166
@@ -170,11 +175,11 @@ class Ontology(val axioms: List[OWLLogicalAxiom], val datafiles: List[File]) {
170 * This is mainly used to instantiate a new reasoner to be used in 175 * This is mainly used to instantiate a new reasoner to be used in
171 * the computation of unsafe roles. 176 * the computation of unsafe roles.
172 */ 177 */
173 private val ontology: OWLOntology = 178 protected val ontology: OWLOntology =
174 Ontology.manager.createOntology((axioms: List[OWLAxiom]).asJava) 179 Ontology.manager.createOntology((axioms: List[OWLAxiom]).asJava)
175 180
176 /** OWLAPI internal reasoner for ontology */ 181 /** OWLAPI internal reasoner for ontology */
177 private val reasoner = 182 protected val reasoner =
178 (new StructuralReasonerFactory()).createReasoner(ontology) 183 (new StructuralReasonerFactory()).createReasoner(ontology)
179 184
180 /** Unsafe roles in the ontology 185 /** Unsafe roles in the ontology
@@ -219,7 +224,28 @@ class Ontology(val axioms: List[OWLLogicalAxiom], val datafiles: List[File]) {
219 unsafe1 ++ unsafe2 224 unsafe1 ++ unsafe2
220 } 225 }
221 226
227 /** Compute the dependency graph for the ontology */
222 lazy val dependencyGraph: Ontology.DependencyGraph = 228 lazy val dependencyGraph: Ontology.DependencyGraph =
223 Ontology.dependencyGraph(axioms, datafiles, this.unsafe) 229 Ontology.dependencyGraph(axioms, datafiles, this.unsafe)
224 230
231 /** RSA check */
232 lazy val isRSA: Boolean = ???
233
234 /** Normalize the ontology according to the given normalizer
235 *
236 * @param normalizer the normalization technique to be used.
237 * @return a new normalized [[Ontology]].
238 */
239 def normalize(normalizer: Normalizer): Ontology = ???
240
241 /** Approximate the ontology according to the given approximation
242 * technique.
243 *
244 * @param approximation the approximation to be used on the ontology.
245 * @return a new approximated [[Ontology]].
246 */
247 def approximate(approximation: Approximation): Ontology = {
248 val approx = approximation.approximate(axioms, datafiles)
249 new Ontology(approx, datafiles)
250 }
225} 251}