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

fr.inria.edelweiss.kgraph.logic.Entailment Maven / Gradle / Ivy

package fr.inria.edelweiss.kgraph.logic;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;

import fr.inria.acacia.corese.api.IDatatype;
import fr.inria.acacia.corese.cg.datatype.DatatypeMap;
import fr.inria.acacia.corese.triple.parser.NSManager;
import fr.inria.edelweiss.kgram.api.core.Edge;
import fr.inria.edelweiss.kgram.api.core.Entity;
import fr.inria.edelweiss.kgram.api.core.ExpType;
import fr.inria.edelweiss.kgram.api.core.Node;
import fr.inria.edelweiss.kgraph.api.Engine;
import fr.inria.edelweiss.kgraph.core.Graph;
import org.apache.log4j.Logger;

/**
 * RDFS Entailment
 *
 * rdfs:domain rdfs:range rdfs:subPropertyOf rdfs:subClassOf
 * owl:SymmetricProperty owl:inverseOf
 *
 * subPropertyOf & subClassOf are not transitive in the graph but their
 * instances are typed according to transitivity
 *
 * @author Olivier Corby, Edelweiss INRIA 2010
 *
 */
public class Entailment implements Engine {

    private static Logger logger = Logger.getLogger(Entailment.class);
    private static final String S_TYPE = RDF.TYPE;
    private static final String S_BLI = RDF.BLI;
    private static final String S_PROPERTY = RDF.PROPERTY;
    private static final String S_RDFS = RDFS.RDFS;
    private static final String S_RESOURCE = RDFS.RESOURCE;
    private static final String S_SUBCLASSOF = RDFS.SUBCLASSOF;
    private static final String S_SUBPROPERTYOF = RDFS.SUBPROPERTYOF;
    private static final String S_MEMBER = RDFS.MEMBER;
    private static final String S_MEMBERSHIP = RDFS.MEMBERSHIP;
    private static final String S_THING = OWL.THING;
    static final String W3C = "http://www.w3.org";
    public static final String KGRAPH2 = "http://ns.inria.fr/edelweiss/2010/kgraph#";
    public static final String KGRAPH = ExpType.KGRAM;
    public static String DEFAULT = KGRAPH + "default";
    public static String ENTAIL = KGRAPH + "entailment";
    public static String RULE = KGRAPH + "rule";
    public static String[] GRAPHS = {DEFAULT, ENTAIL, RULE};
    public static final String XSD = "http://www.w3.org/2001/XMLSchema#";
    // take literal range into account in loader
    public static final String DATATYPE_INFERENCE = KGRAPH + "datatype";
    // false: do not duplicate RDFS entailment in kg:entailment graph
    public static final String DUPLICATE_INFERENCE = KGRAPH + "duplicate";
    static final int UNDEF = -1;
    static final int SUBCLASSOF = 0;
    static final int SUBPROPERTYOF = 1;
    static final int DOMAIN = 2;
    static final int RANGE = 3;
    static final int TYPE = 4;
    static final int MEMBER = 5;
    static final int INVERSEOF = 6;
    static final int RDF_ENTAIL = 7;
    static final int SYMMETRIC = 30;
    public static boolean trace = false;
    Signature domain, range, inverse, symetric, subproperty;
    Graph graph; //, target;
    List targetList;
    Node hasType, subClassOf, graphNode;
    Edge last, current;
    Hashtable count;
    Hashtable keyword;
    boolean // generate rdf:type wrt rdfs:subClassOf
            isSubClassOf = !true,
            isSubPropertyOf = true,
            // entailments in default graph
            isDefaultGraph = true,
            // infer datatype from property range for literal (à la corese)
            isDatatypeInference = false,
            isDomain = true,
            isRange = true,
            isRDF = true,
            isMember = true,
            isActivate = true;
    // deprecated
    boolean recurse = false,
            isDebug = false;

    class Signature extends Hashtable> {

        void define(Node pred, Node value) {
            List list = get(pred);
            if (list == null) {
                list = new ArrayList();
                put(pred, list);
            }
            if (!list.contains(value)) {
                list.add(value);
            }
        }
    }

    public static Entailment create(Graph g) {
        return new Entailment(g);
    }

    Entailment(Graph g) {
        graph = g;
        //targetList= new ArrayList();
        symetric = new Signature();
        inverse = new Signature();
        domain = new Signature();
        range = new Signature();
        subproperty = new Signature();
        keyword = new Hashtable();
        count = new Hashtable();
        hasType = graph.addProperty(S_TYPE);
        defProperty();
    }

    public void onClear() {
        clear();
    }

    void clear() {
        symetric.clear();
        inverse.clear();
        domain.clear();
        range.clear();
        subproperty.clear();
        hasType = graph.addProperty(S_TYPE);
    }

    // use RDFS metamodel
    void defProperty() {
        defEntity(RDF.RDF, RDF_ENTAIL);
        defEntity(RDF.TYPE, TYPE);
        defEntity(RDFS.SUBCLASSOF, SUBCLASSOF);
        defEntity(RDFS.SUBPROPERTYOF, SUBPROPERTYOF);
        defEntity(RDFS.DOMAIN, DOMAIN);
        defEntity(RDFS.RANGE, RANGE);
        defEntity(RDFS.MEMBER, MEMBER);
        defEntity(OWL.INVERSEOF, INVERSEOF);
        defEntity(OWL.SYMMETRIC, SYMMETRIC);

    }

    public void defEntity(String name, int type) {
        keyword.put(name, type);
    }

    Integer getType(String name) {
        Integer type = keyword.get(name);
        if (type == null) {
            type = UNDEF;
        }
        return type;
    }

    public void set(String name, boolean b) {

        switch (getType(name)) {
            case RDF_ENTAIL:
                rdfEntailment();
                break;
            case SUBCLASSOF:
                isSubClassOf = b;
                break;
            case SUBPROPERTYOF:
                isSubPropertyOf = b;
                break;
            case DOMAIN:
                isDomain = b;
                break;
            case RANGE:
                isRange = b;
                break;
            case MEMBER:
                isMember = b;
                break;

            default:
                if (name.equals(DATATYPE_INFERENCE)) {
                    isDatatypeInference = b;
                } else if (name.equals(ENTAIL)) {
                    isDefaultGraph = b;
                }
        }
    }

    void rdfEntailment() {
        set(RDFS.SUBCLASSOF, false);
        set(RDFS.SUBPROPERTYOF, false);
        set(RDFS.DOMAIN, false);
        set(RDFS.RANGE, false);
        set(RDFS.MEMBER, false);
    }

    public boolean isDatatypeInference() {
        return isDatatypeInference;
    }

    public boolean isSubClassOfInference() {
        return isSubClassOf;
    }

    public void setDebug(boolean b) {
        isDebug = b;
    }

    /**
     * clear tables of meta statements (domain, range, etc.) fill these tables
     * with current graph
     */
    public void onDelete() {
        reset();
    }

    void reset() {
        clear();
        define();
    }

    /**
     * Record definitions corresponding to ontological edges from graph: pp
     * rdfs:range rr use case: add Entailment on existing graph use case:
     * redefine after delete
     */
    public void init() {
        define();
    }

    void define() {

        for (Node pred : graph.getSortedProperties()) {
            boolean isType = isType(pred);

            for (Entity ent : graph.getEdges(pred)) {
                Edge edge = ent.getEdge();
                boolean isMeta = define(ent.getGraph(), ent.getEdge());
                if (!isMeta) {
                    if (isType) {
                        // continue for rdf:type owl:Symmetric
                    } else {
                        break;
                    }
                }
            }
        }
    }

    public boolean process() {
        int size = graph.size();
        entail();
        return graph.size() > size;
    }

    int entail() {
        int nb = inference();
        return nb;
    }

    /**
     * Internal process of entailed edge
     */
    void recordWithoutEntailment(Node gNode, Edge ee, Entity edge) {
        if (! graph.exist(edge)){
            targetList.add(edge);
        }       
    }

    void recordWithEntailment(Node gNode, Edge ee, Entity edge) {
        recordWithoutEntailment(gNode, ee, edge);
        define(gNode, edge.getEdge());
    }

    Entity create(Node src, Node sub, Node pred, Node obj) {
        return graph.create(src, sub, pred, obj);
    }

    /**
     * Store property domain, range, subPropertyOf, symmetric, inverse
     */
    public void onInsert(Node gNode, Edge edge) {
        define(gNode, edge);
    }

    boolean define(Node gNode, Edge edge) {
        //if (! edge.getLabel().startsWith(W3C)) return;
        boolean isMeta = true;
//		if (edge.getNode(1).isBlank()){
//			// DRAFT: do nothing
//		}
//		else 
        switch (getType(edge.getLabel())) {

            case TYPE:
                if (getType(edge.getNode(1).getLabel()) == SYMMETRIC) {
                    symetric.define(edge.getNode(0), edge.getNode(0));
                }
                break;

            case DOMAIN:
                domain.define(edge.getNode(0), edge.getNode(1));
                break;

            case RANGE:
                range.define(edge.getNode(0), edge.getNode(1));
                break;

            case SUBPROPERTYOF:
                subproperty.define(edge.getNode(0), edge.getNode(1));
                break;

            case SUBCLASSOF:
                subClassOf = edge.getEdgeNode();
                break;

            case INVERSEOF:
                inverse.define(edge.getNode(0), edge.getNode(1));
                inverse.define(edge.getNode(1), edge.getNode(0));
                break;

            default:
                isMeta = false;


        }


        return isMeta;
    }

    /**
     * Add RDFS entailment to the graph, given edge and RDFS Schema
     *
     * Entail domain, range, subPropertyOf, symmetric, inverse
     *
     */
    public void entail(Node gNode, Edge edge) {
        property(gNode, edge);
        signature(gNode, edge);
        subsume(gNode, edge);
    }

    /**
     * graph creates new property pNode infer: pNode rdf:type rdf:Property TODO:
     * BUG: concurrent modification while entailment TODO: move at entailment
     * time
     */
    void defProperty(Node pNode) {
        Node gNode = graph.addGraph(ENTAIL);
        Node tNode = graph.addResource(S_PROPERTY);
        graph.add(pNode);
        Entity ee = create(gNode, pNode, hasType, tNode);
        recordWithoutEntailment(gNode, null, ee);

        if (isMember && pNode.getLabel().startsWith(S_BLI)) {
            // rdf:_i rdfs:subPropertyOf rdfs:member
            tNode = graph.addResource(S_MEMBER);
            Node sub = graph.addProperty(S_SUBPROPERTYOF);
            ee = create(gNode, pNode, sub, tNode);
            recordWithEntailment(gNode, null, ee);

            // rdf:_i rdf:type rdfs:ContainerMembershipProperty
            Node mem = graph.addResource(S_MEMBERSHIP);
            ee = create(gNode, pNode, hasType, mem);
            recordWithoutEntailment(gNode, null, ee);
        }
    }

    void property(Node gNode, Edge edge) {
        inverse(gNode, edge, symetric);
        inverse(gNode, edge, inverse);

        subproperty(gNode, edge);
    }

    void inverse(Node gNode, Edge edge, Signature table) {
        Node pred = edge.getEdgeNode();
        List list = table.get(pred);
        if (list != null) {
            for (Node type : list) {
                Entity ee = create(gNode, edge.getNode(1), type, edge.getNode(0));
                recordWithoutEntailment(gNode, edge, ee);
            }
        }
    }

    void subproperty(Node gNode, Edge edge) {
        if (!isSubPropertyOf) {
            return;
        }

        Node pred = edge.getEdgeNode();
        List list = subproperty.get(pred);
        if (list != null) {
            for (Node sup : list) {
                Entity ee = create(gNode, edge.getNode(0), sup, edge.getNode(1));
                recordWithoutEntailment(gNode, edge, ee);
                if (isMeta(sup)) {
                    define(gNode, ee.getEdge());
                }
            }
        }
    }

    /**
     * Man intersectionOf (Human Male) Human unionOf (Man Woman) edge: Man
     * intersectionOf _:b
     */
    void interunion(Node gNode, Edge edge) {
        if (edge.getNode(0).isBlank()) {
            return;
        }

        if (hasLabel(edge, OWL.INTERSECTIONOF)) {
            interunion(gNode, edge, false);
        } else if (hasLabel(edge, OWL.UNIONOF)) {
            interunion(gNode, edge, true);
        }
    }

    void interunion(Node gNode, Edge edge, boolean union) {
        Node node = edge.getNode(0);
        Node bnode = edge.getNode(1);
        List list = graph.getList(bnode);

        for (Node elem : list) {
            if (!elem.isBlank()) {
                Entity ee;
                if (union) {
                    ee = create(gNode, elem, subClassOf, node);
                } else {
                    ee = create(gNode, node, subClassOf, elem);
                }
                recordWithoutEntailment(gNode, edge, ee);
            }
        }
    }

    void signature(Node gNode, Edge edge) {
        domain(gNode, edge);
        range(gNode, edge);
    }

    void domain(Node gNode, Edge edge) {
        if (isDomain) {
            Node pred = edge.getEdgeNode();
            infer(gNode, edge, domain.get(pred), 0);
        }
    }

    void range(Node gNode, Edge edge) {
        if (isRange && graph.isIndividual(edge.getNode(1))) {
            Node pred = edge.getEdgeNode();
            infer(gNode, edge, range.get(pred), 1);
        }
    }

    void subsume(Node gNode, Edge edge) {
        // infer types using subClassOf
        if (isSubClassOf && isType(edge)) {
            infer(gNode, edge);
        }
    }

    boolean differ(Edge edge, Edge last) {
        if (last == null) {
            return true;
        }
        return !(edge.getNode(0).same(last.getNode(0))
                && edge.getEdgeNode().same(last.getEdgeNode()));
    }

    /**
     * signature
     */
    void infer(Node gNode, Edge edge, List list, int i) {
        Node node = edge.getNode(i);
        IDatatype dt = (IDatatype) node.getValue();

        if (i == 1 && dt.isLiteral()) {
            return;
        }

        if (list != null) {
            for (Node type : list) {
                Entity ee = create(gNode, node, hasType, type);
                recordWithoutEntailment(gNode, edge, ee);
            }
        }
    }

    /**
     * edge: in:aa rdf:type ex:Person infer super classes
     */
    void infer(Node gNode, Edge edge) {
        if (subClassOf == null) {
            return;
        }

        Iterable list = graph.getEdges(subClassOf, edge.getNode(1), 0);

        if (list != null) {
            for (Entity type : list) {
                Entity ee =
                        create(gNode, edge.getNode(0), hasType, type.getEdge().getNode(1));
                recordWithoutEntailment(gNode, edge, ee);
            }
        }
    }

    public List getSubClass(Node node) {
        ArrayList list = new ArrayList();
        getClasses(node, list, true);
        return list;
    }

    public List getSuperClass(Node node) {
        ArrayList list = new ArrayList();
        getClasses(node, list, false);
        return list;
    }

    /**
     * TODO: track loop
     */
    public void getClasses(Node node, List list, boolean isSubClass) {
        Iterable it =
                graph.getEdges(graph.getPropertyNode(S_SUBCLASSOF), node, (isSubClass) ? 1 : 0);

        if (it == null) {
            return;
        }

        for (Entity ent : it) {
            Node nn = ent.getEdge().getNode((isSubClass) ? 0 : 1);
            if (!list.contains(nn)) {
                list.add(nn);
                getClasses(nn, list, isSubClass);
            }
        }
    }

    class Table extends Hashtable {

        boolean visited(Node node) {
            return containsKey(node);
        }

        void enter(Node node) {
            put(node, node);
        }

        void leave(Node node) {
            remove(node);
        }
    }

    public boolean isSubClassOf(Node node, Node sup) {
        if (node.same(sup)) {
            return true;
        }
        Node pred = graph.getPropertyNode(S_SUBCLASSOF);
        if (pred == null) {
            return false;
        }
        return isSubOf(pred, node, sup, new Table());
    }

    public boolean isSubPropertyOf(Node node, Node sup) {
        if (node.same(sup)) {
            return true;
        }
        Node pred = graph.getPropertyNode(S_SUBPROPERTYOF);
        if (pred == null) {
            return false;
        }
        return isSubOf(pred, node, sup, new Table());
    }

    /**
     * Take loop into account
     */
    boolean isSubOf(Node pred, Node node, Node sup, Table t) {
        Iterable it = graph.getEdges(pred, node, 0);

        if (it == null) {
            return false;
        }

        t.enter(node);

        for (Entity ent : it) {
            Node nn = ent.getEdge().getNode(1);
            if (nn.same(sup)) {
                return true;
            }
            if (nn.same(node)) {
                continue;
            }
            if (t.visited(nn)) {
                continue;
            }
            if (isSubOf(pred, nn, sup, t)) {
                return true;
            }
        }

        t.leave(node);

        return false;
    }

    public boolean isEntailed(Node source) {
        return isEntailment(source) || isRule(source);
    }

    public boolean isEntailment(Node source) {
        return hasLabel(source, ENTAIL);
    }

    public boolean isRule(Node source) {
        return hasLabel(source, RULE);
    }

    public boolean isRule(Entity e) {
        return hasLabel(e.getGraph(), RULE);
    }

    public boolean isType(Edge edge) {
        return getType(edge.getEdgeNode().getLabel()) == TYPE;
    }

    public boolean isType(Node pred) {
        return getType(pred.getLabel()) == TYPE;
    }

    public boolean isSubClassOf(Node pred) {
        return getType(pred.getLabel()) == SUBCLASSOF;
    }

    public boolean isSubClassOf(Edge edge) {
        return getType(edge.getEdgeNode().getLabel()) == SUBCLASSOF;
    }

    boolean hasLabel(Edge edge, String type) {
        return edge.getLabel().equals(type);
    }

    boolean hasLabel(Node node, String type) {
        return node.getLabel().equals(type);
    }

    public boolean isSymmetric(Edge edge) {
        return symetric.containsKey(edge.getEdgeNode());
    }

    public boolean isTopClass(Node node) {
        return node.getLabel().equals(S_RESOURCE)
                || node.getLabel().equals(S_THING);
    }

    /**
     * *******************
     *
     * Entailments
     *
     ********************
     */
    int inference() {
        targetList = new ArrayList();
        int count = 0;

        // extension of metamodel
        meta();
        List lDef = copy(targetList, graph);
        count += lDef.size();

        targetList = new ArrayList();

        // first: entail for all edges in graph
        // and add infered edges in fresh target graph
        graphEntail();

        count += loop();

        return count;
    }

    /**
     * Complementary entailment from rules on new edge list
     */
    public int entail(List list) {
        inference(list);
        return loop();
    }

    /**
     * second: loop on target to infer new edges until no new edges are infered
     * new edges are added in list
     */
    int loop() {
        int count = 0;

        boolean any = true;

        while (any) {

            any = false;

            // Try to add in graph the entailed edges
            // already existing edges are rejected
            // accepted edges are also put in list

            List list = copy(targetList, graph);

            // loop on new infered edges
            if (list.size() > 0) {
                any = true;
                inference(list);
            }

            count += list.size();

        }

        return count;
    }

    void inference(List list) {
        if (isDebug) {
            logger.info("Entail list: " + list.size());
        }
        targetList = new ArrayList();
        
        Entity prev = null;

        for (Entity ent : list) {

            Edge edge = ent.getEdge();
            Node gg = getGraph(ent);

            property(gg, edge);
            subsume(gg, edge);
            signature(gg, edge);

            if (prev == null) {
                prev = ent;
                defProperty(ent.getEdge().getEdgeNode());
            } else if (prev.getEdge().getEdgeNode() != ent.getEdge().getEdgeNode()) {
                defProperty(ent.getEdge().getEdgeNode());
            }
        }
    }

    /**
     * Copy entailed edges into graph
     */
    List copy(List list, Graph to) {
        return to.copy(list);
    }

    /**
     * Graph where entailed edges are stored May be default or edge graph
     */
    Node getGraph(Entity ent) {
        if (isDefaultGraph) {
            if (graphNode == null) {
                graphNode = graph.addGraph(ENTAIL);
            }
            return graphNode;
        }
        return ent.getGraph();
    }

    /**
     * First loop on whole graph that was just loaded Entailed edges stored in
     * fresh target graph TODO: defProperty() for rule entail
     */
    void graphEntail() {
        if (isDebug) {
            //logger.info(graph.getIndex());
        }
        for (Node pred : graph.getProperties()) {
            if (isDebug) {
                logger.info("Entail: " + pred + " " + graph.size(pred));
            }
            Entity pdomain = null, prange = null;
            boolean isFirst = true;

            for (Entity ent : graph.getEdges(pred)) {

                if (isFirst) {
                    // ?p rdf:type rdf:Property
                    defProperty(pred);
                }

                Edge edge = ent.getEdge();
                Node gg = getGraph(ent);

                property(gg, edge);
                subsume(gg, edge);


                //signature(gg, edge);

                if (isFirst) {
                    isFirst = false;
                    signature(gg, edge);
                    pdomain = ent;
                    prange = ent;
                } else {

                    if (pdomain.getEdge().getNode(0) != ent.getEdge().getNode(0)
                            || !isDefaultGraph) {
                        domain(gg, edge);
                        pdomain = ent;
                    }

                    if (prange.getEdge().getNode(1) != ent.getEdge().getNode(1)
                            || !isDefaultGraph) {
                        range(gg, edge);
                        prange = ent;
                    }
                }


            }
        }
    }

    /**
     *
     * Meta model refinement
     *
     * Currently: wrt rdfs:subPropertyOf rdfs:property only direct subproperties
     * only
     *
     * codomain rdfs:subPropertyOf rdfs:range && pp codomain rr => pp range rr
     *
     * TODO: subProperty at depth more than 1 hasType inverseOf rdf:type
     * MySymmetric subClassOf owl:Symmetric
     *
     */
    void meta() {
        Node subprop = graph.getPropertyNode(S_SUBPROPERTYOF);
        if (subprop != null) {
            for (Entity ent : graph.getEdges(subprop)) {
                Edge edge = ent.getEdge();
                if (isMeta(edge.getNode(1))) {
                    // codomain subPropertyOf rdfs:range
                    Node pred = edge.getNode(0);
                    for (Entity meta : graph.getEdges(pred)) {
                        // entail: pp codomain dd
                        subproperty(getGraph(meta), meta.getEdge());
                    }
                }
            }
        }
    }

    boolean isMeta(Node pred) {
        return pred.getLabel().startsWith(S_RDFS);
    }

    public String getRange(String pred) {
        Node node = graph.getPropertyNode(pred);
        if (node == null) {
            return null;
        }
        List list = range.get(node);
        if (list == null) {
            return null;
        }
        return list.get(0).getLabel();
    }

    public boolean typeCheck() {
        boolean res = true;
        NSManager nsm = NSManager.create();
        for (Node prop : graph.getProperties()) {
            boolean isDatatype = false;
            String range = getRange(prop.getLabel());
            if (range != null) {
                isDatatype = DatatypeMap.isDatatype(range);
            }
            for (Entity ent : graph.getEdges(prop)) {
                IDatatype dt = (IDatatype) ent.getNode(1).getValue();
                if (range == null) {
                    if (DatatypeMap.isUndefined(dt)) {
                        logger.warn("Datatype error: " + dt);
                        res = false;
                    }
                } else {
                    boolean b = check(dt, range, isDatatype);
                    if (!b) {
                        logger.warn("Range error: " + dt + " " + nsm.toPrefix(range));
                    }
                    res = res && b;
                }
            }
        }
        return res;
    }

    boolean check(IDatatype dt, String range, boolean isDatatype) {
        if (DatatypeMap.isUndefined(dt)) {
            return false;
        }
        if (isDatatype) {
            if (dt.isLiteral()) {
                return DatatypeMap.check(dt, range);
            }
        } else if (dt.isLiteral()) {
            return false;
        }

        return true;
    }

    void reject(Edge edge) {
        Integer val = count.get(edge.getEdgeNode());
        if (val == null) {
            val = 0;
        }
        count.put(edge.getEdgeNode(), ++val);
    }

    public String display() {
        String str = "";
        for (Node pred : count.keySet()) {
            str += pred + ": " + count.get(pred) + "\n";
        }
        return str;
    }

    public void setActivate(boolean b) {
        isActivate = b;
    }

    public boolean isActivate() {
        return isActivate;
    }

    public void remove() {
        graph.clear(ENTAIL, true);
    }

    public int type() {
        return RDFS_ENGINE;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy