grakn.core.server.kb.concept.ConceptUtils.java Source code

Java tutorial

Introduction

Here is the source code for grakn.core.server.kb.concept.ConceptUtils.java

Source

/*
 * GRAKN.AI - THE KNOWLEDGE GRAPH
 * Copyright (C) 2018 Grakn Labs Ltd
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

package grakn.core.server.kb.concept;

import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import grakn.core.concept.Concept;
import grakn.core.concept.answer.ConceptMap;
import grakn.core.concept.type.SchemaConcept;
import grakn.core.server.kb.Schema;
import graql.lang.statement.Variable;

import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.toSet;

public class ConceptUtils {

    /**
     * @param schemaConcepts entry {@link SchemaConcept} set
     * @return top (most general) non-meta {@link SchemaConcept}s from within the provided set
     */
    public static <T extends SchemaConcept> Set<T> top(Set<T> schemaConcepts) {
        return schemaConcepts.stream().filter(t -> Sets.intersection(nonMetaSups(t), schemaConcepts).isEmpty())
                .collect(toSet());
    }

    /**
     * @param schemaConcepts entry {@link SchemaConcept} set
     * @return bottom (most specific) non-meta {@link SchemaConcept}s from within the provided set
     */
    public static <T extends SchemaConcept> Set<T> bottom(Set<T> schemaConcepts) {
        return schemaConcepts.stream()
                .filter(t -> Sets
                        .intersection(t.subs().filter(t2 -> !t.equals(t2)).collect(toSet()), schemaConcepts)
                        .isEmpty())
                .collect(toSet());
    }

    /**
     * @param schemaConcepts entry {@link SchemaConcept} set
     * @return top {@link SchemaConcept}s from within the provided set or meta concept if it exists
     */
    public static <T extends SchemaConcept> Set<T> topOrMeta(Set<T> schemaConcepts) {
        Set<T> concepts = top(schemaConcepts);
        T meta = concepts.stream().filter(c -> Schema.MetaSchema.isMetaLabel(c.label())).findFirst().orElse(null);
        return meta != null ? Collections.singleton(meta) : concepts;
    }

    /**
     * @param schemaConcept input type
     * @return set of all non-meta super types of the role
     */
    public static Set<? extends SchemaConcept> nonMetaSups(SchemaConcept schemaConcept) {
        Set<SchemaConcept> superTypes = new HashSet<>();
        SchemaConcept superType = schemaConcept.sup();
        while (superType != null && !Schema.MetaSchema.isMetaLabel(superType.label())) {
            superTypes.add(superType);
            superType = superType.sup();
        }
        return superTypes;
    }

    /**
     * @param parent type
     * @param child type
     * @param direct flag indicating whether only direct types should be considered
     * @return true if child is a subtype of parent
     */
    private static boolean typesCompatible(SchemaConcept parent, SchemaConcept child, boolean direct) {
        if (parent == null)
            return true;
        if (child == null)
            return false;
        if (direct)
            return parent.equals(child);
        if (Schema.MetaSchema.isMetaLabel(parent.label()))
            return true;
        SchemaConcept superType = child;
        while (superType != null && !Schema.MetaSchema.isMetaLabel(superType.label())) {
            if (superType.equals(parent))
                return true;
            superType = superType.sup();
        }
        return false;
    }

    /**
     * @param parentTypes set of types defining parent, parent defines type constraints to be fulfilled
     * @param childTypes set of types defining child
     * @param direct flag indicating whether only direct types should be considered
     * @return true if type sets are disjoint - it's possible to find a disjoint pair among parent and child set
     */
    public static boolean areDisjointTypeSets(Set<? extends SchemaConcept> parentTypes,
            Set<? extends SchemaConcept> childTypes, boolean direct) {
        return childTypes.isEmpty() && !parentTypes.isEmpty() || parentTypes.stream().anyMatch(parent -> childTypes
                .stream().anyMatch(child -> ConceptUtils.areDisjointTypes(parent, child, direct)));
    }

    /** determines disjointness of parent-child types, parent defines the bound on the child
     * @param parent {@link SchemaConcept}
     * @param child {@link SchemaConcept}
     * @param direct flag indicating whether only direct types should be considered
     * @return true if types do not belong to the same type hierarchy, also:
     * - true if parent is null and
     * - false if parent non-null and child null - parents defines a constraint to satisfy
     */
    public static boolean areDisjointTypes(SchemaConcept parent, SchemaConcept child, boolean direct) {
        return parent != null && child == null
                || !typesCompatible(parent, child, direct) && !typesCompatible(child, parent, direct);
    }

    /**
     * perform an answer merge with optional explanation
     * NB:assumes answers are compatible (concept corresponding to join vars if any are the same)
     *
     * @return merged answer
     */
    public static ConceptMap mergeAnswers(ConceptMap answerA, ConceptMap answerB) {
        if (answerB.isEmpty())
            return answerA;
        if (answerA.isEmpty())
            return answerB;

        Sets.SetView<Variable> varUnion = Sets.union(answerA.vars(), answerB.vars());
        Set<Variable> varIntersection = Sets.intersection(answerA.vars(), answerB.vars());
        Map<Variable, Concept> entryMap = Sets.union(answerA.map().entrySet(), answerB.map().entrySet()).stream()
                .filter(e -> !varIntersection.contains(e.getKey()))
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
        varIntersection.forEach(var -> {
            Concept concept = answerA.get(var);
            Concept otherConcept = answerB.get(var);
            if (concept.equals(otherConcept))
                entryMap.put(var, concept);
            else {
                if (concept.isSchemaConcept() && otherConcept.isSchemaConcept() && !ConceptUtils
                        .areDisjointTypes(concept.asSchemaConcept(), otherConcept.asSchemaConcept(), false)) {
                    entryMap.put(var, Iterables.getOnlyElement(ConceptUtils.topOrMeta(
                            Sets.newHashSet(concept.asSchemaConcept(), otherConcept.asSchemaConcept()))));
                }
            }
        });
        if (!entryMap.keySet().equals(varUnion))
            return new ConceptMap();
        return new ConceptMap(entryMap, answerA.explanation());
    }
}