
org.aksw.jena_sparql_api.utils.DnfUtils Maven / Gradle / Ivy
package org.aksw.jena_sparql_api.utils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.aksw.commons.util.Pair;
import org.apache.jena.query.Query;
import org.apache.jena.query.QueryFactory;
import org.apache.jena.sparql.algebra.Algebra;
import org.apache.jena.sparql.algebra.Op;
import org.apache.jena.sparql.core.Var;
import org.apache.jena.sparql.expr.E_Equals;
import org.apache.jena.sparql.expr.E_LogicalAnd;
import org.apache.jena.sparql.expr.E_LogicalNot;
import org.apache.jena.sparql.expr.E_LogicalOr;
import org.apache.jena.sparql.expr.E_NotEquals;
import org.apache.jena.sparql.expr.Expr;
import org.apache.jena.sparql.expr.ExprFunction;
import org.apache.jena.sparql.expr.ExprList;
import org.apache.jena.sparql.expr.NodeValue;
//TODO There is already org.apache.jena.sparql.algebra.optimize.TransformFilterDisjunction
public class DnfUtils
{
public static void main(String[] args) {
//String sA = "Select * { ?s ?p ?o . Filter((?s || ?p) && ?o) . }";
//String sA = "Select * { ?s ?p ?o . Filter((?s || ?p) && !(?o || ?g)) . }";
String sA = "Select * { ?s ?p ?o . Filter(?o != ). Optional { { ?x ?y ?z . Filter(?x != && ?x = ?y) . } Union { ?x a ?y . Filter(?x = ) . } } . }";
//String sA = "Select * { ?s ?p ?o . Filter(!(?s = ?p || ?p = ?o && ?s = ?o || ?o = ?s)) . }";
//String sA = "Select * { ?s ?p ?o . Filter(!(?s = ?p || ?j = )) . }";
Query qA = QueryFactory.create(sA);
Op opA = Algebra.compile(qA);
opA = Algebra.toQuadForm(opA);
System.out.println(opA);
// How to deal with union? variables appearing in them
//System.out.println(opA.getClass());
ExprList exprs = FilterUtils.collectExprs(opA, new ExprList());
ExprList proc = eval(exprs);
System.out.println(proc);
List clauses = dnfToClauses(proc);
System.out.println("Mentioned vars:" + proc.getVarsMentioned());
System.out.println("Clauses: " + clauses);
}
public static Expr toExpr(List> ors) {
List tmpOrs = new ArrayList();
for(List ands : ors) {
Expr and = ExprUtils.andifyBalanced(ands);
tmpOrs.add(and);
}
if(ors.size() == 0) {
return NodeValue.FALSE;
}
Expr result = ExprUtils.orifyBalanced(tmpOrs);
return result;
}
public static void addConstantConstraint(Map map, Pair constraint) {
if(constraint == null) {
return;
}
addConstantConstraint(map, constraint.getKey(), constraint.getValue());
}
public static void addConstantConstraint(Map map, Var var, NodeValue nodeValue)
{
if(map.containsKey(var)) {
NodeValue oldValue = map.get(var);
if(oldValue != null && !oldValue.equals(nodeValue)) {
map.put(var, null);
}
}
else {
map.put(var, nodeValue);
}
}
/**
* For each clause determine the constant constraints, and return those,
* that are common to all clauses.
*
* @param dnf
*/
public static Map extractConstantConstraints(Set> dnf) {
Map result = new HashMap();
Iterator> clauseIt = dnf.iterator();
if(!clauseIt.hasNext()) {
return result;
}
Set firstClause = clauseIt.next();
for(Expr expr : firstClause) {
Pair constraint = ExprUtils.extractConstantConstraint(expr);
addConstantConstraint(result, constraint);
}
Set seenVars = new HashSet();
while(clauseIt.hasNext()) {
if(result.isEmpty()) {
return result;
}
Set clause = clauseIt.next();
for(Expr expr : clause) {
Pair constraint = ExprUtils.extractConstantConstraint(expr);
if(constraint == null || !result.containsKey(constraint.getKey())) {
continue;
}
addConstantConstraint(result, constraint);
seenVars.add(constraint.getKey());
}
result.keySet().retainAll(seenVars);
seenVars.clear();
}
return result;
}
public static Set> toSetDnf(Expr expr)
{
List clauses = DnfUtils.toClauses(expr);
Set> dnf = FilterUtils.toSets(clauses);
return dnf;
}
public static boolean isSatisfiable(Set> dnf) {
for(Set clause : dnf) {
if(ClauseUtils.isSatisfiable(clause)) {
return true;
}
}
return false;
}
// FIXME This method removes redundant TRUEs from clauses
public static Expr dnfToExpr(Set> dnf, boolean skipUnsatisfiable) {
Set exprs = new HashSet();
for(Set clause : dnf) {
if(clause.size() > 1 && clause.contains(NodeValue.TRUE)) {
clause.remove(NodeValue.TRUE);
}
if(skipUnsatisfiable) {
if(!ClauseUtils.isSatisfiable(clause)) {
continue;
}
}
exprs.add(ExprUtils.andifyBalanced(clause));
}
Expr result = ExprUtils.orifyBalanced(exprs);
return result != null ? result : NodeValue.FALSE;
}
/**
* Concatenates the sub exressions using Logical_And
*
* and(and(and(0, 1), 2, 3)
*
* @param exprs
* @return
*/
public static Expr andifyLeftSided(ExprList exprs) {
Expr result = null;
for(Expr expr : exprs.getList()) {
result = (result == null) ? expr : new E_LogicalAnd(result, expr);
}
return result;
}
public static List toClauses(Expr expr)
{
Expr evaluated = eval(expr);
return evaluated == null ? null : dnfToClauses(Collections.singleton(evaluated));
}
public static List toClauses(ExprList exprs)
{
Expr evaluated = eval(ExprUtils.andifyBalanced(exprs));
return evaluated == null ? null : dnfToClauses(Collections.singleton(evaluated));
}
/**
* This method only words if the input expressions are in DNF,
* otherwise you will likely get junk back.
*
* @param exprs
* @return
*/
public static List dnfToClauses(Iterable exprs) {
List result = new ArrayList();
for(Expr expr : exprs) {
collectOr(expr, result);
}
return result;
}
public static void collectAnd(Expr expr, ExprList list)
{
if(expr instanceof E_LogicalAnd) {
E_LogicalAnd e = (E_LogicalAnd)expr;
collectAnd(e.getArg1(), list);
collectAnd(e.getArg2(), list);
} else {
list.add(expr);
}
}
public static void collectOr(Expr expr, List list)
{
if(expr instanceof E_LogicalOr) {
E_LogicalOr e = (E_LogicalOr)expr;
collectOr(e.getArg1(), list);
collectOr(e.getArg2(), list);
}
else if(expr instanceof E_LogicalAnd) {
//List ors = new ArrayList();
ExprList ors = new ExprList();
collectAnd(expr, ors);
list.add(ors);
} else {
list.add(new ExprList(expr));
}
}
public static ExprList eval(ExprList exprs) {
System.out.println("ExprList.size = " + exprs.size());
ExprList result = new ExprList();
for(Expr expr : exprs) {
result.add(eval(expr));
}
return result;
}
public static Expr eval(Expr expr)
{
if(expr instanceof ExprFunction) {
return handle((ExprFunction)expr);
} else {
return expr;
}
}
public static boolean containsDirectFuncChild(Expr expr, Class> clazz)
{
if(!(expr instanceof ExprFunction)) {
return false;
}
ExprFunction func = (ExprFunction)expr;
for(Expr arg : func.getArgs()) {
if(arg == null) {
continue;
}
if(clazz.isAssignableFrom(arg.getClass())) {
return true;
}
}
return false;
}
//return new FuncExpr("and", children);
public static Expr handle(ExprFunction expr)
{
//System.out.println("Converting to DNF: [" + expr.getClass() + "]: " + expr);
// not(and(A, B)) -> or(not A, not B)
// not(or(A, B)) -> or(not A, not B)
if(expr instanceof E_LogicalNot) {
Expr tmp = ((E_LogicalNot)expr).getArg();
if (!(tmp instanceof ExprFunction)) {
return expr;
}
ExprFunction child = (ExprFunction)tmp;
Expr newExpr = expr;
if (child instanceof E_LogicalAnd) {
newExpr = new E_LogicalOr(eval(new E_LogicalNot(child.getArg(1))), eval(new E_LogicalNot(child.getArg(2))));
}
else if (child instanceof E_LogicalOr) {
newExpr = new E_LogicalAnd(eval(new E_LogicalNot(child.getArg(1))), eval(new E_LogicalNot(child.getArg(2))));
}
else if (child instanceof E_LogicalNot) { // Remove double negation
newExpr = eval(child.getArg(1));
}
else {
return expr;
}
return eval(newExpr);
}
else if (expr instanceof E_LogicalOr) {
//return expr;
//return eval(expr);
return new E_LogicalOr(eval(expr.getArg(1)), eval(expr.getArg(2)));
}
/* Given:
* (A or B) AND (C x D) becomes:
* (A and (C x D)) OR (B and (c x D))
*
*
* (A or B) AND (C or D)
*
* Goal:
* (A and C) OR (A and D) OR (B and C) OR (B and D)
*
* This method transforms any "or" children of an AND node.
* other nodes are left untouched:
* (A or B) AND (c x D) becomes:
* (A and (c x D)) OR (B and (c x D))
*/
else if (expr instanceof E_LogicalAnd) {
Expr aa = eval(expr.getArg(1));
Expr bb = eval(expr.getArg(2));
E_LogicalOr a = null;
Expr b = null;
if (aa instanceof E_LogicalOr) {
a = (E_LogicalOr)aa;
b = bb;
}
else if(bb instanceof E_LogicalOr) {
a = (E_LogicalOr)bb;
b = aa;
}
if(a == null) {
return new E_LogicalAnd(aa, bb);
} else {
return new E_LogicalOr(eval(new E_LogicalAnd(a.getArg(1), b)), eval(new E_LogicalAnd(a.getArg(2), b)));
}
}
else if (expr instanceof E_NotEquals) { // Normalize (a != b) to !(a = b) --- this makes it easier to find "a and !a" cases
return new E_LogicalNot(eval(new E_Equals(expr.getArg(1), expr.getArg(2))));
}
return expr;
/*
if(expr instanceof E_LogicalNot) {
Expr tmp = ((E_LogicalNot)expr).getArg();
if (!(tmp instanceof ExprFunction))
return expr;
ExprFunction child = (ExprFunction) tmp;
String newFuncName = "";
if (child.getName().equals("and"))
newFuncName = "or";
else if (child.getName().equals("or"))
newFuncName = "and";
else if (child.getName().equals("not")) // Remove double negation
return eval(child.getChildren().get(0));
else
return expr;
FuncExpr result = new FuncExpr(newFuncName, child.getArity());
for (int i = 0; i < child.getArity(); ++i)
result.setChild(i,
new FuncExpr("not", child.getChildren().get(i)));
return eval(result);
}
if (expr.getName().equals("or")) {
List children = new ArrayList();
for (IExpr child : expr.getChildren())
children.add(eval(child));
if (!ExprUtil.containsDirectFuncChild(children, "or"))
return new FuncExpr("or", children);
// flatten or expressions
// or(or(A, B), C) becomes or(A, B, C)
List resultChildren = new ArrayList();
for (IExpr child : children)
if (ExprUtil.isFunc(child, "or"))
resultChildren.addAll(child.getChildren());
else
resultChildren.add(child);
return new FuncExpr("or", resultChildren);
}
if (expr.getName().equals("and")) {
List children = new ArrayList();
for (IExpr child : expr.getChildren())
children.add(eval(child));
// FIXME an and-node must have at least 2 children
// but maybe validation should be done somewhere else
// On the other hand it might be convenient to assume that
// whenever a binary expression only contains a single child
// it should be treated as if there was no operation at all.
// No 'or'-expression => nothing todo
if (!ExprUtil.containsDirectFuncChild(children, "or"))
return new FuncExpr("and", children);
// Collect all expressions
List> tables = new ArrayList>();
for (IExpr child : children) {
if (ExprUtil.isFunc(child, "or"))
tables.add(child.getChildren());
else
tables.add(Collections.singletonList(child));
}
Collection> joinedTable = new JoinIterable(
tables);
FuncExpr result = new FuncExpr("or", joinedTable.size());
// List resultChildren = new ArrayList();
int i = 0;
for (List row : joinedTable) {
IExpr newChild = new FuncExpr("and", row);
result.setChild(i, newChild);
++i;
}
return result;
}
// TODO Auto-generated method stub
return expr;
*/
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy