/** * Copyright (c) 2009 International Health Terminology Standards Development * Organisation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * Copyright CSIRO Australian e-Health Research Centre (http://aehrc.com). * All rights reserved. Use is subject to license terms and conditions. */ package au.csiro.snorocket.core.axioms; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import au.csiro.snorocket.core.IFactory; import au.csiro.snorocket.core.model.AbstractConcept; import au.csiro.snorocket.core.model.Concept; import au.csiro.snorocket.core.model.Conjunction; import au.csiro.snorocket.core.model.Datatype; import au.csiro.snorocket.core.model.Existential; /** * A General Concept Inclusion (C ⊑ D) * * @author Michael Lawley * */ public class GCI extends Inclusion { /** * */ private static final long serialVersionUID = 1L; // Logger private final static Logger log = LoggerFactory.getLogger(GCI.class); private static final int PRIME = 31; final private AbstractConcept lhs; final private AbstractConcept rhs; final private int hashCode; /** * lhs ⊑ rhs * * @param lhs * @param rhs */ public GCI(final AbstractConcept lhs, final AbstractConcept rhs) { if (null == lhs) { throw new IllegalArgumentException("LHS cannot be null (RHS = " + rhs + ")"); } this.lhs = lhs; if (null == rhs) { this.rhs = new Concept(IFactory.TOP_CONCEPT); } else { this.rhs = rhs; } hashCode = PRIME * (PRIME + this.lhs.hashCode()) + this.rhs.hashCode(); } public GCI(final int lhs, final AbstractConcept rhs) { this(new Concept(lhs), rhs); } public GCI(final AbstractConcept lhs, final int rhs) { this(lhs, new Concept(rhs)); } public GCI(final int lhs, final int rhs) { this(new Concept(lhs), new Concept(rhs)); } public AbstractConcept lhs() { return lhs; } public AbstractConcept rhs() { return rhs; } public Inclusion[] normalise1(final IFactory factory) { final Inclusion[] result = { null, null }; if (rule2(factory, result) || rule3(factory, result) || rule4(result)) { return result; } return null; } public Inclusion[] normalise2(final IFactory factory) { final Inclusion[] result = { null, null, null, null, null, null, null, null }; if (isRule7Applicable()) { return rule7(result); } else if (rule6(factory, result) || rule5(factory, result)) { return result; } return null; } /** * C ⊓ D' ⊑ E → {D' ⊑ A, C ⊓ A ⊑ E} * * @param gcis * @return */ boolean rule2(final IFactory factory, final Inclusion[] gcis) { boolean result = false; if (lhs instanceof Conjunction) { Conjunction conjunction = (Conjunction) lhs; final AbstractConcept[] concepts = conjunction.getConcepts(); if (concepts.length == 1) { // unwrap redundant conjuncts gcis[0] = new GCI(concepts[0], rhs); result = true; } else if (concepts.length == 0) { log.warn("Empty conjunct detected in: " + this); gcis[0] = new GCI(IFactory.TOP_CONCEPT, rhs); result = true; } else { // Swap out any non-Concept concepts (ie Existentials) for (int i = 0; !result && i < concepts.length; i++) { if (!(concepts[i] instanceof Concept)) { final Concept a = getA(factory, concepts[i]); gcis[0] = new GCI(concepts[i], a); final AbstractConcept[] newConcepts = new AbstractConcept[concepts.length]; System.arraycopy(concepts, 0, newConcepts, 0, concepts.length); newConcepts[i] = a; gcis[1] = new GCI(new Conjunction(newConcepts), rhs); result = true; } } if (!result) { if (concepts.length > 2) { // Binarise a conjunction of Concepts (expected/assumed // by NF1) result = true; final AbstractConcept[] newConcepts = new AbstractConcept[concepts.length - 1]; System.arraycopy(concepts, 1, newConcepts, 0, concepts.length - 1); final AbstractConcept d = new Conjunction(newConcepts); final Concept a = getA(factory, d); final AbstractConcept cAndA = new Conjunction( new AbstractConcept[] { concepts[0], a }); gcis[0] = new GCI(cAndA, rhs); gcis[1] = new GCI(d, a); } else if (concepts.length < 2) { throw new AssertionError( "Conjunctions of fewer than " + "two elements should not exist at this point: " + this); } } } } return result; } /** * ∃r.C' ⊑ D → {C' ⊑ A, ∃r.A ⊑ D} * * @param gcis * @return */ boolean rule3(final IFactory factory, final Inclusion[] gcis) { boolean result = false; if (lhs instanceof Existential) { Existential existential = (Existential) lhs; final AbstractConcept cHat = existential.getConcept(); if (!(cHat instanceof Concept)) { result = true; Concept a = getA(factory, cHat); gcis[0] = new GCI(cHat, a); gcis[1] = new GCI(new Existential(existential.getRole(), a), rhs); } } return result; } /** * ⊥ ⊑ D → ∅ * * This rule matches ⊥ (bottom), but there is no ⊥ in our * Ontologies (AFAIK) so it's redundant. * * @param gcis * @return */ boolean rule4(Inclusion[] gcis) { boolean result = false; return result; } /** * C' ⊑ D' → {C' ⊑ A, A ⊑ D'} * * @param gcis * @return */ boolean rule5(final IFactory factory, final Inclusion[] gcis) { boolean result = false; if (!(lhs instanceof Concept) && !(rhs instanceof Concept)) { result = true; Concept a = getA(factory, lhs); gcis[0] = new GCI(lhs, a); gcis[1] = new GCI(a, rhs); } return result; } /** * B ⊑ ∃r.C' → {B ⊑ ∃r.A, A ⊑ C'} * * @param gcis * @return */ boolean rule6(final IFactory factory, final Inclusion[] gcis) { boolean result = false; if (rhs instanceof Existential) { Existential existential = (Existential) rhs; final AbstractConcept cHat = existential.getConcept(); if (!(cHat instanceof Concept)) { result = true; Concept a = getA(factory, cHat); gcis[0] = new GCI(lhs, new Existential(existential.getRole(), a)); gcis[1] = new GCI(a, cHat); } } return result; } private Concept getA(final IFactory factory, final AbstractConcept cHat) { final boolean alreadyExists = factory.conceptExists(cHat); final int a = factory.getConcept(cHat); if(!alreadyExists) { factory.setVirtualConcept(a, true); } return new Concept(a); } boolean isRule7Applicable() { return rhs instanceof Conjunction; } /** * B ⊑ C ⊓ D → {B ⊑ C, B ⊑ D} * * @param gcis * @return */ Inclusion[] rule7(Inclusion[] gcis) { assert isRule7Applicable(); final Conjunction conjunction = (Conjunction) rhs; final AbstractConcept[] concepts = conjunction.getConcepts(); if (concepts.length > gcis.length) { gcis = new Inclusion[concepts.length]; } for (int i = 0; i < concepts.length; i++) { gcis[i] = new GCI(lhs, concepts[i]); } return gcis; } @Override public int hashCode() { return hashCode; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; final GCI other = (GCI) obj; if (!lhs.equals(other.lhs)) return false; if (!rhs.equals(other.rhs)) return false; return true; } @Override public String toString() { return lhs.toString() + " [ " + rhs.toString(); } @Override public NormalFormGCI getNormalForm() { final NormalFormGCI result; // try { if (lhs instanceof Concept) { if (rhs instanceof Concept) { final int newLhs = lhs.hashCode(); result = NF1a.getInstance(newLhs, rhs.hashCode()); } else if (rhs instanceof Existential) { final Existential existential = (Existential) rhs; result = NF2.getInstance(lhs.hashCode(), existential.getRole(), existential.getConcept().hashCode()); } else if (rhs instanceof Datatype) { final Datatype datatype = (Datatype) rhs; result = NF7.getInstance(lhs.hashCode(), datatype); } else { throw new IllegalStateException("GCI is not in Normal Form: " + "lhs is Concept but rhs is neither Concept, " + "Existential nor Datatype; it is " + rhs); } } else if (lhs instanceof Conjunction) { final Conjunction conjunction = (Conjunction) lhs; final AbstractConcept[] concepts = conjunction.getConcepts(); if (concepts.length == 1) { result = NF1a.getInstance(concepts[0].hashCode(), rhs.hashCode()); } else if (concepts.length == 2) { result = NF1b.getInstance(concepts[0].hashCode(), concepts[1].hashCode(), rhs.hashCode()); } else { throw new IllegalStateException( "Conjunction should have exactly one or two " + "concepts not " + concepts.length + ": " + conjunction); } } else if (lhs instanceof Existential) { Existential existential = (Existential) lhs; result = NF3.getInstance(existential.getRole(), existential .getConcept().hashCode(), rhs.hashCode()); } else if (lhs instanceof Datatype) { Datatype datatype = (Datatype) lhs; result = NF8.getInstance(datatype, rhs.hashCode()); } else { throw new IllegalStateException("GCI is not in Normal Form: " + lhs + ", " + rhs); } return result; } }