package uk.ac.manchester.cs.jfact.helpers;
/* 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.kernel.ClassifiableEntry.resolveSynonym;
import static uk.ac.manchester.cs.jfact.kernel.Token.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
import uk.ac.manchester.cs.jfact.kernel.ClassifiableEntry;
import uk.ac.manchester.cs.jfact.kernel.Concept;
import uk.ac.manchester.cs.jfact.kernel.Lexeme;
import uk.ac.manchester.cs.jfact.kernel.NamedEntry;
import uk.ac.manchester.cs.jfact.kernel.Role;
import uk.ac.manchester.cs.jfact.kernel.Token;
/** dl tree factory */
public class DLTreeFactory implements Serializable {
private static final long serialVersionUID = 11000L;
private static final EnumSet<Token> snfCalls = EnumSet.of(TOP, BOTTOM,
CNAME, INAME, RNAME, DNAME, DATAEXPR, NOT, INV, AND, FORALL, LE,
SELF, RCOMPOSITION, PROJFROM, PROJINTO);
/** @return BOTTOM element */
public static DLTree createBottom() {
return new LEAFDLTree(new Lexeme(BOTTOM));
}
/**
* @param R
* R
* @return inverse
*/
public static DLTree createInverse(DLTree R) {
assert R != null;
if (R.token() == INV) {
return R.getChild().copy();
}
if (R.token() == RNAME) {
if (isTopRole(R) || isBotRole(R)) {
// top/bottom roles are inverses of themselves
return R;
}
return new ONEDLTree(new Lexeme(INV), R);
}
throw new UnreachableSituationException();
}
// Semantic Locality checking support. DO NOT used in usual reasoning
/**
* @param dr
* dr
* @return true iff a data range DR is semantically equivalent to TOP.
* FIXME!! good approximation for now
*/
public static boolean isSemanticallyDataTop(DLTree dr) {
return dr.elem().getToken() == TOP;
}
/**
* @param dr
* dr
* @return true iff a data range DR is semantically equivalent to BOTTOM.
* FIXME!! good approximation for now
*/
public static boolean isSemanticallyDataBottom(DLTree dr) {
return dr.elem().getToken() == BOTTOM;
}
/**
* @param dr
* dr
* @param n
* n
* @return true iff the cardinality of a given data range DR is greater than
* N. FIXME!! good approximation for now
*/
@SuppressWarnings("unused")
public static boolean isDataRangeBigEnough(DLTree dr, int n) {
// XXX bug
return true;
}
/**
* simplify universal restriction with top data role
*
* @param dr
* dr
* @return simplified tree
*/
public static DLTree simplifyDataTopForall(DLTree dr) {
// if the filler (dr) is TOP (syntactically or semantically), then the
// forall is top
if (isSemanticallyDataTop(dr)) {
return createTop();
}
// in any other case the attempt to restrict the data domain will fail
return createBottom();
}
/**
* simplify minimal cardinality restriction with top data role
*
* @param n
* n
* @param dr
* dr
* @return simplified tree
*/
public static DLTree simplifyDataTopLE(int n, DLTree dr) {
// if the filler (dr) is BOTTOM (syntactically or semantically), then
// the LE is top
if (isSemanticallyDataBottom(dr)) {
return createTop();
}
// if the size of a filler is smaller than the cardinality, then it's
// always possible to make a restriction
if (!isDataRangeBigEnough(dr, n)) {
return createTop();
}
// in any other case the attempt to restrict the data domain will fail
return createBottom();
}
/**
* @param arguments
* arguments to AND
* @return a construction in the form AND (\neg q_i)
*/
public static DLTree buildDisjAux(List<DLTree> arguments) {
List<DLTree> args = new ArrayList<DLTree>(arguments.size());
for (DLTree i : arguments) {
args.add(DLTreeFactory.createSNFNot(i.copy()));
}
return DLTreeFactory.createSNFAnd(args);
}
/**
* @param C
* C
* @param D
* D
* @return and
*/
public static DLTree createSNFAnd(DLTree C, DLTree D) {
if (C == null) {
return D;
}
if (D == null) {
return C;
}
if (C.isTOP() || D.isBOTTOM()) {
return D;
}
if (D.isTOP() || C.isBOTTOM()) {
return C;
}
return new NDLTree(new Lexeme(AND), C, D);
}
/**
* @param collection
* collection
* @return and
*/
public static DLTree createSNFAnd(Collection<DLTree> collection) {
if (collection.isEmpty()) {
return createTop();
}
if (collection.size() == 1) {
return collection.iterator().next();
}
List<DLTree> l = new ArrayList<DLTree>();
for (DLTree d : collection) {
if (d == null) {
continue;
}
if (d.isBOTTOM()) {
return createBottom();
}
if (d.isAND()) {
l.addAll(d.getChildren());
} else {
l.add(d);
}
}
if (l.isEmpty()) {
return createTop();
}
if (l.size() == 1) {
return l.get(0);
}
return new NDLTree(new Lexeme(AND), l);
}
/**
* @param collection
* collection
* @param ancestor
* ancestor
* @return and
*/
public static DLTree createSNFAnd(Collection<DLTree> collection,
DLTree ancestor) {
boolean hasTop = false;
List<DLTree> l = new ArrayList<DLTree>();
for (DLTree d : collection) {
if (d.isTOP()) {
hasTop = true;
}
if (d.isBOTTOM()) {
return createBottom();
}
if (d.isAND()) {
l.addAll(d.getChildren());
} else {
l.add(d);
}
}
if (hasTop && l.isEmpty()) {
return createTop();
}
if (l.size() == collection.size()) {
// no changes, return the ancestor
return ancestor;
}
return new NDLTree(new Lexeme(AND), l);
}
/**
* create existential restriction of given formulas (\ER.C)
*
* @param R
* R
* @param C
* C
* @return exist R C
*/
public static DLTree createSNFExists(DLTree R, DLTree C) {
// \ER.C . \not\AR.\not C
return createSNFNot(createSNFForall(R, createSNFNot(C)));
}
/**
* @param R
* R
* @param C
* C
* @return for all
*/
public static DLTree createSNFForall(DLTree R, DLTree C) {
if (C.isTOP()) {
return C;
} else if (isBotRole(R)) {
return createTop();
}
if (isTopRole(R) && Role.resolveRole(R).isDataRole()) {
return simplifyDataTopForall(C);
}
return new TWODLTree(new Lexeme(FORALL), R, C);
}
/**
* @param R
* R
* @return role
*/
public static DLTree createRole(Role R) {
return createEntry(R.isDataRole() ? DNAME : RNAME, R);
}
/**
* @param tag
* tag
* @param entry
* entry
* @return entry
*/
public static DLTree createEntry(Token tag, NamedEntry entry) {
return new LEAFDLTree(new Lexeme(tag, entry));
}
/**
* create at-most (LE) restriction of given formulas (max n R.C)
*
* @param n
* n
* @param R
* R
* @param C
* C
* @return at most
*/
public static DLTree createSNFLE(int n, DLTree R, DLTree C) {
if (C.isBOTTOM()) {
// <= n R.F -> T;
return createTop();
}
if (n == 0) {
return createSNFForall(R, createSNFNot(C));
}
if (isBotRole(R)) {
return createTop();
}
if (isTopRole(R) && Role.resolveRole(R).isDataRole()) {
return simplifyDataTopLE(n, C);
}
return new TWODLTree(new Lexeme(LE, n), R, C);
}
/**
* check whether T is a bottom (empty) role
*
* @param t
* tree
* @return true if bottom
*/
public static boolean isBotRole(DLTree t) {
return isRName(t) && t.elem().getNE().isBottom();
}
/**
* check whether T is a top (universal) role
*
* @param t
* tree
* @return true if top role
*/
public static boolean isTopRole(DLTree t) {
return isRName(t) && t.elem().getNE().isTop();
}
/**
* create SELF restriction for role R
*
* @param R
* R
* @return self
*/
public static DLTree createSNFSelf(DLTree R) {
if (isBotRole(R)) {
return createBottom();
// loop on bottom role is always unsat
}
if (isTopRole(R)) {
return createTop();
// top role is reflexive
}
return new ONEDLTree(new Lexeme(SELF), R);
}
/**
* @param n
* n
* @param R
* R
* @param C
* C
* @return at least
*/
public static DLTree createSNFGE(int n, DLTree R, DLTree C) {
if (n == 0) {
return createTop();
}
if (C.isBOTTOM()) {
return C;
} else {
return createSNFNot(createSNFLE(n - 1, R, C));
}
}
/**
* @param C
* C
* @return not
*/
public static DLTree createSNFNot(DLTree C) {
assert C != null;
if (C.isBOTTOM()) {
// \not F = T
return createTop();
}
if (C.isTOP()) {
// \not T = F
return createBottom();
}
if (C.token() == NOT) {
// \not\not C = C
return C.getChild().copy();
}
// general case
return new ONEDLTree(new Lexeme(NOT), C);
}
/**
* @param C
* C
* @param ancestor
* ancestor
* @return not
*/
public static DLTree createSNFNot(DLTree C, DLTree ancestor) {
assert C != null;
if (C.isBOTTOM()) {
// \not F = T
return createTop();
}
if (C.isTOP()) {
// \not T = F
return createBottom();
}
if (C.token() == NOT) {
// \not\not C = C
return C.getChild().copy();
}
// general case
return ancestor;
}
/**
* create disjunction of given formulas
*
* @param C
* C
* @return OR C
*/
public static DLTree createSNFOr(Collection<DLTree> C) {
// C\or D . \not(\not C\and\not D)
List<DLTree> list = new ArrayList<DLTree>();
for (DLTree d : C) {
list.add(createSNFNot(d));
}
return createSNFNot(createSNFAnd(list));
}
/** @return TOP element */
public static DLTree createTop() {
return new LEAFDLTree(new Lexeme(TOP));
}
/**
* @param tree
* tree
* @return inverse
*/
public static DLTree inverseComposition(DLTree tree) {
// XXX this needs to be checked with a proper test
// see rolemaster.cpp, inverseComposition
if (tree.token() == RCOMPOSITION) {
return tree.accept(new ReverseCloningVisitor());
} else {
return new LEAFDLTree(new Lexeme(RNAME, Role.resolveRole(tree)
.inverse()));
}
}
/**
* get DLTree by a given TDE
*
* @param t
* t
* @return wrapped entry
*/
public static DLTree wrap(NamedEntry t) {
return new LEAFDLTree(new Lexeme(DATAEXPR, t));
}
/**
* get TDE by a given DLTree
*
* @param t
* t
* @return unwrapped entry
*/
public static NamedEntry unwrap(DLTree t) {
return t.elem().getNE();
}
/**
* @param t
* t
* @param t1
* t1
* @param t2
* t2
* @return tree with two children
*/
public static DLTree buildTree(Lexeme t, DLTree t1, DLTree t2) {
return new TWODLTree(t, t1, t2);
}
/**
* @param t
* t
* @param t1
* t1
* @return single child tree
*/
public static DLTree buildTree(Lexeme t, DLTree t1) {
return new ONEDLTree(t, t1);
}
/**
* @param t
* t
* @return leaf tree
*/
public static DLTree buildTree(Lexeme t) {
return new LEAFDLTree(t);
}
// check if DL tree is a (data)role name
private static boolean isRName(DLTree t) {
if (t == null) {
return false;
}
if (t.token() == RNAME || t.token() == DNAME) {
return true;
}
return false;
}
/**
* check whether T is an expression in the form (atmost 1 RNAME)
*
* @param t
* t
* @param R
* R
* @return true if functional
*/
public static boolean isFunctionalExpr(DLTree t, NamedEntry R) {
return t != null && t.token() == LE
&& R.equals(t.getLeft().elem().getNE())
&& t.elem().getData() == 1 && t.getRight().isTOP();
}
/**
* @param t
* t
* @return true is SNF
*/
public static boolean isSNF(DLTree t) {
if (t == null) {
return true;
}
if (snfCalls.contains(t.token())) {
return isSNF(t.getLeft()) && isSNF(t.getRight());
}
return false;
}
/**
* @param t1
* t1
* @param t2
* t2
* @return true if t2 is a subtree
*/
public static boolean isSubTree(DLTree t1, DLTree t2) {
if (t1 == null || t1.isTOP()) {
return true;
}
if (t2 == null) {
return false;
}
if (t1.isAND()) {
for (DLTree t : t1.getChildren()) {
if (!isSubTree(t, t2)) {
return false;
}
}
return true;
}
if (t2.isAND()) {
for (DLTree t : t2.getChildren()) {
if (isSubTree(t1, t)) {
return true;
}
}
return false;
}
return t1.equals(t2);
}
/**
* check whether T is U-Role
*
* @param t
* t
* @return true if universal
*/
public static boolean isUniversalRole(DLTree t) {
return isRName(t) && t.elem().getNE().isTop();
}
/**
* @param desc
* desc
* @return true if changes happen
*/
public static boolean replaceSynonymsFromTree(DLTree desc) {
if (desc == null) {
return false;
}
if (desc.isName()) {
ClassifiableEntry entry = (ClassifiableEntry) desc.elem.getNE();
if (entry.isSynonym()) {
entry = resolveSynonym(entry);
if (entry.isTop()) {
desc.elem = new Lexeme(TOP);
} else if (entry.isBottom()) {
desc.elem = new Lexeme(BOTTOM);
} else {
desc.elem = new Lexeme(
((Concept) entry).isSingleton() ? INAME : CNAME,
entry);
}
return true;
} else {
return false;
}
} else {
boolean ret = false;
for (DLTree d : desc.getChildren()) {
ret |= replaceSynonymsFromTree(d);
}
return ret;
}
}
}