diff options
author | Federico Igne <git@federicoigne.com> | 2021-07-27 12:52:48 +0100 |
---|---|---|
committer | Federico Igne <git@federicoigne.com> | 2021-07-27 12:52:48 +0100 |
commit | 30b199205f3a16cc92577393ddf9b5d7b36d9dc3 (patch) | |
tree | 3cb94f148a2bfb206c9481e723141a28cfcc310a | |
parent | d017662e2d65ec72e7decde3b76591c198da9819 (diff) | |
download | RSAComb-30b199205f3a16cc92577393ddf9b5d7b36d9dc3.tar.gz RSAComb-30b199205f3a16cc92577393ddf9b5d7b36d9dc3.zip |
Add skeleton for upperbound computation
-rw-r--r-- | src/main/scala/uk/ac/ox/cs/rsacomb/approximation/Upperbound.scala | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/approximation/Upperbound.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/approximation/Upperbound.scala new file mode 100644 index 0000000..ba26113 --- /dev/null +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/approximation/Upperbound.scala | |||
@@ -0,0 +1,183 @@ | |||
1 | package uk.ac.ox.cs.rsacomb.approximation | ||
2 | |||
3 | // import java.io.File | ||
4 | |||
5 | import org.semanticweb.owlapi.apibinding.OWLManager | ||
6 | import org.semanticweb.owlapi.model.{IRI => _, _} | ||
7 | |||
8 | // import tech.oxfordsemantic.jrdfox.logic.expression.{Resource, IRI} | ||
9 | |||
10 | // import scala.collection.mutable.{Set, Map} | ||
11 | // import scalax.collection.Graph | ||
12 | // import scalax.collection.GraphPredef._, scalax.collection.GraphEdge._ | ||
13 | // import scalax.collection.GraphTraversal._ | ||
14 | |||
15 | import uk.ac.ox.cs.rsacomb.RSAOntology | ||
16 | // import uk.ac.ox.cs.rsacomb.RSAUtil | ||
17 | import uk.ac.ox.cs.rsacomb.ontology.Ontology | ||
18 | |||
19 | object UpperBound { | ||
20 | |||
21 | private val manager = OWLManager.createOWLOntologyManager() | ||
22 | private val factory = manager.getOWLDataFactory() | ||
23 | |||
24 | } | ||
25 | |||
26 | /** Approximation algorithm that mantains completeness for CQ answering. | ||
27 | * | ||
28 | * The input OWL 2 ontology is assumed to be normalized and the output | ||
29 | * ontology is guaranteed to be in RSA. | ||
30 | * | ||
31 | * The algorithm is performed in three steps: | ||
32 | * 1. the ontology is reduced to ALCHOIQ by discarding any axiom | ||
33 | * that is not in the language; | ||
34 | * 2. the ontology is further reduced to Horn-ALCHOIQ by shifting | ||
35 | * axioms with disjunction on the rhs; | ||
36 | * 3. the ontology is approximated to RSA by manipulating its | ||
37 | * dependency graph. | ||
38 | * | ||
39 | * @see [[uk.ac.ox.cs.rsacomb.converter.Normalizer]] | ||
40 | */ | ||
41 | class UpperBound extends Approximation[RSAOntology] { | ||
42 | |||
43 | /** Simplify conversion between Java and Scala collections */ | ||
44 | import uk.ac.ox.cs.rsacomb.implicits.JavaCollections._ | ||
45 | |||
46 | /** Simplify conversion between OWLAPI and RDFox concepts */ | ||
47 | // import uk.ac.ox.cs.rsacomb.implicits.RDFox._ | ||
48 | |||
49 | /** Main entry point for the approximation algorithm */ | ||
50 | def approximate(ontology: Ontology): RSAOntology = | ||
51 | toRSA( | ||
52 | new Ontology( | ||
53 | ontology.axioms filter inALCHOIQ flatMap toConjuncts, | ||
54 | ontology.datafiles | ||
55 | ) | ||
56 | ) | ||
57 | |||
58 | /** Discards all axioms outside ALCHOIQ */ | ||
59 | private def inALCHOIQ(axiom: OWLLogicalAxiom): Boolean = | ||
60 | axiom match { | ||
61 | case a: OWLSubClassOfAxiom => { | ||
62 | val sub = a.getSubClass.getNNF | ||
63 | val sup = a.getSuperClass.getNNF | ||
64 | (sub, sup) match { | ||
65 | case (sub: OWLObjectAllValuesFrom, _) => false | ||
66 | case (sub: OWLDataAllValuesFrom, _) => false | ||
67 | case (_, sup: OWLDataAllValuesFrom) => false | ||
68 | case (sub: OWLObjectMinCardinality, _) if sub.getCardinality >= 2 => | ||
69 | false | ||
70 | case (sub: OWLDataMinCardinality, _) if sub.getCardinality >= 2 => | ||
71 | false | ||
72 | case (_, sup: OWLObjectMinCardinality) if sup.getCardinality >= 2 => | ||
73 | false | ||
74 | case (_, sup: OWLDataMinCardinality) if sup.getCardinality >= 2 => | ||
75 | false | ||
76 | case (sub: OWLObjectMaxCardinality, _) => false | ||
77 | case (sub: OWLDataMaxCardinality, _) => false | ||
78 | case (_, sup: OWLObjectMaxCardinality) if sup.getCardinality >= 2 => | ||
79 | false | ||
80 | case (_, sup: OWLDataMaxCardinality) if sup.getCardinality >= 1 => | ||
81 | false | ||
82 | case (_, sup: OWLObjectOneOf) if sup.getIndividuals.length > 2 => | ||
83 | false | ||
84 | case (sub: OWLObjectHasSelf, _) => false | ||
85 | case (_, sup: OWLObjectHasSelf) => false | ||
86 | case _ => true | ||
87 | } | ||
88 | } | ||
89 | case a: OWLTransitiveObjectPropertyAxiom => false | ||
90 | case a: OWLReflexiveObjectPropertyAxiom => false | ||
91 | case a: OWLSubPropertyChainOfAxiom => false | ||
92 | case a: OWLAsymmetricObjectPropertyAxiom => false | ||
93 | case a => true | ||
94 | } | ||
95 | |||
96 | /** Turn disjuncts into conjuncts | ||
97 | * | ||
98 | * This is a very naive way of getting rid of disjunction preserving | ||
99 | * completeness of CQ answering. | ||
100 | * | ||
101 | * @todo implement a choice function that decides which disjunct to | ||
102 | * keep instead of keeping all of them. Note that PAGOdA is currently | ||
103 | * doing something similar. | ||
104 | */ | ||
105 | private def toConjuncts(axiom: OWLLogicalAxiom): List[OWLLogicalAxiom] = | ||
106 | axiom match { | ||
107 | case a: OWLSubClassOfAxiom => { | ||
108 | val sub = a.getSubClass.getNNF | ||
109 | val sup = a.getSuperClass.getNNF | ||
110 | sup match { | ||
111 | case sup: OWLObjectUnionOf => | ||
112 | sup.asDisjunctSet.map( | ||
113 | UpperBound.factory.getOWLSubClassOfAxiom(sub, _) | ||
114 | ) | ||
115 | case _ => List(axiom) | ||
116 | } | ||
117 | } | ||
118 | case _ => List(axiom) | ||
119 | } | ||
120 | |||
121 | // /** Approximate a Horn-ALCHOIQ ontology to RSA | ||
122 | // * | ||
123 | // * This is done by gathering those axioms that prevent the ontology | ||
124 | // * dependency graph from being tree-shaped, and removing them. | ||
125 | // * | ||
126 | // * @param ontology the set of axioms to approximate. | ||
127 | // * @return the approximated RSA ontology | ||
128 | // */ | ||
129 | // private def toRSA(ontology: Ontology): RSAOntology = { | ||
130 | // /* Compute the dependency graph for the ontology */ | ||
131 | // val (graph, nodemap) = ontology.dependencyGraph | ||
132 | |||
133 | // /* Define node colors for the graph visit */ | ||
134 | // sealed trait NodeColor | ||
135 | // case object Unvisited extends NodeColor | ||
136 | // case object Visited extends NodeColor | ||
137 | // case object ToDelete extends NodeColor | ||
138 | |||
139 | // /* Keep track of node colors during graph visit */ | ||
140 | // var color = Map.from[Resource, NodeColor]( | ||
141 | // graph.nodes.toOuter.map(k => (k, Unvisited)) | ||
142 | // ) | ||
143 | |||
144 | // for { | ||
145 | // component <- graph.componentTraverser().map(_ to Graph) | ||
146 | // edge <- component | ||
147 | // .outerEdgeTraverser(component.nodes.head) | ||
148 | // .withKind(BreadthFirst) | ||
149 | // } yield { | ||
150 | // val source = edge._1 | ||
151 | // val target = edge._2 | ||
152 | // color(source) match { | ||
153 | // case Unvisited | Visited => { | ||
154 | // color(target) match { | ||
155 | // case Unvisited => | ||
156 | // color(source) = Visited; | ||
157 | // color(target) = Visited | ||
158 | // case Visited => | ||
159 | // color(source) = ToDelete | ||
160 | // case ToDelete => | ||
161 | // color(source) = Visited | ||
162 | // } | ||
163 | // } | ||
164 | // case ToDelete => | ||
165 | // } | ||
166 | // } | ||
167 | |||
168 | // val toDelete = color.collect { case (resource: IRI, ToDelete) => | ||
169 | // nodemap(resource.getIRI) | ||
170 | // }.toList | ||
171 | |||
172 | // /* Remove axioms from approximated ontology */ | ||
173 | // RSAOntology(ontology.axioms diff toDelete, ontology.datafiles) | ||
174 | // } | ||
175 | |||
176 | // val edges1 = Seq('A ~> 'B, 'B ~> 'C, 'C ~> 'D, 'D ~> 'H, 'H ~> | ||
177 | // 'G, 'G ~> 'F, 'E ~> 'A, 'E ~> 'F, 'B ~> 'E, 'F ~> 'G, 'B ~> 'F, | ||
178 | // 'C ~> 'G, 'D ~> 'C, 'H ~> 'D) | ||
179 | // val edges2 = Seq('I ~> 'M, 'I ~> 'L, 'L ~> 'N, 'M ~> 'N) | ||
180 | // val edges3 = Seq('P ~> 'O) | ||
181 | // val graph = Graph.from(edges = edges1 ++ edges2 ++ edges3) | ||
182 | // | ||
183 | } | ||