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 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 java.util.TreeSet;
import org.semanticweb.owlapi.model.IRI;
import org.semanticweb.owlapi.util.MultiMap;
import uk.ac.manchester.cs.jfact.helpers.DLTree;
import uk.ac.manchester.cs.jfact.kernel.TBox.IterableElem;
import uk.ac.manchester.cs.jfact.kernel.actors.ActorImpl;
import uk.ac.manchester.cs.jfact.kernel.dl.IndividualName;
import uk.ac.manchester.cs.jfact.kernel.dl.interfaces.ConceptExpression;
import uk.ac.manchester.cs.jfact.kernel.dl.interfaces.ObjectRoleExpression;
import uk.ac.manchester.cs.jfact.kernel.queryobjects.QRAtom;
import uk.ac.manchester.cs.jfact.kernel.queryobjects.QRConceptAtom;
import uk.ac.manchester.cs.jfact.kernel.queryobjects.QRQuery;
import uk.ac.manchester.cs.jfact.kernel.queryobjects.QRRoleAtom;
import uk.ac.manchester.cs.jfact.kernel.queryobjects.QRVariable;
import uk.ac.manchester.cs.jfact.kernel.queryobjects.VariableFactory;
import conformance.Original;
import conformance.PortedFrom;
/** conjunctive query folding */
@PortedFrom(file = "ConjunctiveQueryFolding.cpp", name = "ConjunctiveQueryFolding")
public class ConjunctiveQueryFolding implements Serializable {
private static final long serialVersionUID = 11000L;
@PortedFrom(file = "ConjunctiveQueryFolding.cpp", name = "pEM")
private final ExpressionManager pEM;
@PortedFrom(file = "ConjunctiveQueryFolding.cpp", name = "VarFact")
private final VariableFactory VarFact = new VariableFactory();
/** map between new vars and original vars */
@PortedFrom(file = "ConjunctiveQueryFolding.cpp", name = "NewVarMap")
private final Map<QRVariable, QRVariable> NewVarMap = new HashMap<QRVariable, QRVariable>();
/** query to term transformation support */
@PortedFrom(file = "ConjunctiveQueryFolding.cpp", name = "NewNominals")
private final Set<ConceptExpression> NewNominals = new HashSet<ConceptExpression>();
@PortedFrom(file = "ConjunctiveQueryFolding.cpp", name = "VarRestrictions")
private final Map<IRI, ConceptExpression> VarRestrictions = new HashMap<IRI, ConceptExpression>();
/**
* @param em
* expression manager
*/
public ConjunctiveQueryFolding(ExpressionManager em) {
pEM = em;
}
/**
* @param expr
* expr
* @return true if expr is nominal
*/
@PortedFrom(file = "ConjunctiveQueryFolding.cpp", name = "isNominal")
public boolean isNominal(ConceptExpression expr) {
return NewNominals.contains(expr);
}
/**
* @param concept
* concept
*/
@Original
public void addNominal(ConceptExpression concept) {
NewNominals.add(concept);
}
/**
* @param query
* query
* @return concept removal
*/
@PortedFrom(file = "ConjunctiveQueryFolding.cpp", name = "RemoveCFromQuery")
public
QRQuery RemoveCFromQuery(QRQuery query) {
// init VR with \top for all free vars
VarRestrictions.clear();
QRQuery ret = new QRQuery();
for (QRVariable v : query.getFreeVars()) {
ret.setVarFree(v);
VarRestrictions.put(v.getName(), pEM.top());
}
// System.out.println("Remove C atoms from the query: before\n" +
// query);
// remove C(v) for free vars
for (QRAtom p : query.getBody().begin()) {
if (p instanceof QRConceptAtom) {
QRConceptAtom atom = (QRConceptAtom) p;
ConceptExpression C = atom.getConcept();
if (atom.getArg() instanceof QRVariable
&& query.isFreeVar((QRVariable) atom.getArg())) {
QRVariable var = (QRVariable) atom.getArg();
VarRestrictions.put(var.getName(),
pEM.and(C, VarRestrictions.get(var.getName())));
} else {
ret.addAtom(atom);
}
} else {
ret.addAtom(p);
}
}
// System.out.println("after\n" + ret);
return ret;
}
@PortedFrom(file = "ConjunctiveQueryFolding.cpp", name = "buildQueryFigure2")
private
void buildQueryFigure2(QRQuery query) {
QRVariable x = VarFact.getNewVar(IRI.create("urn:test#x"));
QRVariable y = VarFact.getNewVar(IRI.create("urn:test#y"));
QRVariable z = VarFact.getNewVar(IRI.create("urn:test#z"));
QRVariable w = VarFact.getNewVar(IRI.create("urn:test#v"));
query.setVarFree(x);
query.setVarFree(y);
ObjectRoleExpression R1 = pEM.objectRole(IRI.create("urn:test#R1"));
ObjectRoleExpression R2 = pEM.objectRole(IRI.create("urn:test#R2"));
ObjectRoleExpression R3 = pEM.objectRole(IRI.create("urn:test#R3"));
ObjectRoleExpression R4 = pEM.objectRole(IRI.create("urn:test#R4"));
ObjectRoleExpression R5 = pEM.objectRole(IRI.create("urn:test#R5"));
ObjectRoleExpression R6 = pEM.objectRole(IRI.create("urn:test#R6"));
query.addAtom(new QRRoleAtom(R1, x, z));
query.addAtom(new QRRoleAtom(R2, x, w));
query.addAtom(new QRRoleAtom(R3, z, y));
query.addAtom(new QRRoleAtom(R4, y, w));
query.addAtom(new QRRoleAtom(R5, z, w));
query.addAtom(new QRRoleAtom(R6, y, y));
}
@PortedFrom(file = "ConjunctiveQueryFolding.cpp", name = "buildSimpleQuery")
private
void buildSimpleQuery(QRQuery query) {
QRVariable x = VarFact.getNewVar(IRI.create("urn:test#x"));
QRVariable y = VarFact.getNewVar(IRI.create("urn:test#y"));
query.setVarFree(x);
query.setVarFree(y);
ObjectRoleExpression R1 = pEM.objectRole(IRI.create("urn:test#R1"));
ObjectRoleExpression R2 = pEM.objectRole(IRI.create("urn:test#R2"));
query.addAtom(new QRRoleAtom(R1, x, y));
query.addAtom(new QRRoleAtom(R2, y, x));
}
@PortedFrom(file = "ConjunctiveQueryFolding.cpp", name = "buildVerySimpleQuery")
private
void buildVerySimpleQuery(QRQuery query) {
QRVariable x = VarFact.getNewVar(IRI.create("urn:test#x"));
query.setVarFree(x);
ObjectRoleExpression R1 = pEM.objectRole(IRI.create("urn:test#R1"));
query.addAtom(new QRRoleAtom(R1, x, x));
}
@PortedFrom(file = "ConjunctiveQueryFolding.cpp", name = "buildVerySimpleQueryLUBM1")
private
void buildVerySimpleQueryLUBM1(QRQuery query) {
QRVariable x = VarFact.getNewVar(IRI.create("urn:test#x"));
query.setVarFree(x);
QRVariable y = VarFact.getNewVar(IRI.create("urn:test#y"));
query.setVarFree(y);
ObjectRoleExpression R1 = pEM.objectRole(IRI.create("urn:test#R1"));
ConceptExpression C1 = pEM.concept(IRI.create("urn:test#C1"));
query.addAtom(new QRRoleAtom(R1, x, y));
query.addAtom(new QRConceptAtom(C1, x));
}
@PortedFrom(file = "ConjunctiveQueryFolding.cpp", name = "buildLUBM2Query")
private void buildLUBM2Query(int n, QRQuery query) {
if (n == 1) {
QRVariable v0 = VarFact.getNewVar(IRI.create("urn:test#v0"));
QRVariable v1 = VarFact.getNewVar(IRI.create("urn:test#v1"));
QRVariable v2 = VarFact.getNewVar(IRI.create("urn:test#v2"));
QRVariable v3 = VarFact.getNewVar(IRI.create("urn:test#v3"));
query.setVarFree(v0);
query.setVarFree(v2);
ConceptExpression Student = pEM.concept(IRI
.create("urn:test#Student"));
ConceptExpression Course = pEM.concept(IRI
.create("urn:test#Course"));
ConceptExpression Faculty = pEM.concept(IRI
.create("urn:test#Faculty"));
ConceptExpression Department = pEM.concept(IRI
.create("urn:test#Department"));
ObjectRoleExpression takesCourse = pEM.objectRole(IRI
.create("urn:test#takesCourse"));
ObjectRoleExpression teacherOf = pEM.objectRole(IRI
.create("urn:test#teacherOf"));
ObjectRoleExpression worksFor = pEM.objectRole(IRI
.create("urn:test#worksFor"));
ObjectRoleExpression memberOf = pEM.objectRole(IRI
.create("urn:test#memberOf"));
query.addAtom(new QRConceptAtom(Student, v0));
query.addAtom(new QRConceptAtom(Course, v1));
query.addAtom(new QRConceptAtom(Faculty, v2));
query.addAtom(new QRConceptAtom(Department, v3));
query.addAtom(new QRRoleAtom(takesCourse, v0, v1));
query.addAtom(new QRRoleAtom(teacherOf, v2, v1));
query.addAtom(new QRRoleAtom(worksFor, v2, v3));
query.addAtom(new QRRoleAtom(memberOf, v0, v3));
}
}
@PortedFrom(file = "ConjunctiveQueryFolding.cpp", name = "createQuery")
private QRQuery createQuery() {
QRQuery query = new QRQuery();
buildLUBM2Query(1, query);
return query;
}
/**
* support for query decycling
*
* @param query
* query
* @param atomIterator
* atomIterator
* @param newAtom
* newAtom
* @param newArg
* newArg
* @param passedAtoms
* passedAtoms
* @return true if connected
*/
@PortedFrom(file = "ConjunctiveQueryFolding.cpp", name = "PossiblyReplaceAtom")
private
boolean PossiblyReplaceAtom(QRQuery query, int atomIterator,
QRAtom newAtom, QRVariable newArg, Set<QRAtom> passedAtoms) {
// System.out.println("Modified code starts here!\nBefore replacing in copy.\n"
// + query);
QRAtom oldAtom = query.getBody().replaceAtom(atomIterator, newAtom);
query.setVarFree(newArg);
// System.out.println("Running Checker");
QueryConnectednessChecker checker = new QueryConnectednessChecker(query);
boolean ret;
if (checker.isConnected()) {
// System.out.println("Connected\nAfter replacing in Query\n" +
// query);
ret = true;
} else {
// System.out.println("Disconnected");
// restore the old query
newAtom = oldAtom;
oldAtom = query.getBody().replaceAtom(atomIterator, oldAtom);
query.getFreeVars().remove(newArg);
ret = false;
}
passedAtoms.add(newAtom);
return ret;
}
/**
* init vars map
*
* @param query
* query
*/
@PortedFrom(file = "ConjunctiveQueryFolding.cpp", name = "initVarMap")
private void initVarMap(QRQuery query) {
NewVarMap.clear();
for (QRAtom p : query.getBody().begin()) {
if (p instanceof QRRoleAtom) {
QRRoleAtom atom = (QRRoleAtom) p;
if (atom.getArg1() instanceof QRVariable) {
NewVarMap.put((QRVariable) atom.getArg1(),
(QRVariable) atom.getArg1());
}
if (atom.getArg2() instanceof QRVariable) {
NewVarMap.put((QRVariable) atom.getArg2(),
(QRVariable) atom.getArg2());
}
}
}
}
/**
* create a new var which is a copy of an existing one
*
* @param old
* old
* @param suffix
* suffix
* @return new var
*/
@PortedFrom(file = "ConjunctiveQueryFolding.cpp", name = "QRVariable")
private QRVariable getNewCopyVar(QRVariable old, int suffix) {
String buf = "_" + suffix;
QRVariable var = VarFact.getNewVar(IRI.create(old.getName() + buf));
NewVarMap.put(var, old);
return var;
}
/**
* @param query
* query
* @return transformed query
*/
@PortedFrom(file = "ConjunctiveQueryFolding.cpp", name = "transformQueryPhase1")
public
QRQuery transformQueryPhase1(QRQuery query) {
Set<QRAtom> passedAtoms = new HashSet<QRAtom>();
int n = 0;
// remove C's
query = RemoveCFromQuery(query);
// clear the map and make identities
initVarMap(query);
// System.out.println("Phase 1 starts");
List<QRAtom> body = query.getBody().begin();
for (int i = 0; i < body.size(); i++) {
QRRoleAtom atom = null;
if (body.get(i) instanceof QRRoleAtom) {
atom = (QRRoleAtom) body.get(i);
}
// atom is a new role atom
if (atom == null || passedAtoms.contains(atom)) {
continue;
}
ObjectRoleExpression role = atom.getRole();
QRVariable arg1 = (QRVariable) atom.getArg1();
QRVariable arg2 = (QRVariable) atom.getArg2();
if (query.getFreeVars().contains(arg2)) {
QRVariable newArg = getNewCopyVar(arg2, ++n);
QRAtom newAtom = new QRRoleAtom(role, arg1, newArg);
if (PossiblyReplaceAtom(query, i, newAtom, newArg, passedAtoms)) {
continue;
}
} else if (query.getFreeVars().contains(arg1)) {
QRVariable newArg = getNewCopyVar(arg1, ++n);
QRAtom newAtom = new QRRoleAtom(role, newArg, arg2);
if (PossiblyReplaceAtom(query, i, newAtom, newArg, passedAtoms)) {
continue;
}
}
}
return query;
}
/**
* @param query
* query
*/
@PortedFrom(file = "ConjunctiveQueryFolding.cpp", name = "deleteFictiveVariables")
public
void deleteFictiveVariables(QRQuery query) {
Set<QRVariable> RealFreeVars = new TreeSet<QRVariable>();
for (QRAtom atomIterator : query.getBody().begin()) {
if (atomIterator instanceof QRRoleAtom) {
QRRoleAtom atom = (QRRoleAtom) atomIterator;
QRVariable arg1 = (QRVariable) atom.getArg1();
QRVariable arg2 = (QRVariable) atom.getArg2();
if (query.isFreeVar(arg1)) {
RealFreeVars.add(arg1);
}
if (query.isFreeVar(arg2)) {
RealFreeVars.add(arg2);
}
}
}
query.setFreeVars(RealFreeVars);
}
/**
* @param v
* v
* @return concept for var
*/
@PortedFrom(file = "ConjunctiveQueryFolding.cpp", name = "createConceptByVar")
public
ConceptExpression createConceptByVar(QRVariable v) {
return VarRestrictions.get(NewVarMap.get(v).getName());
}
/**
* @param query
* query
*/
@PortedFrom(file = "ConjunctiveQueryFolding.cpp", name = "BuildAproximation")
public
void buildApproximation(QRQuery query) {
QueryApproximation app = new QueryApproximation(this, query);
Map<QRVariable, ConceptExpression> approx = new HashMap<QRVariable, ConceptExpression>();
for (QRVariable p : NewVarMap.values()) {
approx.put(p, pEM.top());
}
for (QRVariable v : query.getFreeVars()) {
QRVariable var = NewVarMap.get(v);
approx.put(var,
pEM.and(approx.get(var), app.Assign(query, null, v)));
}
for (Map.Entry<QRVariable, ConceptExpression> e : approx.entrySet()) {
VarRestrictions.put(
e.getKey().getName(),
pEM.and(VarRestrictions.get(e.getKey().getName()),
e.getValue()));
}
}
/**
* @param query
* query
* @return concept expression from query
*/
@PortedFrom(file = "ConjunctiveQueryFolding.cpp", name = "transformQueryPhase2")
public
ConceptExpression transformQueryPhase2(QRQuery query) {
NewNominals.clear();
TermAssigner assigner = new TermAssigner(this, query);
deleteFictiveVariables(query);
QRVariable next = query.getFreeVars().iterator().next();
// System.out.println("Assigner initialised; var: " + next);
return assigner.Assign(query, null, next);
}
@PortedFrom(file = "ConjunctiveQueryFolding.cpp", name = "doQuery")
private void doQuery(QRQuery query, ReasoningKernel kernel,
boolean artificialaBox) {
TQueryToConceptsTransformer transformer = new TQueryToConceptsTransformer(
this, query);
transformer.Run();
transformer.printResult();
kernel.evaluateQuery(transformer.getResult(), artificialaBox);
}
private void
runQueries(ConjunctiveQuerySet queries, ReasoningKernel kernel) {
for (int i = 0; i < queries.size(); i++) {
doQuery(queries.get(i), kernel, queries.isArtificialABox());
}
}
@PortedFrom(file = "ConjunctiveQuery.cpp", name = "Var2I")
private final Map<IRI, Integer> Var2I = new HashMap<IRI, Integer>();
@PortedFrom(file = "ConjunctiveQuery.cpp", name = "I2Var")
private final List<IRI> I2Var = new ArrayList<IRI>();
/**
* fills in variable index
*
* @param query
* query
*/
@PortedFrom(file = "ConjunctiveQuery.cpp", name = "fillVarIndex")
private void fillVarIndex(MultiMap<IRI, ConceptExpression> query) {
int n = 0;
Var2I.clear();
I2Var.clear();
for (IRI p : query.keySet()) {
if (!Var2I.containsKey(p)) {
// new name
Var2I.put(p, n++);
I2Var.add(p);
}
}
assert I2Var.size() == n;
}
/**
* @param query
* query
* @param kernel
* kernel
* @param artificialABox
* true if artificial abox
*/
@PortedFrom(file = "ConjunctiveQuery.cpp", name = "evaluateQuery")
public void evaluateQuery(MultiMap<IRI, ConceptExpression> query,
ReasoningKernel kernel, boolean artificialABox) {
// make index of all vars
fillVarIndex(query);
if (I2Var.isEmpty()) {
return;
}
// for every var: create an expression of vars
List<DLTree> Concepts = new ArrayList<DLTree>();
for (int i = 0; i < I2Var.size(); ++i) {
IRI var = I2Var.get(i);
List<ConceptExpression> list = new ArrayList<ConceptExpression>(
query.get(var));
Concepts.add(kernel.e(pEM.and(list)));
}
fillIVec(kernel, artificialABox);
kernel.getTBox().answerQuery(Concepts);
}
@PortedFrom(file = "ConjunctiveQuery.cpp", name = "getABoxInstances")
private void getABoxInstances(ReasoningKernel kernel, ConceptExpression C,
boolean artificialABox) {
// get all instances of C
ActorImpl a = new ActorImpl();
List<Individual> individuals = new ArrayList<Individual>();
if (artificialABox) {
// HACK: work only for our individualisation of NCIt/etc
a.needConcepts();
kernel.getConcepts(C, false, a, false);
for (ClassifiableEntry p : a.getElements1D()) {
IndividualName ind = pEM.individual(p.getName());
individuals.add((Individual) ind.getEntry());
}
} else {
a.needIndividuals();
kernel.getInstances(C, a);
for (ClassifiableEntry p : a.getElements1D()) {
individuals.add((Individual) p);
}
}
kernel.getTBox().getIV().add(new IterableElem<Individual>(individuals));
}
@PortedFrom(file = "ConjunctiveQuery.cpp", name = "fillIVec")
private void fillIVec(ReasoningKernel kernel, boolean artificialABox) {
kernel.getTBox().getIV().clear();
for (int i = 0; i < I2Var.size(); i++) {
// The i'th var is I2Var[i]; get its concept
ConceptExpression C = VarRestrictions.get(I2Var.get(i));
getABoxInstances(kernel, C, artificialABox);
}
}
/** @return expression manager */
public ExpressionManager getpEM() {
return pEM;
}
/** @return var map */
public Map<QRVariable, QRVariable> getNewVarMap() {
return NewVarMap;
}
}