All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.aksw.jena_sparql_api.utils.DnfUtils Maven / Gradle / Ivy

There is a newer version: 3.17.0-1
Show newest version
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