package uk.ac.manchester.cs.jfact.kernel; /* 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 static uk.ac.manchester.cs.jfact.helpers.Helper.*; import java.io.Serializable; import java.util.ArrayList; import java.util.EnumMap; import java.util.List; import org.semanticweb.owlapi.model.OWLRuntimeException; import uk.ac.manchester.cs.jfact.datatypes.DatatypeEntry; import uk.ac.manchester.cs.jfact.datatypes.LiteralEntry; import uk.ac.manchester.cs.jfact.helpers.DLVertex; import uk.ac.manchester.cs.jfact.helpers.FastSet; import uk.ac.manchester.cs.jfact.helpers.FastSetFactory; import uk.ac.manchester.cs.jfact.helpers.LogAdapter; import uk.ac.manchester.cs.jfact.helpers.StatIndex; import uk.ac.manchester.cs.jfact.helpers.Templates; import uk.ac.manchester.cs.jfact.helpers.UnreachableSituationException; import uk.ac.manchester.cs.jfact.kernel.modelcaches.ModelCacheInterface; import uk.ac.manchester.cs.jfact.kernel.options.JFactReasonerConfiguration; import conformance.Original; import conformance.PortedFrom; /** directed acyclic graph */ @PortedFrom(file = "dlDag.h", name = "DLDag") public class DLDag implements Serializable { private static final long serialVersionUID = 11000L; /** body of DAG */ @PortedFrom(file = "dlDag.h", name = "Heap") private final List<DLVertex> heap = new ArrayList<DLVertex>(); /** all the AND nodes (needs to recompute) */ @PortedFrom(file = "dlDag.h", name = "listAnds") private final FastSet listAnds = FastSetFactory.create(); @Original private final EnumMap<DagTag, DLVTable> indexes = new EnumMap<DagTag, DLVTable>( DagTag.class); /** cache efficiency -- statistic purposes */ @PortedFrom(file = "dlDag.h", name = "nCacheHits") private int nCacheHits; /** size of sort array */ @PortedFrom(file = "dlDag.h", name = "sortArraySize") private int sortArraySize; /** sort index (if necessary). Possible values are Size, Depth, Freq */ @PortedFrom(file = "dlDag.h", name = "iSort") private int iSort; /** whether or not sorting order is ascending */ @PortedFrom(file = "dlDag.h", name = "sortAscend") private boolean sortAscend; /** prefer non-generating rules in OR orderings */ @PortedFrom(file = "dlDag.h", name = "preferNonGen") private boolean preferNonGen; /** flag whether cache should be used */ @PortedFrom(file = "dlDag.h", name = "useDLVCache") private boolean useDLVCache; @PortedFrom(file = "dlDag.h", name = "finalDagSize") private int finalDagSize; @Original private final JFactReasonerConfiguration options; /** * replace existing vertex at index I with a vertex V * * @param i * i * @param v * v * @param C * C */ @PortedFrom(file = "dlDag.h", name = "replaceVertex") public void replaceVertex(int i, DLVertex v, NamedEntry C) { heap.set(i > 0 ? i : -i, v); v.setConcept(C); } /** * @param c * c * @return index of a vertex containing a concept */ @PortedFrom(file = "dlDag.h", name = "index") public int index(NamedEntry c) { for (int i = 0; i < heap.size(); i++) { NamedEntry concept = heap.get(i).getConcept(); if (concept != null && concept.equals(c)) { return i; } } return bpINVALID; } /** * check if given string is correct sort ordering representation * * @param str * str * @return true if correct */ @PortedFrom(file = "dlDag.h", name = "isCorrectOption") private boolean isCorrectOption(String str) { if (str == null) { return false; } int n = str.length(); if (n < 1 || n > 3) { return false; } char Method = str.charAt(0), Order = n >= 2 ? str.charAt(1) : 'a', NGPref = n == 3 ? str .charAt(2) : 'p'; return (Method == 'S' || Method == 'D' || Method == 'F' || Method == 'B' || Method == 'G' || Method == '0') && (Order == 'a' || Order == 'd') && (NGPref == 'p' || NGPref == 'n'); } /** change order of ADD elements wrt statistic */ @PortedFrom(file = "dlDag.h", name = "Recompute") private void recompute() { for (int p = 0; p < listAnds.size(); p++) { heap.get(listAnds.get(p)).sortEntry(this); } } /** clear all DFS info from elements of DAG */ @PortedFrom(file = "dlDag.h", name = "clearDFS") private void clearDFS() { for (DLVertex d : heap) { d.clearDFS(); } } /** * update index corresponding to DLVertex's tag * * @param tag * tag * @param value * value */ @PortedFrom(file = "dlDag.h", name = "updateIndex") public void updateIndex(DagTag tag, int value) { if (!indexes.containsKey(tag)) { return; } indexes.get(tag).addElement(value); if (tag == DagTag.dtCollection || tag == DagTag.dtAnd) { listAnds.add(value); } } /** * add vertex to the end of DAG and calculate it's statistic if necessary * * @param v * v * @return size of heap */ @PortedFrom(file = "dlDag.h", name = "directAdd") public int directAdd(DLVertex v) { int index = index(v.getConcept()); if (index != bpINVALID) { return index; } heap.add(v); // return an index of just added entry return heap.size() - 1; } /** * add vertex to the end of DAG and calculate it's statistic if necessary; * put it into cache * * @param v * v * @return size of heap */ @PortedFrom(file = "dlDag.h", name = "directAddAndCache") public int directAddAndCache(DLVertex v) { int ret = directAdd(v); if (useDLVCache) { updateIndex(v.getType(), ret); } return ret; } /** * @param p * p * @return if given index points to the last DAG entry */ @PortedFrom(file = "dlDag.h", name = "isLast") public boolean isLast(int p) { return p == heap.size() - 1 || -p == heap.size() - 1; } // access methods /** * whether to use cache for nodes * * @param val * val */ @PortedFrom(file = "dlDag.h", name = "setExpressionCache") public void setExpressionCache(boolean val) { useDLVCache = val; } /** * @param i * i * @return access by index */ @PortedFrom(file = "dlDag.h", name = "get") public DLVertex get(int i) { assert isValid(i); return heap.get(i < 0 ? -i : i); } /** @return get size of DAG */ @PortedFrom(file = "dlDag.h", name = "size") public int size() { return heap.size(); } /** @return approximation of the size after query is added */ @PortedFrom(file = "dlDag.h", name = "maxSize") public int maxSize() { return size() + (size() < 220 ? 10 : size() / 20); } /** use SUB options to OR ordering */ @PortedFrom(file = "dlDag.h", name = "setSubOrder") public void setSubOrder() { setOrderOptions(options.getORSortSub()); } /** use SAT options to OR ordering; */ @PortedFrom(file = "dlDag.h", name = "setSatOrder") public void setSatOrder() { setOrderOptions(options.getORSortSat()); } /** * @param p * p * @return cache for given BiPointer (may return null if no cache defined) */ @PortedFrom(file = "dlDag.h", name = "getCache") public ModelCacheInterface getCache(int p) { return get(p).getCache(p > 0); } /** * set cache for given BiPointer; * * @param p * p * @param cache * cache */ @PortedFrom(file = "dlDag.h", name = "setCache") public void setCache(int p, ModelCacheInterface cache) { get(p).setCache(p > 0, cache); } // sort interface /** * merge two given DAG entries * * @param ml * ml * @param p * p */ @PortedFrom(file = "dlDag.h", name = "merge") public void merge(MergableLabel ml, int p) { if (p != bpINVALID && p != bpTOP && p != bpBOTTOM) { get(p).merge(ml); } } /** * @param p * p * @param q * q * @return check if two BPs are of the same sort */ @PortedFrom(file = "dlDag.h", name = "haveSameSort") public boolean haveSameSort(int p, int q) { if (options.isRKG_USE_SORTED_REASONING()) { assert p > 0 && q > 0; // everything has the same label as TOP if (p == 1 || q == 1) { return true; } // if some concepts were added to DAG => nothing to say if (p >= sortArraySize || q >= sortArraySize) { return true; } // check whether two sorts are identical return get(p).getSort().equals(get(q).getSort()); } else { return true; } } // output interface /** * print DAG size and number of cache hits, together with DAG usage * * @param o * o */ @PortedFrom(file = "dlDag.h", name = "PrintStat") public void printStat(LogAdapter o) { o.printTemplate(Templates.PRINT_STAT, heap.size(), nCacheHits); if (options.isRKG_PRINT_DAG_USAGE()) { printDAGUsage(o); } } @Override public String toString() { StringBuilder o = new StringBuilder("\nDag structure"); for (int i = 1; i < size(); ++i) { o.append('\n'); o.append(i); o.append(' '); o.append(get(i)); } o.append('\n'); return o.toString(); } /** * @param v * v * @return bipolar pointer */ @PortedFrom(file = "dlDag.h", name = "add") public int add(DLVertex v) { int ret = useDLVCache ? indexes.get(v.getType()).locate(v) : bpINVALID; if (!isValid(ret)) { ret = directAddAndCache(v); return ret; } // node was found in cache ++nCacheHits; return ret; } /** * @param Options * Options */ public DLDag(JFactReasonerConfiguration Options) { options = Options; /** hash-table for verteces (and, all, LE) fast search */ DLVTable indexAnd = new DLVTable(this); DLVTable indexAll = new DLVTable(this); DLVTable indexLE = new DLVTable(this); indexes.put(DagTag.dtCollection, indexAnd); indexes.put(DagTag.dtAnd, indexAnd); indexes.put(DagTag.dtIrr, indexAll); indexes.put(DagTag.dtForall, indexAll); indexes.put(DagTag.dtLE, indexLE); nCacheHits = 0; useDLVCache = true; finalDagSize = 0; heap.add(new DLVertex(DagTag.dtBad)); heap.add(new DLVertex(DagTag.dtTop)); if (!isCorrectOption(options.getORSortSat()) || !isCorrectOption(options.getORSortSub())) { throw new OWLRuntimeException("DAG: wrong OR sorting options"); } } /** set the DAG size */ @PortedFrom(file = "dlDag.h", name = "setFinalSize") public void setFinalSize() { finalDagSize = size(); setExpressionCache(false); } /** clean query */ @PortedFrom(file = "dlDag.h", name = "removeQuery") public void removeQuery() { for (int i = size() - 1; i >= finalDagSize; --i) { DLVertex v = heap.get(i); switch (v.getType()) { case dtDataType: case dtDataExpr: ((DatatypeEntry) v.getConcept()).setIndex(bpINVALID); break; case dtDataValue: ((LiteralEntry) v.getConcept()).setIndex(bpINVALID); break; case dtPConcept: case dtNConcept: ((Concept) v.getConcept()).clear(); break; default: break; } } resize(heap, finalDagSize); } /** * @param defSat * defSat * @param defSub * defSub */ @PortedFrom(file = "dlDag.h", name = "setOrderDefaults") public void setOrderDefaults(String defSat, String defSub) { assert isCorrectOption(defSat) && isCorrectOption(defSub); options.getLog().print("orSortSat: initial=", options.getORSortSat(), ", default=", defSat); if (options.getORSortSat().charAt(0) == '0') { options.setorSortSat(defSat); } options.getLog().print(", used=", options.getORSortSat(), "\n"); options.getLog().print("orSortSub: initial=", options.getORSortSub(), ", default=", defSub); if (options.getORSortSub().charAt(0) == '0') { options.setorSortSub(defSub); } options.getLog().print(", used=", options.getORSortSub(), "\n"); } /** * @param opt * opt */ @PortedFrom(file = "dlDag.h", name = "setOrderOptions") public void setOrderOptions(String opt) { if (opt.charAt(0) == '0') { return; } sortAscend = opt.charAt(1) == 'a'; preferNonGen = opt.charAt(2) == 'p'; iSort = StatIndex.choose(opt.charAt(0)); recompute(); } @PortedFrom(file = "dlDag.h", name = "computeVertexStat") private void computeVertexStat(DLVertex v, boolean pos, int depth) { // in case of cycle: mark concept as such if (v.isVisited(pos)) { v.setInCycle(pos); return; } v.setVisited(pos); // ensure that the statistic is gather for all sub-concepts of the // expression switch (v.getType()) { case dtCollection: // if pos then behaves like and if (!pos) { break; } // fallthrough //$FALL-THROUGH$ case dtAnd: // check all the conjuncts case dtSplitConcept: for (int q : v.begin()) { int index = createBiPointer(q, pos); DLVertex vertex = get(index); boolean pos2 = index > 0; if (!vertex.isProcessed(pos2)) { computeVertexStat(vertex, pos2, depth + 1); } } break; case dtProj: if (!pos) { break; } // fallthrough //$FALL-THROUGH$ case dtPConcept: case dtNConcept: case dtPSingleton: case dtNSingleton: case dtForall: case dtChoose: case dtLE: // check a single referenced concept int index = createBiPointer(v.getConceptIndex(), pos); DLVertex vertex = get(index); boolean pos2 = index > 0; if (!vertex.isProcessed(pos2)) { computeVertexStat(vertex, pos2, depth + 1); } break; default: // nothing to do break; } v.setProcessed(pos); // here all the necessary statistics is gathered -- use it in the init updateVertexStat(v, pos); } @PortedFrom(file = "dlDag.h", name = "updateVertexStat") private void updateVertexStat(DLVertex v, boolean pos) { int d = 0, s = 1, b = 0, g = 0; if (!v.getType().omitStat(pos)) { if (isValid(v.getConceptIndex())) { updateVertexStat(v, v.getConceptIndex(), pos); } else { for (int q : v.begin()) { updateVertexStat(v, q, pos); } } } // correct values wrt POS d = v.getDepth(pos); switch (v.getType()) { case dtAnd: if (!pos) { ++b; // OR is branching } break; case dtForall: ++d; // increase depth if (!pos) { ++g; // SOME is generating } break; case dtLE: ++d; // increase depth if (!pos) { ++g; // >= is generating } else if (v.getNumberLE() != 1) { ++b; // <= is branching } break; case dtProj: if (pos) { ++b; // projection sometimes involves branching } break; default: break; } v.updateStatValues(d, s, b, g, pos); } /** * gather vertex freq statistics * * @param p * p */ @PortedFrom(file = "dlDag.h", name = "computeVertexFreq") private void computeVertexFreq(int p) { DLVertex v = get(p); boolean pos = p > 0; if (v.isVisited(pos)) { return; } // increment frequence of current vertex v.incFreqValue(pos); v.setVisited(pos); if (v.getType().omitStat(pos)) { return; } // increment frequence of all subvertex if (isValid(v.getConceptIndex())) { computeVertexFreq(v.getConceptIndex(), pos); } else { for (int q : v.begin()) { computeVertexFreq(q, pos); } } } /** * helper for the recursion * * @param v * v * @param p * p * @param pos * pos */ @PortedFrom(file = "dlDag.h", name = "updateVertexStat") private void updateVertexStat(DLVertex v, int p, boolean pos) { DLVertex w = get(p); boolean same = pos == p > 0; // update in-cycle information if (w.isInCycle(same)) { v.setInCycle(pos); } v.updateStatValues(w, same, pos); } /** * helper for the recursion * * @param p * p * @param pos * pos */ @PortedFrom(file = "dlDag.h", name = "computeVertexFreq") private void computeVertexFreq(int p, boolean pos) { computeVertexFreq(createBiPointer(p, pos)); } /** stats collection */ @PortedFrom(file = "dlDag.h", name = "gatherStatistic") public void gatherStatistic() { // gather main statistics for disjunctions for (int i = 0; i < listAnds.size(); i++) { int index = -listAnds.get(i); DLVertex v = get(index); boolean pos = index > 0; if (!v.isProcessed(pos)) { computeVertexStat(v, pos, 0); } } // if necessary -- gather frequency if (options.getORSortSat().charAt(0) != 'F' && options.getORSortSub().charAt(0) != 'F') { return; } clearDFS(); for (int i = size() - 1; i > 1; --i) { if (get(i).getType().isCNameTag()) { computeVertexFreq(i); } } } /** * @param p1 * p1 * @param p2 * p2 * @return true if p1 dlvertex is smaller than p2 dlvertex */ @PortedFrom(file = "dlDag.h", name = "less") public int compare(int p1, int p2) { if (p1 == p2) { return 0; } if (preferNonGen) { if (p1 < 0 && p2 > 0) { return -1; } if (p1 > 0 && p2 < 0) { return 1; } } DLVertex v1 = get(p1); DLVertex v2 = get(p2); int key1 = v1.getStat(iSort); int key2 = v2.getStat(iSort); if (key1 == key2) { return 0; } if (sortAscend) { return key1 - key2; } else { return key2 - key1; } } /** * @param o * debug dag usage */ @PortedFrom(file = "dlDag.h", name = "PrintDAGUsage") public void printDAGUsage(LogAdapter o) { // number of no-used DAG entries int n = 0; // number of total DAG entries int total = heap.size() * 2 - 2; for (DLVertex i : heap) { if (i.getUsage(true) == 0) { ++n; } if (i.getUsage(false) == 0) { ++n; } } o.printTemplate(Templates.PRINTDAGUSAGE, n, n * 100 / total, total); } /** * build the sort system for given TBox * * @param ORM * ORM * @param DRM * DRM */ @PortedFrom(file = "dlDag.h", name = "determineSorts") public void determineSorts(RoleMaster ORM, RoleMaster DRM) { sortArraySize = heap.size(); // init roles R&D sorts List<Role> ORM_Begin = ORM.getRoles(); for (Role p : ORM_Begin) { if (!p.isSynonym()) { mergeSorts(p); } } List<Role> DRM_Begin = DRM.getRoles(); for (Role p : DRM_Begin) { if (!p.isSynonym()) { mergeSorts(p); } } for (int i = 2; i < heap.size(); ++i) { mergeSorts(heap.get(i)); } int sum = 0; for (int i = 2; i < heap.size(); ++i) { MergableLabel lab = heap.get(i).getSort(); lab.resolve(); if (lab.isSample()) { ++sum; } } for (Role p : ORM_Begin) { if (!p.isSynonym()) { MergableLabel lab = p.getDomainLabel(); lab.resolve(); if (lab.isSample()) { ++sum; } } } for (Role p : DRM_Begin) { if (!p.isSynonym()) { MergableLabel lab = p.getDomainLabel(); lab.resolve(); if (lab.isSample()) { ++sum; } } } // we added a temp concept here; don't count it if (sum > 0) { sum--; } options.getLog().printTemplate(Templates.DETERMINE_SORTS, sum > 0 ? sum : "no"); } /** * merge sorts for a given role * * @param R * R */ @PortedFrom(file = "dlDag.h", name = "mergeSorts") private void mergeSorts(Role R) { // associate role domain labels R.mergeSupersDomain(); merge(R.getDomainLabel(), R.getBPDomain()); // also associate functional nodes (if any) for (Role q : R.begin_topfunc()) { merge(R.getDomainLabel(), q.getFunctional()); } } /** * merge sorts for a given vertex * * @param v * v */ @PortedFrom(file = "dlDag.h", name = "mergeSorts") private void mergeSorts(DLVertex v) { switch (v.getType()) { case dtLE: // set R&D for role case dtForall: v.merge(v.getRole().getDomainLabel()); // domain(role)=cur merge(v.getRole().getRangeLabel(), v.getConceptIndex()); break; case dtProj: // projection: equate R&D of R and ProjR, and D(R) with // C v.merge(v.getRole().getDomainLabel()); v.merge(v.getProjRole().getDomainLabel()); merge(v.getRole().getDomainLabel(), v.getConceptIndex()); v.getRole().getRangeLabel() .merge(v.getProjRole().getRangeLabel()); break; case dtIrr: // equate R&D for role v.merge(v.getRole().getDomainLabel()); v.merge(v.getRole().getRangeLabel()); break; case dtAnd: case dtCollection: case dtSplitConcept: for (int q : v.begin()) { merge(v.getSort(), q); } break; case dtNSingleton: case dtPSingleton: case dtPConcept: case dtNConcept: // merge with description case dtChoose: merge(v.getSort(), v.getConceptIndex()); break; case dtDataType: // nothing to do case dtDataValue: case dtDataExpr: case dtNN: break; case dtTop: default: throw new UnreachableSituationException(); } } /** * update sorts for (a,b):R construction * * @param a * a * @param R * R * @param b * b */ @PortedFrom(file = "dlDag.h", name = "updateSorts") public void updateSorts(int a, Role R, int b) { merge(R.getDomainLabel(), a); merge(R.getRangeLabel(), b); } }