diff options
Diffstat (limited to 'src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala')
-rw-r--r-- | src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala | 291 |
1 files changed, 190 insertions, 101 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 30e1305..1ff466b 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala | |||
@@ -27,6 +27,7 @@ import org.semanticweb.owlapi.model.{OWLOntology, OWLAxiom, OWLLogicalAxiom} | |||
27 | import org.semanticweb.owlapi.model.{ | 27 | import org.semanticweb.owlapi.model.{ |
28 | OWLClass, | 28 | OWLClass, |
29 | OWLClassExpression, | 29 | OWLClassExpression, |
30 | OWLDataProperty, | ||
30 | OWLDataPropertyAssertionAxiom, | 31 | OWLDataPropertyAssertionAxiom, |
31 | OWLObjectProperty, | 32 | OWLObjectProperty, |
32 | OWLSubObjectPropertyOfAxiom, | 33 | OWLSubObjectPropertyOfAxiom, |
@@ -46,17 +47,20 @@ import tech.oxfordsemantic.jrdfox.client.{ | |||
46 | } | 47 | } |
47 | import tech.oxfordsemantic.jrdfox.Prefixes | 48 | import tech.oxfordsemantic.jrdfox.Prefixes |
48 | import tech.oxfordsemantic.jrdfox.logic.datalog.{ | 49 | import tech.oxfordsemantic.jrdfox.logic.datalog.{ |
50 | BodyFormula, | ||
51 | FilterAtom, | ||
52 | Negation, | ||
49 | Rule, | 53 | Rule, |
50 | TupleTableAtom, | 54 | TupleTableAtom, |
51 | Negation, | 55 | TupleTableName |
52 | BodyFormula | ||
53 | } | 56 | } |
54 | import tech.oxfordsemantic.jrdfox.logic.expression.{ | 57 | import tech.oxfordsemantic.jrdfox.logic.expression.{ |
55 | Term, | 58 | FunctionCall, |
56 | Variable, | ||
57 | IRI, | 59 | IRI, |
60 | Literal, | ||
58 | Resource, | 61 | Resource, |
59 | Literal | 62 | Term, |
63 | Variable | ||
60 | } | 64 | } |
61 | import tech.oxfordsemantic.jrdfox.logic.sparql.statement.SelectQuery | 65 | import tech.oxfordsemantic.jrdfox.logic.sparql.statement.SelectQuery |
62 | 66 | ||
@@ -81,30 +85,6 @@ import uk.ac.ox.cs.rsacomb.util.{RDFoxUtil, RSA} | |||
81 | import uk.ac.ox.cs.rsacomb.util.Logger | 85 | import uk.ac.ox.cs.rsacomb.util.Logger |
82 | import uk.ac.ox.cs.rsacomb.ontology.Ontology | 86 | import uk.ac.ox.cs.rsacomb.ontology.Ontology |
83 | 87 | ||
84 | object RSAUtil { | ||
85 | |||
86 | // implicit def axiomsToOntology(axioms: Seq[OWLAxiom]) = { | ||
87 | // val manager = OWLManager.createOWLOntologyManager() | ||
88 | // manager.createOntology(axioms.asJava) | ||
89 | // } | ||
90 | |||
91 | /** Manager instance to interface with OWLAPI */ | ||
92 | val manager = OWLManager.createOWLOntologyManager() | ||
93 | val factory = manager.getOWLDataFactory() | ||
94 | |||
95 | /** Simple fresh variable/class generator */ | ||
96 | private var counter = -1; | ||
97 | def genFreshVariable(): Variable = { | ||
98 | counter += 1 | ||
99 | Variable.create(f"I$counter%05d") | ||
100 | } | ||
101 | def getFreshOWLClass(): OWLClass = { | ||
102 | counter += 1 | ||
103 | factory.getOWLClass(s"X$counter") | ||
104 | } | ||
105 | |||
106 | } | ||
107 | |||
108 | object RSAOntology { | 88 | object RSAOntology { |
109 | 89 | ||
110 | import uk.ac.ox.cs.rsacomb.implicits.JavaCollections._ | 90 | import uk.ac.ox.cs.rsacomb.implicits.JavaCollections._ |
@@ -115,6 +95,20 @@ object RSAOntology { | |||
115 | /** Name of the RDFox data store used for CQ answering */ | 95 | /** Name of the RDFox data store used for CQ answering */ |
116 | private val DataStore = "answer_computation" | 96 | private val DataStore = "answer_computation" |
117 | 97 | ||
98 | /** Canonical model named graph */ | ||
99 | private val CanonGraph: IRI = | ||
100 | RDFoxUtil.getNamedGraph(DataStore, "CanonicalModel") | ||
101 | |||
102 | /** Filtering program named graph | ||
103 | * | ||
104 | * @param query query associated with the returned named graph. | ||
105 | * | ||
106 | * @return named graph for the filtering program associated with the | ||
107 | * input query. | ||
108 | */ | ||
109 | private def FilterGraph(query: ConjunctiveQuery): IRI = | ||
110 | RDFoxUtil.getNamedGraph(DataStore, s"Filter${query.id}") | ||
111 | |||
118 | /** Filtering program for a given query | 112 | /** Filtering program for a given query |
119 | * | 113 | * |
120 | * @param query the query to derive the filtering program | 114 | * @param query the query to derive the filtering program |
@@ -122,15 +116,19 @@ object RSAOntology { | |||
122 | */ | 116 | */ |
123 | def filteringProgram(query: ConjunctiveQuery): FilteringProgram = | 117 | def filteringProgram(query: ConjunctiveQuery): FilteringProgram = |
124 | Logger.timed( | 118 | Logger.timed( |
125 | FilteringProgram(FilterType.REVISED)(query), | 119 | { |
120 | val filter = FilteringProgram(FilterType.REVISED) | ||
121 | filter(CanonGraph, FilterGraph(query), query) | ||
122 | }, | ||
126 | "Generating filtering program", | 123 | "Generating filtering program", |
127 | Logger.DEBUG | 124 | Logger.DEBUG |
128 | ) | 125 | ) |
129 | 126 | ||
130 | def apply( | 127 | def apply( |
128 | origin: OWLOntology, | ||
131 | axioms: List[OWLLogicalAxiom], | 129 | axioms: List[OWLLogicalAxiom], |
132 | datafiles: List[File] | 130 | datafiles: List[os.Path] |
133 | ): RSAOntology = new RSAOntology(axioms, datafiles) | 131 | ): RSAOntology = new RSAOntology(origin, axioms, datafiles) |
134 | 132 | ||
135 | // def apply( | 133 | // def apply( |
136 | // ontofile: File, | 134 | // ontofile: File, |
@@ -197,8 +195,11 @@ object RSAOntology { | |||
197 | * @param ontology the input OWL2 ontology. | 195 | * @param ontology the input OWL2 ontology. |
198 | * @param datafiles additinal data (treated as part of the ABox) | 196 | * @param datafiles additinal data (treated as part of the ABox) |
199 | */ | 197 | */ |
200 | class RSAOntology(axioms: List[OWLLogicalAxiom], datafiles: List[File]) | 198 | class RSAOntology( |
201 | extends Ontology(axioms, datafiles) { | 199 | origin: OWLOntology, |
200 | axioms: List[OWLLogicalAxiom], | ||
201 | datafiles: List[os.Path] | ||
202 | ) extends Ontology(origin, axioms, datafiles) { | ||
202 | 203 | ||
203 | /** Simplify conversion between OWLAPI and RDFox concepts */ | 204 | /** Simplify conversion between OWLAPI and RDFox concepts */ |
204 | import implicits.RDFox._ | 205 | import implicits.RDFox._ |
@@ -227,10 +228,9 @@ class RSAOntology(axioms: List[OWLLogicalAxiom], datafiles: List[File]) | |||
227 | /** Retrieve concepts/roles in the ontology */ | 228 | /** Retrieve concepts/roles in the ontology */ |
228 | val concepts: List[OWLClass] = | 229 | val concepts: List[OWLClass] = |
229 | ontology.getClassesInSignature().asScala.toList | 230 | ontology.getClassesInSignature().asScala.toList |
230 | val roles: List[OWLObjectPropertyExpression] = | 231 | val objroles: List[OWLObjectPropertyExpression] = |
231 | axioms | 232 | axioms.flatMap(_.objectPropertyExpressionsInSignature).distinct |
232 | .flatMap(_.objectPropertyExpressionsInSignature) | 233 | val dataroles: List[OWLDataProperty] = origin.getDataPropertiesInSignature |
233 | .distinct | ||
234 | 234 | ||
235 | /** Unsafe roles of a given ontology. | 235 | /** Unsafe roles of a given ontology. |
236 | * | 236 | * |
@@ -364,21 +364,32 @@ class RSAOntology(axioms: List[OWLLogicalAxiom], datafiles: List[File]) | |||
364 | private val topAxioms: List[Rule] = { | 364 | private val topAxioms: List[Rule] = { |
365 | val varX = Variable.create("X") | 365 | val varX = Variable.create("X") |
366 | val varY = Variable.create("Y") | 366 | val varY = Variable.create("Y") |
367 | concepts | 367 | val varZ = Variable.create("Z") |
368 | .map(c => { | 368 | val graph = TupleTableName.create(RSAOntology.CanonGraph.getIRI) |
369 | Rule.create( | 369 | Rule.create( |
370 | RSA.Thing(varX), | 370 | TupleTableAtom.create(graph, varX, IRI.RDF_TYPE, IRI.THING), |
371 | TupleTableAtom.rdf(varX, IRI.RDF_TYPE, c.getIRI) | 371 | TupleTableAtom.create(graph, varX, IRI.RDF_TYPE, varY) |
372 | ) | 372 | ) :: objroles.map(r => { |
373 | }) ++ roles.map(r => { | ||
374 | val name = r match { | 373 | val name = r match { |
375 | case x: OWLObjectProperty => x.getIRI.getIRIString | 374 | case x: OWLObjectProperty => x.getIRI.getIRIString |
376 | case x: OWLObjectInverseOf => | 375 | case x: OWLObjectInverseOf => |
377 | x.getInverse.getNamedProperty.getIRI.getIRIString :: Inverse | 376 | x.getInverse.getNamedProperty.getIRI.getIRIString :: Inverse |
378 | } | 377 | } |
379 | Rule.create( | 378 | Rule.create( |
380 | List(RSA.Thing(varX), RSA.Thing(varY)), | 379 | List( |
381 | List(TupleTableAtom.rdf(varX, name, varY)) | 380 | TupleTableAtom.create(graph, varX, IRI.RDF_TYPE, IRI.THING), |
381 | TupleTableAtom.create(graph, varY, IRI.RDF_TYPE, IRI.THING) | ||
382 | ), | ||
383 | List(TupleTableAtom.create(graph, varX, name, varY)) | ||
384 | ) | ||
385 | }) ::: dataroles.map(r => { | ||
386 | val name = r.getIRI.getIRIString | ||
387 | Rule.create( | ||
388 | List( | ||
389 | TupleTableAtom.create(graph, varX, IRI.RDF_TYPE, IRI.THING), | ||
390 | TupleTableAtom.create(graph, varY, IRI.RDF_TYPE, IRI.THING) | ||
391 | ), | ||
392 | List(TupleTableAtom.create(graph, varX, name, varY)) | ||
382 | ) | 393 | ) |
383 | }) | 394 | }) |
384 | } | 395 | } |
@@ -403,23 +414,31 @@ class RSAOntology(axioms: List[OWLLogicalAxiom], datafiles: List[File]) | |||
403 | val varX = Variable.create("X") | 414 | val varX = Variable.create("X") |
404 | val varY = Variable.create("Y") | 415 | val varY = Variable.create("Y") |
405 | val varZ = Variable.create("Z") | 416 | val varZ = Variable.create("Z") |
406 | List( | 417 | val graph = TupleTableName.create(RSAOntology.CanonGraph.getIRI) |
418 | // Equality properties | ||
419 | val properties = List( | ||
407 | // Reflexivity | 420 | // Reflexivity |
408 | Rule.create(RSA.Congruent(varX, varX), RSA.Thing(varX)), | 421 | Rule.create( |
422 | TupleTableAtom.create(graph, varX, RSA.CONGRUENT, varX), | ||
423 | TupleTableAtom.create(graph, varX, IRI.RDF_TYPE, IRI.THING) | ||
424 | ), | ||
409 | // Simmetry | 425 | // Simmetry |
410 | Rule.create(RSA.Congruent(varY, varX), RSA.Congruent(varX, varY)), | 426 | Rule.create( |
427 | TupleTableAtom.create(graph, varY, RSA.CONGRUENT, varX), | ||
428 | TupleTableAtom.create(graph, varX, RSA.CONGRUENT, varY) | ||
429 | ), | ||
411 | // Transitivity | 430 | // Transitivity |
412 | Rule.create( | 431 | Rule.create( |
413 | RSA.Congruent(varX, varZ), | 432 | TupleTableAtom.create(graph, varX, RSA.CONGRUENT, varZ), |
414 | RSA.Congruent(varX, varY), | 433 | TupleTableAtom.create(graph, varX, RSA.CONGRUENT, varY), |
415 | RSA.Congruent(varY, varZ) | 434 | TupleTableAtom.create(graph, varY, RSA.CONGRUENT, varZ) |
416 | ) | 435 | ) |
417 | ) | 436 | ) |
418 | } | 437 | } |
419 | 438 | ||
420 | /** Canonical model of the ontology */ | 439 | /** Canonical model of the ontology */ |
421 | lazy val canonicalModel = Logger.timed( | 440 | lazy val canonicalModel = Logger.timed( |
422 | new CanonicalModel(this), | 441 | new CanonicalModel(this, RSAOntology.CanonGraph), |
423 | "Generating canonical model program", | 442 | "Generating canonical model program", |
424 | Logger.DEBUG | 443 | Logger.DEBUG |
425 | ) | 444 | ) |
@@ -520,73 +539,143 @@ class RSAOntology(axioms: List[OWLLogicalAxiom], datafiles: List[File]) | |||
520 | def unfold(axiom: OWLSubClassOfAxiom): Set[Term] = | 539 | def unfold(axiom: OWLSubClassOfAxiom): Set[Term] = |
521 | this.self(axiom) | this.cycle(axiom) | 540 | this.self(axiom) | this.cycle(axiom) |
522 | 541 | ||
523 | /** Returns the answers to a query | 542 | /** Returns the answers to a single query |
524 | * | 543 | * |
525 | * @param query query to execute | 544 | * @param queries a sequence of conjunctive queries to answer. |
526 | * @return a collection of answers | 545 | * @return a collection of answers for each query. |
527 | */ | 546 | */ |
528 | def ask(query: ConjunctiveQuery): ConjunctiveQueryAnswers = Logger.timed( | 547 | def ask(query: ConjunctiveQuery): ConjunctiveQueryAnswers = this._ask(query) |
529 | { | ||
530 | val (server, data) = RDFoxUtil.openConnection(RSAOntology.DataStore) | ||
531 | val canon = this.canonicalModel | ||
532 | val filter = RSAOntology.filteringProgram(query) | ||
533 | 548 | ||
534 | /* Upload data from data file */ | 549 | /** Returns the answers to a collection of queries |
535 | RDFoxUtil.addData(data, datafiles: _*) | 550 | * |
536 | 551 | * @param queries a sequence of conjunctive queries to answer. | |
537 | RDFoxUtil printStatisticsFor data | 552 | * @return a collection of answers for each query. |
553 | */ | ||
554 | def ask(queries: Seq[ConjunctiveQuery]): Seq[ConjunctiveQueryAnswers] = | ||
555 | queries map _ask | ||
538 | 556 | ||
539 | /* Top / equality axiomatization */ | 557 | private lazy val _ask: ConjunctiveQuery => ConjunctiveQueryAnswers = { |
540 | RDFoxUtil.addRules(data, topAxioms ++ equalityAxioms) | 558 | val (server, data) = RDFoxUtil.openConnection(RSAOntology.DataStore) |
541 | 559 | ||
542 | /* Generate `named` predicates */ | 560 | /* Upload data from data file */ |
543 | RDFoxUtil.addFacts(data, (individuals ++ literals) map RSA.Named) | 561 | RDFoxUtil.addData(data, RSAOntology.CanonGraph, datafiles: _*) |
544 | data.evaluateUpdate( | 562 | |
545 | RSA.Prefixes, | 563 | /* Top/equality axiomatization */ |
546 | "INSERT { ?X a rsa:Named } WHERE { ?X a owl:Thing }", | 564 | RDFoxUtil.addRules(data, topAxioms ++ equalityAxioms) |
547 | new java.util.HashMap[String, String] | 565 | Logger.write(topAxioms.mkString("\n"), "canonical_model.datalog") |
548 | ) | 566 | Logger.write(equalityAxioms.mkString("\n"), "canonical_model.datalog") |
567 | |||
568 | /* Introduce `rsacomb:Named` concept */ | ||
569 | data.evaluateUpdate( | ||
570 | null, // the base IRI for the query (if null, a default is used) | ||
571 | RSA.Prefixes, | ||
572 | s""" | ||
573 | INSERT { | ||
574 | GRAPH ${RSAOntology.CanonGraph} { ?X a ${RSA.NAMED} } | ||
575 | } WHERE { | ||
576 | GRAPH ${RSAOntology.CanonGraph} { ?X a ${IRI.THING} } | ||
577 | } | ||
578 | """, | ||
579 | new java.util.HashMap[String, String] | ||
580 | ) | ||
549 | 581 | ||
550 | /* Add canonical model */ | 582 | /* Add canonical model */ |
551 | Logger print s"Canonical model rules: ${canon.rules.length}" | 583 | Logger print s"Canonical model facts: ${this.canonicalModel.facts.length}" |
552 | RDFoxUtil.addRules(data, canon.rules) | 584 | RDFoxUtil.addFacts(data, RSAOntology.CanonGraph, this.canonicalModel.facts) |
585 | Logger print s"Canonical model rules: ${this.canonicalModel.rules.length}" | ||
586 | Logger.write(canonicalModel.rules.mkString("\n"), "canonical_model.datalog") | ||
587 | RDFoxUtil.addRules(data, this.canonicalModel.rules) | ||
553 | 588 | ||
554 | Logger print s"Canonical model facts: ${canon.facts.length}" | 589 | RDFoxUtil.closeConnection(server, data) |
555 | RDFoxUtil.addFacts(data, canon.facts) | ||
556 | 590 | ||
557 | RDFoxUtil printStatisticsFor data | 591 | (query => { |
592 | val (server, data) = RDFoxUtil.openConnection(RSAOntology.DataStore) | ||
558 | 593 | ||
559 | //{ | 594 | val filter = RSAOntology.filteringProgram(query) |
560 | // import java.io.{PrintStream, FileOutputStream, File} | ||
561 | // val rules1 = new FileOutputStream(new File("rules1-lubm200.dlog")) | ||
562 | // val facts1 = new FileOutputStream(new File("facts1-lubm200.ttl")) | ||
563 | // RDFoxUtil.export(data, rules1, facts1) | ||
564 | // val rules2 = new PrintStream(new File("rules2-q34.dlog")) | ||
565 | // rules2.print(filter.rules.mkString("\n")) | ||
566 | //} | ||
567 | 595 | ||
568 | /* Add filtering program */ | 596 | /* Add filtering program */ |
569 | Logger print s"Filtering program rules: ${filter.rules.length}" | 597 | Logger print s"Filtering program rules: ${filter.rules.length}" |
598 | Logger.write(filter.rules.mkString("\n"), s"filter${query.id}.datalog") | ||
570 | RDFoxUtil.addRules(data, filter.rules) | 599 | RDFoxUtil.addRules(data, filter.rules) |
571 | 600 | ||
572 | RDFoxUtil printStatisticsFor data | 601 | // TODO: We remove the rules, should we drop the tuple table as well? |
602 | data.clearRulesAxiomsExplicateFacts() | ||
573 | 603 | ||
574 | /* Gather answers to the query */ | 604 | /* Gather answers to the query */ |
575 | val answers = { | 605 | val answers = RDFoxUtil |
576 | val ans = filter.answerQuery | 606 | .submitQuery(data, filter.answerQuery, RSA.Prefixes) |
577 | RDFoxUtil | 607 | .map(new ConjunctiveQueryAnswers(query, query.variables, _)) |
578 | .submitQuery(data, ans, RSA.Prefixes) | 608 | .get |
579 | .map(new ConjunctiveQueryAnswers(query.bcq, query.variables, _)) | ||
580 | .get | ||
581 | } | ||
582 | 609 | ||
583 | RDFoxUtil.closeConnection(server, data) | 610 | RDFoxUtil.closeConnection(server, data) |
584 | 611 | ||
585 | answers | 612 | answers |
586 | }, | 613 | }) |
587 | "Answers computation", | 614 | } |
588 | Logger.DEBUG | 615 | |
589 | ) | 616 | //def ask(query: ConjunctiveQuery): ConjunctiveQueryAnswers = Logger.timed( |
617 | // { | ||
618 | // val (server, data) = RDFoxUtil.openConnection(RSAOntology.DataStore) | ||
619 | // val canon = this.canonicalModel | ||
620 | // val filter = RSAOntology.filteringProgram(query) | ||
621 | |||
622 | // /* Upload data from data file */ | ||
623 | // RDFoxUtil.addData(data, datafiles: _*) | ||
624 | |||
625 | // RDFoxUtil printStatisticsFor data | ||
626 | |||
627 | // /* Top / equality axiomatization */ | ||
628 | // RDFoxUtil.addRules(data, topAxioms ++ equalityAxioms) | ||
629 | |||
630 | // /* Generate `named` predicates */ | ||
631 | // RDFoxUtil.addFacts(data, (individuals ++ literals) map RSA.Named) | ||
632 | // data.evaluateUpdate( | ||
633 | // null, // the base IRI for the query (if null, a default is used) | ||
634 | // RSA.Prefixes, | ||
635 | // "INSERT { ?X a rsa:Named } WHERE { ?X a owl:Thing }", | ||
636 | // new java.util.HashMap[String, String] | ||
637 | // ) | ||
638 | |||
639 | // /* Add canonical model */ | ||
640 | // Logger print s"Canonical model rules: ${canon.rules.length}" | ||
641 | // RDFoxUtil.addRules(data, canon.rules) | ||
642 | |||
643 | // Logger print s"Canonical model facts: ${canon.facts.length}" | ||
644 | // RDFoxUtil.addFacts(data, canon.facts) | ||
645 | |||
646 | // RDFoxUtil printStatisticsFor data | ||
647 | |||
648 | // //{ | ||
649 | // // import java.io.{PrintStream, FileOutputStream, File} | ||
650 | // // val rules1 = new FileOutputStream(new File("rules1-lubm200.dlog")) | ||
651 | // // val facts1 = new FileOutputStream(new File("facts1-lubm200.ttl")) | ||
652 | // // RDFoxUtil.export(data, rules1, facts1) | ||
653 | // // val rules2 = new PrintStream(new File("rules2-q34.dlog")) | ||
654 | // // rules2.print(filter.rules.mkString("\n")) | ||
655 | // //} | ||
656 | |||
657 | // /* Add filtering program */ | ||
658 | // Logger print s"Filtering program rules: ${filter.rules.length}" | ||
659 | // RDFoxUtil.addRules(data, filter.rules) | ||
660 | |||
661 | // RDFoxUtil printStatisticsFor data | ||
662 | |||
663 | // /* Gather answers to the query */ | ||
664 | // val answers = { | ||
665 | // val ans = filter.answerQuery | ||
666 | // RDFoxUtil | ||
667 | // .submitQuery(data, ans, RSA.Prefixes) | ||
668 | // .map(new ConjunctiveQueryAnswers(query, query.variables, _)) | ||
669 | // .get | ||
670 | // } | ||
671 | |||
672 | // RDFoxUtil.closeConnection(server, data) | ||
673 | |||
674 | // answers | ||
675 | // }, | ||
676 | // "Answers computation", | ||
677 | // Logger.DEBUG | ||
678 | //) | ||
590 | 679 | ||
591 | /** Query the RDFox data store used for query answering. | 680 | /** Query the RDFox data store used for query answering. |
592 | * | 681 | * |