package uk.ac.manchester.cs.jfact.split;
/* This file is part of the JFact DL reasoner
Copyright 2011-2013 by Ignazio Palmisano, Dmitry Tsarkov, University of Manchester
This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA*/
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.semanticweb.owlapi.model.IRI;
import uk.ac.manchester.cs.jfact.kernel.Ontology;
import uk.ac.manchester.cs.jfact.kernel.dl.ConceptName;
import uk.ac.manchester.cs.jfact.kernel.dl.ConceptTop;
import uk.ac.manchester.cs.jfact.kernel.dl.axioms.AxiomConceptInclusion;
import uk.ac.manchester.cs.jfact.kernel.dl.axioms.AxiomEquivalentConcepts;
import uk.ac.manchester.cs.jfact.kernel.dl.interfaces.AxiomInterface;
import uk.ac.manchester.cs.jfact.kernel.dl.interfaces.ConceptExpression;
import uk.ac.manchester.cs.jfact.kernel.options.JFactReasonerConfiguration;
import conformance.PortedFrom;
/** axiom splitter */
@PortedFrom(file = "AxiomSplitter.h", name = "TAxiomSplitter")
public class TAxiomSplitter implements Serializable {
private static final long serialVersionUID = 11000L;
/**
* keep the single rename: named concept C in an axiom (C=D or C[=D) into a
* new name C' and new axiom C'=D or C'[=D
*/
protected class TRecord implements Serializable {
private static final long serialVersionUID = 11000L;
protected ConceptName oldName;
protected ConceptName newName;
protected final List<AxiomInterface> oldAxioms = new ArrayList<AxiomInterface>();
protected AxiomInterface newAxiom = null;
protected TSignature newAxSig = null;
// module for a new axiom
protected final Set<AxiomInterface> Module = new HashSet<AxiomInterface>();
/**
* set old axiom as an equivalent AX; create a new one
*
* @param ax
* ax
*/
protected void setEqAx(AxiomEquivalentConcepts ax) {
oldAxioms.add(ax);
List<ConceptExpression> copy = new ArrayList<ConceptExpression>();
for (ConceptExpression p : ax.getArguments()) {
if (p.equals(oldName)) {
copy.add(newName);
} else {
copy.add(p);
}
}
newAxiom = new AxiomEquivalentConcepts(ax.getOWLAxiom(), copy);
}
/**
* set a new implication axiom based on a (known) set of old ones
*
* @param Desc
* Desc
*/
protected void setImpAx(ConceptExpression Desc) {
newAxiom = new AxiomConceptInclusion(null, newName, Desc);
}
}
@PortedFrom(file = "AxiomSplitter.h", name = "SubNames")
protected final Set<ConceptName> SubNames = new HashSet<ConceptName>();
@PortedFrom(file = "AxiomSplitter.h", name = "Rejects")
protected final Set<ConceptName> Rejects = new HashSet<ConceptName>();
@PortedFrom(file = "AxiomSplitter.h", name = "Renames")
protected final List<TRecord> Renames = new ArrayList<TRecord>();
@PortedFrom(file = "AxiomSplitter.h", name = "R2")
protected final List<TRecord> R2 = new ArrayList<TRecord>();
@PortedFrom(file = "AxiomSplitter.h", name = "ImpRens")
protected final Map<ConceptName, TRecord> ImpRens = new HashMap<ConceptName, TRecord>();
// XXX multimap
@PortedFrom(file = "AxiomSplitter.h", name = "ImplNames")
protected final Map<ConceptName, Set<AxiomConceptInclusion>> ImplNames = new HashMap<ConceptName, Set<AxiomConceptInclusion>>();
@PortedFrom(file = "AxiomSplitter.h", name = "newNameId")
private int newNameId;
// seed signature
@PortedFrom(file = "AxiomSplitter.h", name = "sig")
protected TSignature sig = new TSignature();
@PortedFrom(file = "AxiomSplitter.h", name = "mod")
protected TModularizer mod = null;
@PortedFrom(file = "AxiomSplitter.h", name = "O")
protected final Ontology O;
/**
* rename old concept into a new one with a fresh name
*
* @param oldName
* oldName
* @return new concept
*/
@PortedFrom(file = "AxiomSplitter.h", name = "rename")
protected ConceptName rename(ConceptName oldName) {
ConceptExpression c = O.getExpressionManager().concept(
IRI.create(oldName.getName() + "+" + ++newNameId));
if (c instanceof ConceptName) {
return (ConceptName) c;
}
return null;
}
/**
* process (register/unregister) axioms in a record REC
*
* @param rec
* rec
*/
@PortedFrom(file = "AxiomSplitter.h", name = "processRec")
public void processRec(TRecord rec) {
mod.getSigIndex().preprocessOntology(rec.oldAxioms);
mod.getSigIndex().processAx(rec.newAxiom);
}
/**
* register a record in the ontology
*
* @param rec
* rec
*/
@PortedFrom(file = "AxiomSplitter.h", name = "registerRec")
protected void registerRec(TRecord rec) {
for (AxiomInterface p : rec.oldAxioms) {
O.retract(p);
}
O.add(rec.newAxiom);
processRec(rec);
}
/**
* unregister a record
*
* @param rec
* rec
*/
@PortedFrom(file = "AxiomSplitter.h", name = "unregisterRec")
protected void unregisterRec(TRecord rec) {
for (AxiomInterface p : rec.oldAxioms) {
p.setUsed(true);
}
rec.newAxiom.setUsed(false);
processRec(rec);
}
/**
* create a signature of a module corresponding to a new axiom in record
*
* @param rec
* rec
*/
@PortedFrom(file = "AxiomSplitter.h", name = "buildSig")
protected void buildSig(TRecord rec) {
sig = rec.newAxiom.getSignature();
mod.extract(O.getAxioms(), sig, ModuleType.M_STAR);
// build a module/signature for the axiom
rec.newAxSig = mod.getSignature();
// FIXME!! check that SIG wouldn't change after some axiom retractions
rec.Module.clear();
rec.Module.addAll(mod.getModule());
}
/**
* add axiom CI in a form C [= D for D != TOP
*
* @param ci
* ci
*/
@PortedFrom(file = "AxiomSplitter.h", name = "addSingleCI")
protected void addSingleCI(AxiomConceptInclusion ci) {
// skip axioms with RHS=TOP
if (ci != null && !(ci.getSupConcept() instanceof ConceptTop)
&& ci.getSubConcept() instanceof ConceptName) {
ConceptName name = (ConceptName) ci.getSubConcept();
SubNames.add(name);
if (!ImplNames.containsKey(name)) {
ImplNames.put(name, new HashSet<AxiomConceptInclusion>());
}
ImplNames.get(name).add(ci);
}
}
/** register all axioms in a form C [= D */
@PortedFrom(file = "AxiomSplitter.h", name = "registerCIs")
protected void registerCIs() {
// FIXME!! check for the case (not D) [= (not C) later
// FIXME!! disjoints here as well
for (AxiomInterface p : O.getAxioms()) {
if (p.isUsed() && p instanceof AxiomConceptInclusion) {
addSingleCI((AxiomConceptInclusion) p);
}
}
}
/**
* check whether an equivalent axiom is splittable;
*
* @return split name or NULL if not splittable
* @param ce
* ce
*/
@PortedFrom(file = "AxiomSplitter.h", name = "getEqSplit")
protected ConceptName getEqSplit(AxiomEquivalentConcepts ce) {
// check whether it is not a synonym definition
ConceptName splitName = null, name = null;
int size = ce.size();
for (ConceptExpression q : ce.getArguments()) {
if (q instanceof ConceptName) {
name = (ConceptName) q;
if (SubNames.contains(name)) {
// found a split candidate; save the name
if (splitName == null) {
splitName = name;
} else {
// FIXME!! now we jump out right now, later on we're
// going to do the same with changed axiom
return splitName;
}
} else {
--size;
}
}
}
return size > 1 ? splitName : null;
}
/**
* make the axiom split for the equivalence axiom
*
* @param ce
* ce
*/
@PortedFrom(file = "AxiomSplitter.h", name = "makeEqSplit")
protected void makeEqSplit(AxiomEquivalentConcepts ce) {
if (ce == null) {
return;
}
ConceptName splitName = getEqSplit(ce);
if (splitName == null) {
return;
}
// create new record
TRecord rec = new TRecord();
rec.oldName = splitName;
rec.newName = rename(splitName);
rec.setEqAx(ce);
registerRec(rec);
// register rec
Renames.add(rec);
}
/** split all possible EQ axioms */
@PortedFrom(file = "AxiomSplitter.h", name = "registerEQ")
protected void registerEQ() {
// use index instead of iterators will be invalidated during additions
for (int i = 0; i < O.size(); ++i) {
if (O.get(i).isUsed()
&& O.get(i) instanceof AxiomEquivalentConcepts) {
makeEqSplit((AxiomEquivalentConcepts) O.get(i));
}
}
}
/**
* @return make implication split for a given old NAME
* @param oldName
* oldName
*/
@PortedFrom(file = "AxiomSplitter.h", name = "makeImpSplit")
protected TRecord makeImpSplit(ConceptName oldName) {
ConceptName newName = rename(oldName);
TRecord rec = new TRecord();
rec.oldName = oldName;
rec.newName = newName;
List<ConceptExpression> args = new ArrayList<ConceptExpression>();
for (AxiomConceptInclusion s : ImplNames.get(oldName)) {
rec.oldAxioms.add(s);
args.add(s.getSupConcept());
}
rec.setImpAx(O.getExpressionManager().and(args));
registerRec(rec);
return rec;
}
/**
* @return get imp record of a given name; create if necessary
* @param oldName
* oldName
*/
@PortedFrom(file = "AxiomSplitter.h", name = "getImpRec")
protected TRecord getImpRec(ConceptName oldName) {
if (!ImpRens.containsKey(oldName)) {
ImpRens.put(oldName, makeImpSplit(oldName));
}
return ImpRens.get(oldName);
}
/** create all the necessary records for the implications */
@PortedFrom(file = "AxiomSplitter.h", name = "createAllImplications")
protected void createAllImplications() {
for (TRecord r : Renames) {
getImpRec(r.oldName);
}
}
/** clear modules of Imp and Eq split records */
@PortedFrom(file = "AxiomSplitter.h", name = "clearModules")
protected void clearModules() {
for (Map.Entry<ConceptName, TRecord> p : ImpRens.entrySet()) {
p.getValue().newAxSig.clear();
}
for (TRecord r : Renames) {
r.newAxSig.clear();
}
}
/**
* check whether the record is independent wrt modularity;
*
* @return true iff split was incorrect
* @param rec
* rec
*/
@PortedFrom(file = "AxiomSplitter.h", name = "checkSplitCorrectness")
protected boolean checkSplitCorrectness(TRecord rec) {
if (Rejects.contains(rec.oldName)) {
// unsplit:
// restore the old axiom, get rid of the new one
unregisterRec(rec);
return true;
}
TRecord imp = getImpRec(rec.oldName);
if (imp.newAxSig.size() == 0) {
buildSig(imp);
}
buildSig(rec);
if (rec.newAxSig.containsNamedEntity(rec.oldName)
|| !rec.newAxSig.intersect(imp.newAxSig).isEmpty()) {
// mark name as rejected, un-register imp
Rejects.add(rec.oldName);
unregisterRec(imp);
unregisterRec(rec);
return true;
} else {
// keep the split
R2.add(rec);
return false;
}
}
/** move all independent splits in R2; delete all the rest */
@PortedFrom(file = "AxiomSplitter.h", name = "keepIndependentSplits")
protected void keepIndependentSplits() {
boolean change;
do {
change = false;
clearModules();
for (TRecord r : Renames) {
change |= checkSplitCorrectness(r);
}
Renames.clear();
Renames.addAll(R2);
R2.clear();
} while (change);
}
/**
* split all implications corresponding to oldName;
*
* @return split pointer
* @param oldName
* oldName
*/
@PortedFrom(file = "AxiomSplitter.h", name = "splitImplicationsFor")
protected TSplitVar splitImplicationsFor(ConceptName oldName) {
// check whether we already did translation for such a name
if (O.getSplits().hasCN(oldName)) {
return O.getSplits().get(oldName);
}
TRecord rec = getImpRec(oldName);
// create new split
TSplitVar split = new TSplitVar();
split.setOldName(oldName);
split.addEntry(rec.newName, rec.newAxSig, rec.Module);
O.getSplits().set(oldName, split);
return split;
}
/** split all implications for which equivalences were split as well */
@PortedFrom(file = "AxiomSplitter.h", name = "splitImplications")
protected void splitImplications() {
for (TRecord r : Renames) {
if (!Rejects.contains(r.oldName)) {
TSplitVar split = splitImplicationsFor(r.oldName);
split.addEntry(r.newName, r.newAxSig, r.Module);
} else {
unregisterRec(r);
}
}
}
/**
* @param config
* config
* @param o
* o
*/
public TAxiomSplitter(JFactReasonerConfiguration config, Ontology o) {
newNameId = 0;
O = o;
mod = new TModularizer(config, new SyntacticLocalityChecker());
}
/** build splits */
@PortedFrom(file = "AxiomSplitter.h", name = "buildSplit")
public void buildSplit() {
// first make a set of named concepts C s.t. C [= D is in the ontology
registerCIs();
// now check if some of the C's contains in an equivalence axioms
registerEQ();
if (Renames.isEmpty()) {
return;
}
// make records for the implications
createAllImplications();
// here we have a maximal split; check whether modules are fine
keepIndependentSplits();
// now R2 contains all separated axioms; make one replacement for every
// C [= D axiom
splitImplications();
}
}