ai.grakn.graql.internal.reasoner.atom.Atom.java Source code

Java tutorial

Introduction

Here is the source code for ai.grakn.graql.internal.reasoner.atom.Atom.java

Source

/*
 * Grakn - A Distributed Semantic Database
 * Copyright (C) 2016  Grakn Labs Limited
 *
 * Grakn is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Grakn 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Grakn. If not, see <http://www.gnu.org/licenses/gpl.txt>.
 */
package ai.grakn.graql.internal.reasoner.atom;

import ai.grakn.concept.ConceptId;
import ai.grakn.concept.Rule;
import ai.grakn.concept.Type;
import ai.grakn.graql.Var;
import ai.grakn.graql.admin.Atomic;
import ai.grakn.graql.admin.ReasonerQuery;
import ai.grakn.graql.admin.Unifier;
import ai.grakn.graql.admin.VarPatternAdmin;
import ai.grakn.graql.internal.reasoner.utils.ReasonerUtils;
import ai.grakn.graql.internal.reasoner.atom.binary.TypeAtom;
import ai.grakn.graql.internal.reasoner.atom.predicate.IdPredicate;
import ai.grakn.graql.internal.reasoner.atom.predicate.Predicate;
import ai.grakn.graql.internal.reasoner.atom.predicate.ValuePredicate;
import ai.grakn.graql.internal.reasoner.query.ReasonerQueryImpl;
import ai.grakn.graql.internal.reasoner.rule.InferenceRule;
import com.google.common.collect.Sets;

import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static ai.grakn.graql.internal.reasoner.utils.ReasonerUtils.checkTypesCompatible;

/**
 *
 * <p>
 * {@link AtomicBase} extension defining specialised functionalities.
 * </p>
 *
 * @author Kasper Piskorski
 *
 */
public abstract class Atom extends AtomicBase {

    private Type type = null;
    protected ConceptId typeId = null;
    private int basePriority = Integer.MAX_VALUE;
    protected Set<InferenceRule> applicableRules = null;

    protected Atom(VarPatternAdmin pattern, ReasonerQuery par) {
        super(pattern, par);
    }

    protected Atom(Atom a) {
        super(a);
        this.type = a.type;
        this.typeId = a.typeId;
        this.applicableRules = a.applicableRules;
    }

    @Override
    public boolean isAtom() {
        return true;
    }

    /**
     * @return true if the atom corresponds to a atom
     * */
    public boolean isType() {
        return false;
    }

    /**
     * @return true if the atom corresponds to a non-unary atom
     * */
    public boolean isRelation() {
        return false;
    }

    /**
     * @return true if the atom corresponds to a resource atom
     * */
    public boolean isResource() {
        return false;
    }

    /**
     * @return partial substitutions for this atom (NB: instances)
     */
    public Set<IdPredicate> getPartialSubstitutions() {
        return new HashSet<>();
    }

    /**
     * compute base resolution priority of this atom
     * @return priority value
     */
    public int computePriority() {
        return computePriority(
                getPartialSubstitutions().stream().map(IdPredicate::getVarName).collect(Collectors.toSet()));
    }

    /**
     * compute resolution priority based on provided substitution variables
     * @param subbedVars variables having a substitution
     * @return resolution priority value
     */
    public int computePriority(Set<Var> subbedVars) {
        int priority = 0;
        priority += Sets.intersection(getVarNames(), subbedVars).size() * ResolutionStrategy.PARTIAL_SUBSTITUTION;
        priority += isRuleResolvable() ? ResolutionStrategy.RULE_RESOLVABLE_ATOM : 0;
        priority += isRecursive() ? ResolutionStrategy.RECURSIVE_ATOM : 0;

        priority += getTypeConstraints().size() * ResolutionStrategy.GUARD;
        Set<Var> otherVars = getParentQuery().getAtoms().stream().filter(a -> a != this)
                .flatMap(at -> at.getVarNames().stream()).collect(Collectors.toSet());
        priority += Sets.intersection(getVarNames(), otherVars).size() * ResolutionStrategy.BOUND_VARIABLE;
        return priority;
    }

    /**
     * @return measure of priority with which this atom should be resolved
     */
    public int baseResolutionPriority() {
        if (basePriority == Integer.MAX_VALUE) {
            basePriority = computePriority();
        }
        return basePriority;
    }

    public abstract boolean isRuleApplicable(InferenceRule child);

    /**
     * @return set of potentially applicable rules - does shallow (fast) check for applicability
     */
    private Set<Rule> getPotentialRules() {
        Type type = getType();
        return type != null ? type.subTypes().stream().flatMap(t -> t.getRulesOfConclusion().stream())
                .collect(Collectors.toSet()) : ReasonerUtils.getRules(graph());
    }

    /**
     * @return set of applicable rules - does detailed (slow) check for applicability
     */
    public Set<InferenceRule> getApplicableRules() {
        if (applicableRules == null) {
            applicableRules = getPotentialRules().stream().map(rule -> new InferenceRule(rule, graph()))
                    .filter(this::isRuleApplicable).collect(Collectors.toSet());
        }
        return applicableRules;
    }

    @Override
    public boolean isRuleResolvable() {
        return !getApplicableRules().isEmpty();
    }

    @Override
    public boolean isRecursive() {
        if (isResource() || getType() == null)
            return false;
        Type type = getType();
        return getApplicableRules().stream()
                .filter(rule -> rule.getBody().selectAtoms().stream().filter(at -> Objects.nonNull(at.getType()))
                        .filter(at -> checkTypesCompatible(type, at.getType())).findFirst().isPresent())
                .filter(this::isRuleApplicable).findFirst().isPresent();
    }

    /**
     * @return true if the atom can constitute a head of a rule
     */
    public boolean isAllowedToFormRuleHead() {
        return false;
    }

    /**
     * @return true if the atom requires materialisation in order to be referenced
     */
    public boolean requiresMaterialisation() {
        return false;
    }

    /**
     * @return corresponding type if any
     */
    public Type getType() {
        if (type == null && typeId != null) {
            type = getParentQuery().graph().getConcept(typeId).asType();
        }
        return type;
    }

    /**
     * @return type id of the corresponding type if any
     */
    public ConceptId getTypeId() {
        return typeId;
    }

    /**
     * @return value variable name
     */
    public abstract Var getValueVariable();

    /**
     * @return set of predicates relevant to this atom
     */
    public Set<Predicate> getPredicates() {
        Set<Predicate> predicates = new HashSet<>();
        predicates.addAll(getValuePredicates());
        predicates.addAll(getIdPredicates());
        return predicates;
    }

    /**
     * @return set of id predicates relevant to this atom
     */
    public Set<IdPredicate> getIdPredicates() {
        return ((ReasonerQueryImpl) getParentQuery()).getIdPredicates().stream()
                .filter(atom -> containsVar(atom.getVarName())).collect(Collectors.toSet());
    }

    /**
     * @return set of value predicates relevant to this atom
     */
    public Set<ValuePredicate> getValuePredicates() {
        return ((ReasonerQueryImpl) getParentQuery()).getValuePredicates().stream()
                .filter(atom -> atom.getVarName().equals(getValueVariable())).collect(Collectors.toSet());
    }

    /**
     * @return set of types relevant to this atom
     */
    public Set<TypeAtom> getTypeConstraints() {
        Set<TypeAtom> relevantTypes = new HashSet<>();
        //ids from indirect types
        ((ReasonerQueryImpl) getParentQuery()).getTypeConstraints().stream().filter(atom -> atom != this)
                .filter(atom -> containsVar(atom.getVarName())).forEach(relevantTypes::add);
        return relevantTypes;
    }

    /**
     * @return neighbours of this atoms, i.e. atoms connected to this atom via shared variable
     */
    public Stream<Atom> getNeighbours() {
        return getParentQuery().getAtoms().stream().filter(Atomic::isAtom).map(at -> (Atom) at)
                .filter(at -> at != this)
                .filter(at -> !Sets.intersection(this.getVarNames(), at.getVarNames()).isEmpty());
    }

    /**
     * @return set of constraints of this atom (predicates + types) that are not selectable
     */
    public Set<Atomic> getNonSelectableConstraints() {
        Set<Atom> types = getTypeConstraints().stream().filter(at -> !at.isSelectable())
                .collect(Collectors.toSet());
        return Sets.union(types, getPredicates());
    }

    public Set<IdPredicate> getUnmappedIdPredicates() {
        return new HashSet<>();
    }

    public Set<TypeAtom> getUnmappedTypeConstraints() {
        return new HashSet<>();
    }

    public Set<TypeAtom> getMappedTypeConstraints() {
        return new HashSet<>();
    }

    public Set<Unifier> getPermutationUnifiers(Atom headAtom) {
        return new HashSet<>();
    }

    /**
     * infers types (type, role types) fo the atom if applicable/possible
     */
    public void inferTypes() {
    }

    /**
     * rewrites the atom to one with user defined name
     * @return pair of (rewritten atom, unifiers required to unify child with rewritten atom)
     */
    public Atom rewriteToUserDefined() {
        return this;
    }

    /**
     * find unifier with parent atom
     * @param parentAtom atom to be unified with
     * @return unifier
     */
    public abstract Unifier getUnifier(Atom parentAtom);
}