diff options
5 files changed, 532 insertions, 241 deletions
diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala index f9de801..b749401 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala | |||
@@ -106,12 +106,11 @@ object RSAComb extends App { | |||
106 | /* Command-line options */ | 106 | /* Command-line options */ |
107 | val config = RSAConfig.parse(args.toList) | 107 | val config = RSAConfig.parse(args.toList) |
108 | 108 | ||
109 | val ontology = RSAOntology( | 109 | val rsa = RSAOntology( |
110 | config('ontology).get[File], | 110 | config('ontology).get[File], |
111 | config('data).get[List[File]]: _* | 111 | config('data).get[List[File]], |
112 | None | ||
112 | ) | 113 | ) |
113 | val rsa = ontology.toRSA() | ||
114 | ontology.statistics() | ||
115 | 114 | ||
116 | if (config contains 'query) { | 115 | if (config contains 'query) { |
117 | val query = | 116 | val query = |
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 0e10f89..bbbbcf3 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala | |||
@@ -50,13 +50,13 @@ import scala.collection.JavaConverters._ | |||
50 | import scala.collection.mutable.{Set, Map} | 50 | import scala.collection.mutable.{Set, Map} |
51 | import scalax.collection.Graph | 51 | import scalax.collection.Graph |
52 | import scalax.collection.GraphPredef._, scalax.collection.GraphEdge._ | 52 | import scalax.collection.GraphPredef._, scalax.collection.GraphEdge._ |
53 | import scalax.collection.GraphTraversal._ | ||
54 | 53 | ||
55 | /* Debug only */ | 54 | /* Debug only */ |
56 | import org.semanticweb.owlapi.dlsyntax.renderer.DLSyntaxObjectRenderer | 55 | import org.semanticweb.owlapi.dlsyntax.renderer.DLSyntaxObjectRenderer |
57 | import tech.oxfordsemantic.jrdfox.logic._ | 56 | import tech.oxfordsemantic.jrdfox.logic._ |
58 | import org.semanticweb.owlapi.model.OWLObjectInverseOf | 57 | import org.semanticweb.owlapi.model.OWLObjectInverseOf |
59 | 58 | ||
59 | import uk.ac.ox.cs.rsacomb.approximation.Approximation | ||
60 | import uk.ac.ox.cs.rsacomb.converter._ | 60 | import uk.ac.ox.cs.rsacomb.converter._ |
61 | import uk.ac.ox.cs.rsacomb.filtering.{FilteringProgram, FilterType} | 61 | import uk.ac.ox.cs.rsacomb.filtering.{FilteringProgram, FilterType} |
62 | import uk.ac.ox.cs.rsacomb.suffix._ | 62 | import uk.ac.ox.cs.rsacomb.suffix._ |
@@ -64,29 +64,189 @@ import uk.ac.ox.cs.rsacomb.sparql._ | |||
64 | import uk.ac.ox.cs.rsacomb.util.{RDFoxUtil, RSA} | 64 | import uk.ac.ox.cs.rsacomb.util.{RDFoxUtil, RSA} |
65 | import uk.ac.ox.cs.rsacomb.util.Logger | 65 | import uk.ac.ox.cs.rsacomb.util.Logger |
66 | 66 | ||
67 | object RSAUtil { | ||
68 | |||
69 | // implicit def axiomsToOntology(axioms: Seq[OWLAxiom]) = { | ||
70 | // val manager = OWLManager.createOWLOntologyManager() | ||
71 | // manager.createOntology(axioms.asJava) | ||
72 | // } | ||
73 | |||
74 | /** Compute the RSA dependency graph for a set of axioms | ||
75 | * | ||
76 | * @return a tuple containing the dependency graph and a map between | ||
77 | * the newly introduced constants and the corresponding input axioms. | ||
78 | * | ||
79 | * @note no check on the ontology language is performed since the | ||
80 | * construction of the dependency graph is computed regardless. The | ||
81 | * input axioms are assumed to be normalized. | ||
82 | */ | ||
83 | def dependencyGraph( | ||
84 | axioms: List[OWLLogicalAxiom], | ||
85 | datafiles: List[File] | ||
86 | ): (Graph[Resource, DiEdge], Map[String, OWLAxiom]) = { | ||
87 | val unsafe = RSAOntology(axioms, datafiles).unsafeRoles | ||
88 | var nodemap = Map.empty[String, OWLAxiom] | ||
89 | |||
90 | object RSAConverter extends RDFoxConverter { | ||
91 | |||
92 | override def convert( | ||
93 | expr: OWLClassExpression, | ||
94 | term: Term, | ||
95 | unsafe: List[OWLObjectPropertyExpression], | ||
96 | skolem: SkolemStrategy, | ||
97 | suffix: RSASuffix | ||
98 | ): Shards = | ||
99 | (expr, skolem) match { | ||
100 | |||
101 | case (e: OWLObjectSomeValuesFrom, c: Constant) => { | ||
102 | nodemap.update(c.iri.getIRI, c.axiom) | ||
103 | val (res, ext) = super.convert(e, term, unsafe, skolem, suffix) | ||
104 | if (unsafe contains e.getProperty) | ||
105 | (RSA.PE(term, c.iri) :: RSA.U(c.iri) :: res, ext) | ||
106 | else | ||
107 | (RSA.PE(term, c.iri) :: res, ext) | ||
108 | } | ||
109 | |||
110 | case (e: OWLDataSomeValuesFrom, c: Constant) => { | ||
111 | nodemap.update(c.iri.getIRI, c.axiom) | ||
112 | val (res, ext) = super.convert(e, term, unsafe, skolem, suffix) | ||
113 | if (unsafe contains e.getProperty) | ||
114 | (RSA.PE(term, c.iri) :: RSA.U(c.iri) :: res, ext) | ||
115 | else | ||
116 | (RSA.PE(term, c.iri) :: res, ext) | ||
117 | } | ||
118 | |||
119 | case _ => super.convert(expr, term, unsafe, skolem, suffix) | ||
120 | } | ||
121 | } | ||
122 | |||
123 | /* Ontology convertion into LP rules */ | ||
124 | val term = RSAOntology.genFreshVariable() | ||
125 | val result = axioms.map(a => | ||
126 | RSAConverter.convert(a, term, unsafe, new Constant(a), Empty) | ||
127 | ) | ||
128 | |||
129 | val datalog = result.unzip | ||
130 | val facts = datalog._1.flatten | ||
131 | var rules = datalog._2.flatten | ||
132 | |||
133 | /* Open connection with RDFox */ | ||
134 | val (server, data) = RDFoxUtil.openConnection("rsa_dependency_graph") | ||
135 | |||
136 | /* Add additional built-in rules */ | ||
137 | val varX = Variable.create("X") | ||
138 | val varY = Variable.create("Y") | ||
139 | rules = Rule.create( | ||
140 | RSA.E(varX, varY), | ||
141 | RSA.PE(varX, varY), | ||
142 | RSA.U(varX), | ||
143 | RSA.U(varY) | ||
144 | ) :: rules | ||
145 | /* Load facts and rules from ontology */ | ||
146 | RDFoxUtil.addFacts(data, facts) | ||
147 | RDFoxUtil.addRules(data, rules) | ||
148 | /* Load data files */ | ||
149 | RDFoxUtil.addData(data, datafiles: _*) | ||
150 | |||
151 | /* Build the graph */ | ||
152 | val query = "SELECT ?X ?Y WHERE { ?X rsa:E ?Y }" | ||
153 | val answers = RDFoxUtil.submitQuery(data, query, RSA.Prefixes).get | ||
154 | var edges: Seq[DiEdge[Resource]] = | ||
155 | answers.collect { case (_, Seq(n1, n2)) => n1 ~> n2 } | ||
156 | val graph = Graph(edges: _*) | ||
157 | |||
158 | /* Close connection to RDFox */ | ||
159 | RDFoxUtil.closeConnection(server, data) | ||
160 | |||
161 | (graph, nodemap) | ||
162 | } | ||
163 | } | ||
164 | |||
67 | object RSAOntology { | 165 | object RSAOntology { |
68 | 166 | ||
167 | import uk.ac.ox.cs.rsacomb.implicits.JavaCollections._ | ||
168 | |||
169 | /** Manager instance to interface with OWLAPI */ | ||
170 | val manager = OWLManager.createOWLOntologyManager() | ||
171 | val factory = manager.getOWLDataFactory() | ||
172 | |||
69 | /** Name of the RDFox data store used for CQ answering */ | 173 | /** Name of the RDFox data store used for CQ answering */ |
70 | private val DataStore = "answer_computation" | 174 | private val DataStore = "answer_computation" |
71 | 175 | ||
72 | /** Simple fresh variable generator */ | 176 | /** Simple fresh variable/class generator */ |
73 | private var counter = -1; | 177 | private var counter = -1; |
74 | def genFreshVariable(): Variable = { | 178 | def genFreshVariable(): Variable = { |
75 | counter += 1 | 179 | counter += 1 |
76 | Variable.create(f"I$counter%05d") | 180 | Variable.create(f"I$counter%05d") |
77 | } | 181 | } |
182 | def getFreshOWLClass(): OWLClass = { | ||
183 | counter += 1 | ||
184 | factory.getOWLClass(s"X$counter") | ||
185 | } | ||
78 | 186 | ||
79 | /** Manager instance to interface with OWLAPI */ | 187 | def apply( |
80 | val manager = OWLManager.createOWLOntologyManager() | 188 | axioms: List[OWLLogicalAxiom], |
189 | datafiles: List[File] | ||
190 | ): RSAOntology = new RSAOntology(axioms, datafiles: _*) | ||
191 | |||
192 | def apply( | ||
193 | ontofile: File, | ||
194 | datafiles: List[File], | ||
195 | approx: Option[Approximation] | ||
196 | ): RSAOntology = { | ||
197 | val ontology = manager.loadOntologyFromOntologyDocument(ontofile) | ||
198 | RSAOntology(ontology, datafiles, approx) | ||
199 | } | ||
200 | |||
201 | def apply( | ||
202 | ontology: OWLOntology, | ||
203 | datafiles: List[File], | ||
204 | approx: Option[Approximation] | ||
205 | ): RSAOntology = { | ||
206 | val normalizer = new Normalizer() | ||
207 | |||
208 | /** TBox axioms */ | ||
209 | var tbox: List[OWLLogicalAxiom] = | ||
210 | ontology | ||
211 | .tboxAxioms(Imports.INCLUDED) | ||
212 | .collect(Collectors.toList()) | ||
213 | .collect { case a: OWLLogicalAxiom => a } | ||
214 | .flatMap(normalizer.normalize) | ||
215 | |||
216 | /** RBox axioms */ | ||
217 | var rbox: List[OWLLogicalAxiom] = | ||
218 | ontology | ||
219 | .rboxAxioms(Imports.INCLUDED) | ||
220 | .collect(Collectors.toList()) | ||
221 | .collect { case a: OWLLogicalAxiom => a } | ||
222 | .flatMap(normalizer.normalize) | ||
223 | |||
224 | /** ABox axioms | ||
225 | * | ||
226 | * @note this represents only the set of assertions contained in the | ||
227 | * ontology file. Data files specified in `datafiles` are directly | ||
228 | * imported in RDFox due to performance issues when trying to import | ||
229 | * large data files via OWLAPI. | ||
230 | */ | ||
231 | var abox: List[OWLLogicalAxiom] = | ||
232 | ontology | ||
233 | .aboxAxioms(Imports.INCLUDED) | ||
234 | .collect(Collectors.toList()) | ||
235 | .collect { case a: OWLLogicalAxiom => a } | ||
236 | .flatMap(normalizer.normalize) | ||
237 | |||
238 | /** Collection of logical axioms in the input ontology */ | ||
239 | var axioms: List[OWLLogicalAxiom] = abox ::: tbox ::: rbox | ||
81 | 240 | ||
82 | def apply(ontology: File, data: File*): RSAOntology = | ||
83 | new RSAOntology( | 241 | new RSAOntology( |
84 | manager.loadOntologyFromOntologyDocument(ontology), | 242 | approx match { |
85 | data: _* | 243 | case Some(a) => a.approximate(axioms, datafiles) |
244 | case None => axioms | ||
245 | }, | ||
246 | datafiles: _* | ||
86 | ) | 247 | ) |
248 | } | ||
87 | 249 | ||
88 | def apply(ontology: OWLOntology, data: File*): RSAOntology = | ||
89 | new RSAOntology(ontology, data: _*) | ||
90 | } | 250 | } |
91 | 251 | ||
92 | /** Wrapper class for an ontology in RSA | 252 | /** Wrapper class for an ontology in RSA |
@@ -94,59 +254,21 @@ object RSAOntology { | |||
94 | * @param ontology the input OWL2 ontology. | 254 | * @param ontology the input OWL2 ontology. |
95 | * @param datafiles additinal data (treated as part of the ABox) | 255 | * @param datafiles additinal data (treated as part of the ABox) |
96 | */ | 256 | */ |
97 | class RSAOntology(val original: OWLOntology, val datafiles: File*) { | 257 | class RSAOntology(val axioms: List[OWLLogicalAxiom], val datafiles: File*) { |
98 | 258 | ||
99 | /** Simplify conversion between OWLAPI and RDFox concepts */ | 259 | /** Simplify conversion between OWLAPI and RDFox concepts */ |
100 | import implicits.RDFox._ | 260 | import implicits.RDFox._ |
101 | import uk.ac.ox.cs.rsacomb.implicits.RSAAxiom._ | 261 | import uk.ac.ox.cs.rsacomb.implicits.RSAAxiom._ |
262 | |||
263 | /** Simplify conversion between Java and Scala collections */ | ||
102 | import uk.ac.ox.cs.rsacomb.implicits.JavaCollections._ | 264 | import uk.ac.ox.cs.rsacomb.implicits.JavaCollections._ |
103 | 265 | ||
104 | /** Set of axioms removed during the approximation to RSA */ | 266 | /** Set of axioms removed during the approximation to RSA */ |
105 | private var removed: Seq[OWLAxiom] = Seq.empty | 267 | private var removed: Seq[OWLAxiom] = Seq.empty |
106 | 268 | ||
107 | /** The normalizer normalizes the ontology and approximate it to | ||
108 | * Horn-ALCHOIQ. A further step is needed to obtain an RSA | ||
109 | * approximation of the input ontology `original`. | ||
110 | */ | ||
111 | private val normalizer = new Normalizer() | ||
112 | |||
113 | /** TBox axioms */ | ||
114 | var tbox: List[OWLLogicalAxiom] = | ||
115 | original | ||
116 | .tboxAxioms(Imports.INCLUDED) | ||
117 | .collect(Collectors.toList()) | ||
118 | .collect { case a: OWLLogicalAxiom => a } | ||
119 | .flatMap(normalizer.normalize) | ||
120 | |||
121 | /** RBox axioms */ | ||
122 | var rbox: List[OWLLogicalAxiom] = | ||
123 | original | ||
124 | .rboxAxioms(Imports.INCLUDED) | ||
125 | .collect(Collectors.toList()) | ||
126 | .collect { case a: OWLLogicalAxiom => a } | ||
127 | .flatMap(normalizer.normalize) | ||
128 | |||
129 | /** ABox axioms | ||
130 | * | ||
131 | * @note this represents only the set of assertions contained in the | ||
132 | * ontology file. Data files specified in `datafiles` are directly | ||
133 | * imported in RDFox due to performance issues when trying to import | ||
134 | * large data files via OWLAPI. | ||
135 | */ | ||
136 | var abox: List[OWLLogicalAxiom] = | ||
137 | original | ||
138 | .aboxAxioms(Imports.INCLUDED) | ||
139 | .collect(Collectors.toList()) | ||
140 | .collect { case a: OWLLogicalAxiom => a } | ||
141 | .flatMap(normalizer.normalize) | ||
142 | |||
143 | /** Collection of logical axioms in the input ontology */ | ||
144 | var axioms: List[OWLLogicalAxiom] = abox ::: tbox ::: rbox | ||
145 | |||
146 | /** Normalized Horn-ALCHOIQ ontology */ | 269 | /** Normalized Horn-ALCHOIQ ontology */ |
147 | val ontology = RSAOntology.manager.createOntology( | 270 | val ontology = |
148 | axioms.asInstanceOf[List[OWLAxiom]].asJava | 271 | RSAOntology.manager.createOntology((axioms: List[OWLAxiom]).asJava) |
149 | ) | ||
150 | 272 | ||
151 | /** OWLAPI internal reasoner instantiated over the approximated ontology */ | 273 | /** OWLAPI internal reasoner instantiated over the approximated ontology */ |
152 | private val reasoner = | 274 | private val reasoner = |
@@ -170,7 +292,7 @@ class RSAOntology(val original: OWLOntology, val datafiles: File*) { | |||
170 | val concepts: List[OWLClass] = | 292 | val concepts: List[OWLClass] = |
171 | ontology.getClassesInSignature().asScala.toList | 293 | ontology.getClassesInSignature().asScala.toList |
172 | val roles: List[OWLObjectPropertyExpression] = | 294 | val roles: List[OWLObjectPropertyExpression] = |
173 | (tbox ++ rbox) | 295 | axioms |
174 | .flatMap(_.objectPropertyExpressionsInSignature) | 296 | .flatMap(_.objectPropertyExpressionsInSignature) |
175 | .distinct | 297 | .distinct |
176 | 298 | ||
@@ -190,12 +312,12 @@ class RSAOntology(val original: OWLOntology, val datafiles: File*) { | |||
190 | 312 | ||
191 | /* Checking for unsafety condition (1) */ | 313 | /* Checking for unsafety condition (1) */ |
192 | val unsafe1 = for { | 314 | val unsafe1 = for { |
193 | axiom <- tbox | 315 | axiom <- axioms |
194 | if axiom.isT5 | 316 | if axiom.isT5 |
195 | role1 <- axiom.objectPropertyExpressionsInSignature | 317 | role1 <- axiom.objectPropertyExpressionsInSignature |
196 | roleSuper = role1 +: reasoner.superObjectProperties(role1) | 318 | roleSuper = role1 +: reasoner.superObjectProperties(role1) |
197 | roleSuperInv = roleSuper.map(_.getInverseProperty) | 319 | roleSuperInv = roleSuper.map(_.getInverseProperty) |
198 | axiom <- tbox | 320 | axiom <- axioms |
199 | if axiom.isT3 && !axiom.isT3top | 321 | if axiom.isT3 && !axiom.isT3top |
200 | role2 <- axiom.objectPropertyExpressionsInSignature | 322 | role2 <- axiom.objectPropertyExpressionsInSignature |
201 | if roleSuperInv contains role2 | 323 | if roleSuperInv contains role2 |
@@ -203,12 +325,12 @@ class RSAOntology(val original: OWLOntology, val datafiles: File*) { | |||
203 | 325 | ||
204 | /* Checking for unsafety condition (2) */ | 326 | /* Checking for unsafety condition (2) */ |
205 | val unsafe2 = for { | 327 | val unsafe2 = for { |
206 | axiom <- tbox | 328 | axiom <- axioms |
207 | if axiom.isT5 | 329 | if axiom.isT5 |
208 | role1 <- axiom.objectPropertyExpressionsInSignature | 330 | role1 <- axiom.objectPropertyExpressionsInSignature |
209 | roleSuper = role1 +: reasoner.superObjectProperties(role1) | 331 | roleSuper = role1 +: reasoner.superObjectProperties(role1) |
210 | roleSuperInv = roleSuper.map(_.getInverseProperty) | 332 | roleSuperInv = roleSuper.map(_.getInverseProperty) |
211 | axiom <- tbox | 333 | axiom <- axioms |
212 | if axiom.isT4 | 334 | if axiom.isT4 |
213 | role2 <- axiom.objectPropertyExpressionsInSignature | 335 | role2 <- axiom.objectPropertyExpressionsInSignature |
214 | if roleSuper.contains(role2) || roleSuperInv.contains(role2) | 336 | if roleSuper.contains(role2) || roleSuperInv.contains(role2) |
@@ -217,93 +339,6 @@ class RSAOntology(val original: OWLOntology, val datafiles: File*) { | |||
217 | unsafe1 ++ unsafe2 | 339 | unsafe1 ++ unsafe2 |
218 | } | 340 | } |
219 | 341 | ||
220 | /** Compute the RSA dependency graph | ||
221 | * | ||
222 | * This is used to approximate the input ontology to RSA. | ||
223 | * | ||
224 | * @return a tuple containing the dependency graph and a map between | ||
225 | * the constants newly introduced and the corresponding axioms in the | ||
226 | * ontology. | ||
227 | */ | ||
228 | private def dependencyGraph() | ||
229 | : (Graph[Resource, DiEdge], Map[String, OWLAxiom]) = { | ||
230 | val unsafe = this.unsafeRoles | ||
231 | var nodemap = Map.empty[String, OWLAxiom] | ||
232 | |||
233 | object RSAConverter extends RDFoxConverter { | ||
234 | |||
235 | override def convert( | ||
236 | expr: OWLClassExpression, | ||
237 | term: Term, | ||
238 | unsafe: List[OWLObjectPropertyExpression], | ||
239 | skolem: SkolemStrategy, | ||
240 | suffix: RSASuffix | ||
241 | ): Shards = | ||
242 | (expr, skolem) match { | ||
243 | |||
244 | case (e: OWLObjectSomeValuesFrom, c: Constant) => { | ||
245 | nodemap.update(c.iri.getIRI, c.axiom) | ||
246 | val (res, ext) = super.convert(e, term, unsafe, skolem, suffix) | ||
247 | if (unsafe contains e.getProperty) | ||
248 | (RSA.PE(term, c.iri) :: RSA.U(c.iri) :: res, ext) | ||
249 | else | ||
250 | (RSA.PE(term, c.iri) :: res, ext) | ||
251 | } | ||
252 | |||
253 | case (e: OWLDataSomeValuesFrom, c: Constant) => { | ||
254 | nodemap.update(c.iri.getIRI, c.axiom) | ||
255 | val (res, ext) = super.convert(e, term, unsafe, skolem, suffix) | ||
256 | if (unsafe contains e.getProperty) | ||
257 | (RSA.PE(term, c.iri) :: RSA.U(c.iri) :: res, ext) | ||
258 | else | ||
259 | (RSA.PE(term, c.iri) :: res, ext) | ||
260 | } | ||
261 | |||
262 | case _ => super.convert(expr, term, unsafe, skolem, suffix) | ||
263 | } | ||
264 | } | ||
265 | |||
266 | /* Ontology convertion into LP rules */ | ||
267 | val term = RSAOntology.genFreshVariable() | ||
268 | val result = axioms.map(a => | ||
269 | RSAConverter.convert(a, term, unsafe, new Constant(a), Empty) | ||
270 | ) | ||
271 | |||
272 | val datalog = result.unzip | ||
273 | val facts = datalog._1.flatten | ||
274 | var rules = datalog._2.flatten | ||
275 | |||
276 | /* Open connection with RDFox */ | ||
277 | val (server, data) = RDFoxUtil.openConnection("rsa_dependency_graph") | ||
278 | |||
279 | /* Add additional built-in rules */ | ||
280 | val varX = Variable.create("X") | ||
281 | val varY = Variable.create("Y") | ||
282 | rules = Rule.create( | ||
283 | RSA.E(varX, varY), | ||
284 | RSA.PE(varX, varY), | ||
285 | RSA.U(varX), | ||
286 | RSA.U(varY) | ||
287 | ) :: rules | ||
288 | /* Load facts and rules from ontology */ | ||
289 | RDFoxUtil.addFacts(data, facts) | ||
290 | RDFoxUtil.addRules(data, rules) | ||
291 | /* Load data files */ | ||
292 | RDFoxUtil.addData(data, datafiles: _*) | ||
293 | |||
294 | /* Build the graph */ | ||
295 | val query = "SELECT ?X ?Y WHERE { ?X rsa:E ?Y }" | ||
296 | val answers = RDFoxUtil.submitQuery(data, query, RSA.Prefixes).get | ||
297 | var edges: Seq[DiEdge[Resource]] = | ||
298 | answers.collect { case (_, Seq(n1, n2)) => n1 ~> n2 } | ||
299 | val graph = Graph(edges: _*) | ||
300 | |||
301 | /* Close connection to RDFox */ | ||
302 | RDFoxUtil.closeConnection(server, data) | ||
303 | |||
304 | (graph, nodemap) | ||
305 | } | ||
306 | |||
307 | /** Approximate a Horn-ALCHOIQ ontology to RSA | 342 | /** Approximate a Horn-ALCHOIQ ontology to RSA |
308 | * | 343 | * |
309 | * This is done by gathering those axioms that prevent the ontology | 344 | * This is done by gathering those axioms that prevent the ontology |
@@ -313,61 +348,61 @@ class RSAOntology(val original: OWLOntology, val datafiles: File*) { | |||
313 | * @param graph the graph used to compute the axioms to remove. | 348 | * @param graph the graph used to compute the axioms to remove. |
314 | * @param nodemap map from graph nodes to ontology axioms. | 349 | * @param nodemap map from graph nodes to ontology axioms. |
315 | */ | 350 | */ |
316 | def toRSA(): RSAOntology = Logger.timed( | 351 | // def toRSA(): RSAOntology = Logger.timed( |
317 | { | 352 | // { |
318 | 353 | ||
319 | /* Compute the dependency graph for the ontology */ | 354 | // /* Compute the dependency graph for the ontology */ |
320 | val (graph, nodemap) = this.dependencyGraph() | 355 | // val (graph, nodemap) = this.dependencyGraph() |
321 | 356 | ||
322 | /* Define node colors for the graph visit */ | 357 | // /* Define node colors for the graph visit */ |
323 | sealed trait NodeColor | 358 | // sealed trait NodeColor |
324 | case object Unvisited extends NodeColor | 359 | // case object Unvisited extends NodeColor |
325 | case object Visited extends NodeColor | 360 | // case object Visited extends NodeColor |
326 | case object ToDelete extends NodeColor | 361 | // case object ToDelete extends NodeColor |
327 | 362 | ||
328 | /* Keep track of node colors during graph visit */ | 363 | // /* Keep track of node colors during graph visit */ |
329 | var color = Map.from[Resource, NodeColor]( | 364 | // var color = Map.from[Resource, NodeColor]( |
330 | graph.nodes.toOuter.map(k => (k, Unvisited)) | 365 | // graph.nodes.toOuter.map(k => (k, Unvisited)) |
331 | ) | 366 | // ) |
332 | 367 | ||
333 | for { | 368 | // for { |
334 | component <- graph.componentTraverser().map(_ to Graph) | 369 | // component <- graph.componentTraverser().map(_ to Graph) |
335 | edge <- component | 370 | // edge <- component |
336 | .outerEdgeTraverser(component.nodes.head) | 371 | // .outerEdgeTraverser(component.nodes.head) |
337 | .withKind(BreadthFirst) | 372 | // .withKind(BreadthFirst) |
338 | } yield { | 373 | // } yield { |
339 | val source = edge._1 | 374 | // val source = edge._1 |
340 | val target = edge._2 | 375 | // val target = edge._2 |
341 | color(source) match { | 376 | // color(source) match { |
342 | case Unvisited | Visited => { | 377 | // case Unvisited | Visited => { |
343 | color(target) match { | 378 | // color(target) match { |
344 | case Unvisited => | 379 | // case Unvisited => |
345 | color(source) = Visited; | 380 | // color(source) = Visited; |
346 | color(target) = Visited | 381 | // color(target) = Visited |
347 | case Visited => | 382 | // case Visited => |
348 | color(source) = ToDelete | 383 | // color(source) = ToDelete |
349 | case ToDelete => | 384 | // case ToDelete => |
350 | color(source) = Visited | 385 | // color(source) = Visited |
351 | } | 386 | // } |
352 | } | 387 | // } |
353 | case ToDelete => | 388 | // case ToDelete => |
354 | } | 389 | // } |
355 | } | 390 | // } |
356 | 391 | ||
357 | val toDelete = color.iterator.collect { case (resource: IRI, ToDelete) => | 392 | // val toDelete = color.iterator.collect { case (resource: IRI, ToDelete) => |
358 | nodemap(resource.getIRI) | 393 | // nodemap(resource.getIRI) |
359 | }.toSeq | 394 | // }.toSeq |
360 | 395 | ||
361 | /* Remove axioms from approximated ontology */ | 396 | // /* Remove axioms from approximated ontology */ |
362 | ontology.removeAxioms(toDelete: _*) | 397 | // ontology.removeAxioms(toDelete: _*) |
363 | this.removed = toDelete | 398 | // this.removed = toDelete |
364 | 399 | ||
365 | /* Return RSA ontology */ | 400 | // /* Return RSA ontology */ |
366 | RSAOntology(ontology, datafiles: _*) | 401 | // RSAOntology(ontology, datafiles: _*) |
367 | }, | 402 | // }, |
368 | "Horn-ALCHOIQ to RSA approximation:", | 403 | // "Horn-ALCHOIQ to RSA approximation:", |
369 | Logger.DEBUG | 404 | // Logger.DEBUG |
370 | ) | 405 | // ) |
371 | // val edges1 = Seq('A ~> 'B, 'B ~> 'C, 'C ~> 'D, 'D ~> 'H, 'H ~> | 406 | // val edges1 = Seq('A ~> 'B, 'B ~> 'C, 'C ~> 'D, 'D ~> 'H, 'H ~> |
372 | // 'G, 'G ~> 'F, 'E ~> 'A, 'E ~> 'F, 'B ~> 'E, 'F ~> 'G, 'B ~> 'F, | 407 | // 'G, 'G ~> 'F, 'E ~> 'A, 'E ~> 'F, 'B ~> 'E, 'F ~> 'G, 'B ~> 'F, |
373 | // 'C ~> 'G, 'D ~> 'C, 'H ~> 'D) | 408 | // 'C ~> 'G, 'D ~> 'C, 'H ~> 'D) |
@@ -651,7 +686,7 @@ class RSAOntology(val original: OWLOntology, val datafiles: File*) { | |||
651 | val conflR = this.confl(roleR) | 686 | val conflR = this.confl(roleR) |
652 | // We just need the TBox to find | 687 | // We just need the TBox to find |
653 | val terms = for { | 688 | val terms = for { |
654 | axiom1 <- tbox | 689 | axiom1 <- axioms |
655 | if axiom1.isT5 | 690 | if axiom1.isT5 |
656 | // We expect only one role coming out of a T5 axiom | 691 | // We expect only one role coming out of a T5 axiom |
657 | roleS <- axiom1.objectPropertyExpressionsInSignature | 692 | roleS <- axiom1.objectPropertyExpressionsInSignature |
@@ -676,28 +711,28 @@ class RSAOntology(val original: OWLOntology, val datafiles: File*) { | |||
676 | this.self(axiom) | this.cycle(axiom) | 711 | this.self(axiom) | this.cycle(axiom) |
677 | 712 | ||
678 | /** Log normalization/approximation statistics */ | 713 | /** Log normalization/approximation statistics */ |
679 | def statistics(level: Logger.Level = Logger.DEBUG): Unit = { | 714 | // def statistics(level: Logger.Level = Logger.DEBUG): Unit = { |
680 | Logger.print( | 715 | // Logger.print( |
681 | s"Logical axioms in original input ontology: ${original.getLogicalAxiomCount(true)}", | 716 | // s"Logical axioms in original input ontology: ${original.getLogicalAxiomCount(true)}", |
682 | level | 717 | // level |
683 | ) | 718 | // ) |
684 | Logger.print( | 719 | // Logger.print( |
685 | s"Logical axioms discarded in Horn-ALCHOIQ approximation: ${normalizer.discarded}", | 720 | // s"Logical axioms discarded in Horn-ALCHOIQ approximation: ${normalizer.discarded}", |
686 | level | 721 | // level |
687 | ) | 722 | // ) |
688 | Logger.print( | 723 | // Logger.print( |
689 | s"Logical axioms shifted in Horn-ALCHOIQ approximation: ${normalizer.shifted}", | 724 | // s"Logical axioms shifted in Horn-ALCHOIQ approximation: ${normalizer.shifted}", |
690 | level | 725 | // level |
691 | ) | 726 | // ) |
692 | Logger.print( | 727 | // Logger.print( |
693 | s"Logical axioms in Horn-ALCHOIQ ontology: ${ontology | 728 | // s"Logical axioms in Horn-ALCHOIQ ontology: ${ontology |
694 | .getLogicalAxiomCount(true)} (${tbox.length}/${rbox.length}/${abox.length})", | 729 | // .getLogicalAxiomCount(true)} (${axioms.length}/${axioms.length}/${axioms.length})", |
695 | level | 730 | // level |
696 | ) | 731 | // ) |
697 | Logger.print( | 732 | // Logger.print( |
698 | s"Logical axioms discarded in RSA approximation: ${removed.length}", | 733 | // s"Logical axioms discarded in RSA approximation: ${removed.length}", |
699 | level | 734 | // level |
700 | ) | 735 | // ) |
701 | } | 736 | // } |
702 | 737 | ||
703 | } // class RSAOntology | 738 | } // class RSAOntology |
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 new file mode 100644 index 0000000..1b49413 --- /dev/null +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/approximation/approximation.scala | |||
@@ -0,0 +1,19 @@ | |||
1 | package uk.ac.ox.cs.rsacomb.approximation | ||
2 | |||
3 | import java.io.File | ||
4 | import org.semanticweb.owlapi.model.OWLLogicalAxiom | ||
5 | |||
6 | /** Ontology approximation technique. */ | ||
7 | trait Approximation { | ||
8 | |||
9 | /** Approximate an ontology. | ||
10 | * | ||
11 | * @param ontology input ontology | ||
12 | * @return a new approximated ontology | ||
13 | */ | ||
14 | def approximate( | ||
15 | ontology: List[OWLLogicalAxiom], | ||
16 | datafiles: List[File] | ||
17 | ): List[OWLLogicalAxiom] | ||
18 | |||
19 | } | ||
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 new file mode 100644 index 0000000..8a86d19 --- /dev/null +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/approximation/lowerbound.scala | |||
@@ -0,0 +1,241 @@ | |||
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.converter.Normalizer | ||
18 | |||
19 | /** Approximation algorithm that mantains soundness for CQ answering. | ||
20 | * | ||
21 | * The input OWL 2 ontology is assumed to be normalized and the output | ||
22 | * ontology is guaranteed to be in RSA. | ||
23 | * | ||
24 | * The algorithm is performed in three steps: | ||
25 | * 1. the ontology is reduced to ALCHOIQ by discarding any axiom | ||
26 | * that is not in the language; | ||
27 | * 2. the ontology is further reduced to Horn-ALCHOIQ by shifting | ||
28 | * axioms with disjunction on the rhs; | ||
29 | * 3. the ontology is approximated to RSA by manipulating its | ||
30 | * dependency graph. | ||
31 | * | ||
32 | * @see [[uk.ac.ox.cs.rsacomb.converter.Normalizer]] | ||
33 | */ | ||
34 | class LowerBound extends Approximation { | ||
35 | |||
36 | /** Simplify conversion between Java and Scala collections */ | ||
37 | import uk.ac.ox.cs.rsacomb.implicits.JavaCollections._ | ||
38 | |||
39 | /** Simplify conversion between OWLAPI and RDFox concepts */ | ||
40 | import uk.ac.ox.cs.rsacomb.implicits.RDFox._ | ||
41 | |||
42 | /** Manager instance to interface with OWLAPI */ | ||
43 | val manager = OWLManager.createOWLOntologyManager() | ||
44 | val factory = manager.getOWLDataFactory() | ||
45 | |||
46 | val normalizer = new Normalizer() | ||
47 | |||
48 | /** Main entry point for the approximation algorithm */ | ||
49 | def approximate( | ||
50 | ontology: List[OWLLogicalAxiom], | ||
51 | datafiles: List[File] | ||
52 | ): List[OWLLogicalAxiom] = { | ||
53 | /* Normalize axioms */ | ||
54 | val axioms1 = ontology flatMap normalizer.normalize | ||
55 | /* Delete any axiom outside of ALCHOIQ */ | ||
56 | val axioms2 = axioms1 filterNot inALCHOIQ | ||
57 | /* Shift any axiom with disjunction on the rhs */ | ||
58 | val axioms3 = for { | ||
59 | a1 <- axioms1 | ||
60 | a2 <- shift(a1) | ||
61 | a3 <- normalizer.normalize(a2) | ||
62 | } yield a3 | ||
63 | /* Approximate to RSA */ | ||
64 | toRSA(axioms3, datafiles) | ||
65 | } | ||
66 | |||
67 | /** Discards all axioms outside ALCHOIQ */ | ||
68 | def inALCHOIQ(axiom: OWLLogicalAxiom): Boolean = | ||
69 | axiom match { | ||
70 | case a: OWLSubClassOfAxiom => { | ||
71 | val sub = a.getSubClass.getNNF | ||
72 | val sup = a.getSuperClass.getNNF | ||
73 | (sub, sup) match { | ||
74 | case (sub: OWLObjectAllValuesFrom, _) => false | ||
75 | case (sub: OWLDataAllValuesFrom, _) => false | ||
76 | case (_, sup: OWLDataAllValuesFrom) => false | ||
77 | case (sub: OWLObjectMinCardinality, _) if sub.getCardinality >= 2 => | ||
78 | false | ||
79 | case (sub: OWLDataMinCardinality, _) if sub.getCardinality >= 2 => | ||
80 | false | ||
81 | case (_, sup: OWLObjectMinCardinality) if sup.getCardinality >= 2 => | ||
82 | false | ||
83 | case (_, sup: OWLDataMinCardinality) if sup.getCardinality >= 2 => | ||
84 | false | ||
85 | case (sub: OWLObjectMaxCardinality, _) => false | ||
86 | case (sub: OWLDataMaxCardinality, _) => false | ||
87 | case (_, sup: OWLObjectMaxCardinality) if sup.getCardinality >= 2 => | ||
88 | false | ||
89 | case (_, sup: OWLDataMaxCardinality) if sup.getCardinality >= 1 => | ||
90 | false | ||
91 | case (_, sup: OWLObjectOneOf) if sup.getIndividuals.length > 2 => | ||
92 | false | ||
93 | case (sub: OWLObjectHasSelf, _) => false | ||
94 | case (_, sup: OWLObjectHasSelf) => false | ||
95 | case _ => true | ||
96 | } | ||
97 | } | ||
98 | case a: OWLTransitiveObjectPropertyAxiom => false | ||
99 | case a: OWLReflexiveObjectPropertyAxiom => false | ||
100 | case a: OWLSubPropertyChainOfAxiom => false | ||
101 | case a: OWLAsymmetricObjectPropertyAxiom => false | ||
102 | case a => true | ||
103 | } | ||
104 | |||
105 | /** Shifting axioms with disjunction on the rhs. | ||
106 | * | ||
107 | * The process of shifting presenves soundness but completenes w.r.t. | ||
108 | * CQ answering is lost. | ||
109 | * | ||
110 | * @example | ||
111 | * | ||
112 | * A -> B1 u B2 u B3 . | ||
113 | * | ||
114 | * becomes | ||
115 | * | ||
116 | * A n nB1 n nB2 -> B3 . | ||
117 | * A n nB1 n nB3 -> B2 . | ||
118 | * A n nB2 n nB3 -> B1 . | ||
119 | * nB1 n nB2 n nB3 -> nA . | ||
120 | * | ||
121 | * where nA, nB1, nB2, nB3 are fresh predicates "corresponding" to | ||
122 | * the negation of A, B1, B2, B3 respectively. | ||
123 | */ | ||
124 | def shift(axiom: OWLLogicalAxiom): List[OWLLogicalAxiom] = | ||
125 | axiom match { | ||
126 | case a: OWLSubClassOfAxiom => { | ||
127 | val sub = a.getSubClass.getNNF | ||
128 | val sup = a.getSuperClass.getNNF | ||
129 | sup match { | ||
130 | case sup: OWLObjectUnionOf => { | ||
131 | val body = sub.asConjunctSet.map((atom) => | ||
132 | (atom, RSAOntology.getFreshOWLClass()) | ||
133 | ) | ||
134 | val head = sup.asDisjunctSet.map((atom) => | ||
135 | (atom, RSAOntology.getFreshOWLClass()) | ||
136 | ) | ||
137 | |||
138 | val r1 = | ||
139 | factory.getOWLSubClassOfAxiom( | ||
140 | factory.getOWLObjectIntersectionOf( | ||
141 | (body.map(_._1) ++ head.map(_._2)): _* | ||
142 | ), | ||
143 | factory.getOWLNothing | ||
144 | ) | ||
145 | |||
146 | val r2s = | ||
147 | for { | ||
148 | (a, na) <- head | ||
149 | hs = head.map(_._2).filterNot(_ equals na) | ||
150 | } yield factory.getOWLSubClassOfAxiom( | ||
151 | factory.getOWLObjectIntersectionOf( | ||
152 | (body.map(_._1) ++ hs): _* | ||
153 | ), | ||
154 | a | ||
155 | ) | ||
156 | |||
157 | val r3s = | ||
158 | for { | ||
159 | (a, na) <- body | ||
160 | bs = body.map(_._1).filterNot(_ equals a) | ||
161 | } yield factory.getOWLSubClassOfAxiom( | ||
162 | factory.getOWLObjectIntersectionOf( | ||
163 | (bs ++ head.map(_._2)): _* | ||
164 | ), | ||
165 | na | ||
166 | ) | ||
167 | |||
168 | List(r1) ++ r2s ++ r3s | ||
169 | } | ||
170 | case _ => List(axiom) | ||
171 | } | ||
172 | } | ||
173 | case _ => List(axiom) | ||
174 | } | ||
175 | |||
176 | /** Approximate a Horn-ALCHOIQ ontology to RSA | ||
177 | * | ||
178 | * This is done by gathering those axioms that prevent the ontology | ||
179 | * dependency graph from being tree-shaped, and removing them. | ||
180 | * | ||
181 | * @param axioms the set of axioms to approximate. | ||
182 | * @return the approximated set of axioms. | ||
183 | */ | ||
184 | def toRSA( | ||
185 | axioms: List[OWLLogicalAxiom], | ||
186 | datafiles: List[File] | ||
187 | ): List[OWLLogicalAxiom] = { | ||
188 | /* Compute the dependency graph for the ontology */ | ||
189 | val (graph, nodemap) = RSAUtil.dependencyGraph(axioms, datafiles) | ||
190 | |||
191 | /* Define node colors for the graph visit */ | ||
192 | sealed trait NodeColor | ||
193 | case object Unvisited extends NodeColor | ||
194 | case object Visited extends NodeColor | ||
195 | case object ToDelete extends NodeColor | ||
196 | |||
197 | /* Keep track of node colors during graph visit */ | ||
198 | var color = Map.from[Resource, NodeColor]( | ||
199 | graph.nodes.toOuter.map(k => (k, Unvisited)) | ||
200 | ) | ||
201 | |||
202 | for { | ||
203 | component <- graph.componentTraverser().map(_ to Graph) | ||
204 | edge <- component | ||
205 | .outerEdgeTraverser(component.nodes.head) | ||
206 | .withKind(BreadthFirst) | ||
207 | } yield { | ||
208 | val source = edge._1 | ||
209 | val target = edge._2 | ||
210 | color(source) match { | ||
211 | case Unvisited | Visited => { | ||
212 | color(target) match { | ||
213 | case Unvisited => | ||
214 | color(source) = Visited; | ||
215 | color(target) = Visited | ||
216 | case Visited => | ||
217 | color(source) = ToDelete | ||
218 | case ToDelete => | ||
219 | color(source) = Visited | ||
220 | } | ||
221 | } | ||
222 | case ToDelete => | ||
223 | } | ||
224 | } | ||
225 | |||
226 | val toDelete = color.iterator.collect { case (resource: IRI, ToDelete) => | ||
227 | nodemap(resource.getIRI) | ||
228 | }.toList | ||
229 | |||
230 | /* Remove axioms from approximated ontology */ | ||
231 | axioms diff toDelete | ||
232 | } | ||
233 | |||
234 | // val edges1 = Seq('A ~> 'B, 'B ~> 'C, 'C ~> 'D, 'D ~> 'H, 'H ~> | ||
235 | // 'G, 'G ~> 'F, 'E ~> 'A, 'E ~> 'F, 'B ~> 'E, 'F ~> 'G, 'B ~> 'F, | ||
236 | // 'C ~> 'G, 'D ~> 'C, 'H ~> 'D) | ||
237 | // val edges2 = Seq('I ~> 'M, 'I ~> 'L, 'L ~> 'N, 'M ~> 'N) | ||
238 | // val edges3 = Seq('P ~> 'O) | ||
239 | // val graph = Graph.from(edges = edges1 ++ edges2 ++ edges3) | ||
240 | // | ||
241 | } | ||
diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/converter/Normalizer.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/converter/Normalizer.scala index 5329f26..4b298f4 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/converter/Normalizer.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/converter/Normalizer.scala | |||
@@ -4,6 +4,7 @@ import org.semanticweb.owlapi.apibinding.OWLManager | |||
4 | import org.semanticweb.owlapi.model._ | 4 | import org.semanticweb.owlapi.model._ |
5 | 5 | ||
6 | import uk.ac.ox.cs.rsacomb.util.Logger | 6 | import uk.ac.ox.cs.rsacomb.util.Logger |
7 | import uk.ac.ox.cs.rsacomb.RSAOntology | ||
7 | 8 | ||
8 | object Normalizer { | 9 | object Normalizer { |
9 | 10 | ||
@@ -25,12 +26,6 @@ class Normalizer() { | |||
25 | /** Simplify conversion between Java and Scala collections */ | 26 | /** Simplify conversion between Java and Scala collections */ |
26 | import uk.ac.ox.cs.rsacomb.implicits.JavaCollections._ | 27 | import uk.ac.ox.cs.rsacomb.implicits.JavaCollections._ |
27 | 28 | ||
28 | private var counter = -1 | ||
29 | def freshOWLClass(): OWLClass = { | ||
30 | counter += 1 | ||
31 | factory.getOWLClass(s"X$counter") | ||
32 | } | ||
33 | |||
34 | /** Statistics */ | 29 | /** Statistics */ |
35 | var discarded = 0 | 30 | var discarded = 0 |
36 | var shifted = 0 | 31 | var shifted = 0 |
@@ -58,7 +53,7 @@ class Normalizer() { | |||
58 | * C c D -> { C c X, X c D } | 53 | * C c D -> { C c X, X c D } |
59 | */ | 54 | */ |
60 | case _ if !sub.isOWLClass && !sup.isOWLClass => { | 55 | case _ if !sub.isOWLClass && !sup.isOWLClass => { |
61 | val cls = freshOWLClass() | 56 | val cls = RSAOntology.getFreshOWLClass() |
62 | Seq( | 57 | Seq( |
63 | factory.getOWLSubClassOfAxiom(sub, cls), | 58 | factory.getOWLSubClassOfAxiom(sub, cls), |
64 | factory.getOWLSubClassOfAxiom(cls, sup) | 59 | factory.getOWLSubClassOfAxiom(cls, sup) |
@@ -79,7 +74,7 @@ class Normalizer() { | |||
79 | if (conj.isOWLClass) | 74 | if (conj.isOWLClass) |
80 | (acc1 :+ conj, acc2) | 75 | (acc1 :+ conj, acc2) |
81 | else { | 76 | else { |
82 | val cls = freshOWLClass() | 77 | val cls = RSAOntology.getFreshOWLClass() |
83 | ( | 78 | ( |
84 | acc1 :+ cls, | 79 | acc1 :+ cls, |
85 | acc2 :+ factory.getOWLSubClassOfAxiom(conj, cls) | 80 | acc2 :+ factory.getOWLSubClassOfAxiom(conj, cls) |
@@ -138,7 +133,7 @@ class Normalizer() { | |||
138 | */ | 133 | */ |
139 | case (sub: OWLObjectSomeValuesFrom, _) | 134 | case (sub: OWLObjectSomeValuesFrom, _) |
140 | if !sub.getFiller.isOWLClass => { | 135 | if !sub.getFiller.isOWLClass => { |
141 | val cls = freshOWLClass() | 136 | val cls = RSAOntology.getFreshOWLClass() |
142 | Seq( | 137 | Seq( |
143 | factory.getOWLSubClassOfAxiom(sub.getFiller, cls), | 138 | factory.getOWLSubClassOfAxiom(sub.getFiller, cls), |
144 | factory.getOWLSubClassOfAxiom( | 139 | factory.getOWLSubClassOfAxiom( |
@@ -153,7 +148,7 @@ class Normalizer() { | |||
153 | */ | 148 | */ |
154 | case (_, sup: OWLObjectSomeValuesFrom) | 149 | case (_, sup: OWLObjectSomeValuesFrom) |
155 | if !sup.getFiller.isOWLClass => { | 150 | if !sup.getFiller.isOWLClass => { |
156 | val cls = freshOWLClass() | 151 | val cls = RSAOntology.getFreshOWLClass() |
157 | Seq( | 152 | Seq( |
158 | factory.getOWLSubClassOfAxiom(cls, sup.getFiller), | 153 | factory.getOWLSubClassOfAxiom(cls, sup.getFiller), |
159 | factory.getOWLSubClassOfAxiom( | 154 | factory.getOWLSubClassOfAxiom( |
@@ -298,7 +293,7 @@ class Normalizer() { | |||
298 | ) | 293 | ) |
299 | case (_, sup: OWLObjectMaxCardinality) | 294 | case (_, sup: OWLObjectMaxCardinality) |
300 | if sup.getCardinality == 1 && !sup.getFiller.isOWLClass => { | 295 | if sup.getCardinality == 1 && !sup.getFiller.isOWLClass => { |
301 | val cls = freshOWLClass() | 296 | val cls = RSAOntology.getFreshOWLClass() |
302 | Seq( | 297 | Seq( |
303 | factory.getOWLSubClassOfAxiom(cls, sup.getFiller), | 298 | factory.getOWLSubClassOfAxiom(cls, sup.getFiller), |
304 | factory.getOWLSubClassOfAxiom( | 299 | factory.getOWLSubClassOfAxiom( |
@@ -488,7 +483,7 @@ class Normalizer() { | |||
488 | * C(a) -> { X(a), X c C } | 483 | * C(a) -> { X(a), X c C } |
489 | */ | 484 | */ |
490 | case a: OWLClassAssertionAxiom if !a.getClassExpression.isOWLClass => { | 485 | case a: OWLClassAssertionAxiom if !a.getClassExpression.isOWLClass => { |
491 | val cls = freshOWLClass() | 486 | val cls = RSAOntology.getFreshOWLClass() |
492 | Seq( | 487 | Seq( |
493 | factory.getOWLClassAssertionAxiom(cls, a.getIndividual), | 488 | factory.getOWLClassAssertionAxiom(cls, a.getIndividual), |
494 | factory.getOWLSubClassOfAxiom(cls, a.getClassExpression) | 489 | factory.getOWLSubClassOfAxiom(cls, a.getClassExpression) |
@@ -532,8 +527,10 @@ class Normalizer() { | |||
532 | sub: OWLClassExpression, | 527 | sub: OWLClassExpression, |
533 | sup: OWLObjectUnionOf | 528 | sup: OWLObjectUnionOf |
534 | ): Seq[OWLLogicalAxiom] = { | 529 | ): Seq[OWLLogicalAxiom] = { |
535 | val body = sub.asConjunctSet.map((atom) => (atom, freshOWLClass())) | 530 | val body = |
536 | val head = sup.asDisjunctSet.map((atom) => (atom, freshOWLClass())) | 531 | sub.asConjunctSet.map((atom) => (atom, RSAOntology.getFreshOWLClass())) |
532 | val head = | ||
533 | sup.asDisjunctSet.map((atom) => (atom, RSAOntology.getFreshOWLClass())) | ||
537 | 534 | ||
538 | /* Update statistics */ | 535 | /* Update statistics */ |
539 | shifted += 1 | 536 | shifted += 1 |