/** * * @param rule The rule to be validated * @return Error messages if the when or then of a rule refers to a non existent type */ static Set<String> validateRuleSchemaConceptExist(GraknTx graph, Rule rule){ Set<String> errors = new HashSet<>(); errors.addAll(checkRuleSideInvalid(graph, rule, Schema.VertexProperty.RULE_WHEN, rule.when())); errors.addAll(checkRuleSideInvalid(graph, rule, Schema.VertexProperty.RULE_THEN, rule.then())); return errors; }
/** * @return set of inference rules contained in the graph */ public Stream<Rule> getRules() { Rule metaRule = tx.getMetaRule(); return metaRule.subs().filter(sub -> !sub.equals(metaRule)); }
/** * @param graph graph used to ensure the rule head is valid * @param rule the rule to be validated * @return Error messages if the rule head is invalid - is not a single-atom conjunction, doesn't contain illegal atomics and is ontologically valid */ private static Set<String> validateRuleHead(GraknTx graph, Rule rule) { Set<String> errors = new HashSet<>(); Set<Conjunction<VarPatternAdmin>> headPatterns = rule.then().admin().getDisjunctiveNormalForm().getPatterns(); if (headPatterns.size() != 1){ errors.add(ErrorMessage.VALIDATION_RULE_DISJUNCTION_IN_HEAD.getMessage(rule.label())); } else { ReasonerQuery bodyQuery = Iterables.getOnlyElement(rule.when().admin().getDisjunctiveNormalForm().getPatterns()).toReasonerQuery(graph); ReasonerQuery headQuery = Iterables.getOnlyElement(headPatterns).toReasonerQuery(graph); ReasonerQuery combinedQuery = headQuery.conjunction(bodyQuery); Set<Atomic> headAtoms = headQuery.getAtoms(); combinedQuery.getAtoms().stream() .filter(headAtoms::contains) .map(at -> at.validateAsRuleHead(rule)) .forEach(errors::addAll); Set<Atomic> selectableHeadAtoms = headAtoms.stream() .filter(Atomic::isAtom) .filter(Atomic::isSelectable) .collect(Collectors.toSet()); if (selectableHeadAtoms.size() > 1) { errors.add(ErrorMessage.VALIDATION_RULE_HEAD_NON_ATOMIC.getMessage(rule.label())); } } return errors; }
/** * @param graph graph used to ensure the rule is a valid Horn clause * @param rule the rule to be validated * @return Error messages if the rule is not a valid Horn clause (in implication form, conjunction in the body, single-atom conjunction in the head) */ static Set<String> validateRuleIsValidHornClause(GraknTx graph, Rule rule){ Set<String> errors = new HashSet<>(); if (rule.when().admin().isDisjunction()){ errors.add(ErrorMessage.VALIDATION_RULE_DISJUNCTION_IN_BODY.getMessage(rule.label())); } if (errors.isEmpty()){ errors.addAll(validateRuleHead(graph, rule)); } return errors; }
@Override Label transform(MatchableConcept item) { Concept concept = item.get(); return concept.isRule() ? concept.asRule().label() : null; } };
/** * Make the second argument the super of the first argument * * @throws GraqlQueryException if the types are different, or setting the super to be a meta-type */ public static void setSuper(SchemaConcept subConcept, SchemaConcept superConcept) { if (superConcept.isEntityType()) { subConcept.asEntityType().sup(superConcept.asEntityType()); } else if (superConcept.isRelationshipType()) { subConcept.asRelationshipType().sup(superConcept.asRelationshipType()); } else if (superConcept.isRole()) { subConcept.asRole().sup(superConcept.asRole()); } else if (superConcept.isAttributeType()) { subConcept.asAttributeType().sup(superConcept.asAttributeType()); } else if (superConcept.isRule()) { subConcept.asRule().sup(superConcept.asRule()); } else { throw GraqlQueryException.insertMetaType(subConcept.label(), superConcept); } }
@Override public Rule putRule(Label label, Pattern when, Pattern then) { Rule rule = putSchemaConcept(label, Schema.BaseType.RULE, false, v -> factory().buildRule(v, getMetaRule(), when, then)); //NB: thenTypes() will be empty as type edges added on commit //NB: this will cache also non-committed rules if (rule.then() != null){ rule.then().admin().varPatterns().stream() .flatMap(v -> v.getTypeLabels().stream()) .map(vl -> this.admin().<SchemaConcept>getSchemaConcept(vl)) .filter(Objects::nonNull) .filter(Concept::isType) .forEach(type -> ruleCache.updateRules(type, rule)); } return rule; }
/** * @param rules set of rules of interest forming a rule subgraph * @return true if the rule subgraph formed from provided rules contains loops */ public static boolean subGraphIsCyclical(Set<InferenceRule> rules){ Iterator<Rule> ruleIterator = rules.stream() .map(InferenceRule::getRule) .iterator(); boolean cyclical = false; while (ruleIterator.hasNext() && !cyclical){ Set<Rule> visitedRules = new HashSet<>(); Stack<Rule> rulesToVisit = new Stack<>(); rulesToVisit.push(ruleIterator.next()); while(!rulesToVisit.isEmpty() && !cyclical) { Rule rule = rulesToVisit.pop(); if (!visitedRules.contains(rule)){ rule.thenTypes() .flatMap(SchemaConcept::whenRules) .forEach(rulesToVisit::add); visitedRules.add(rule); } else { cyclical = true; } } } return cyclical; }
public static Collection<? extends SchemaConcept> allSchemaElementsFrom(GraknTx graph) { Set<SchemaConcept> allSchemaConcepts = new HashSet<>(); allSchemaConcepts.addAll(graph.admin().getMetaConcept().subs().collect(toSet())); allSchemaConcepts.addAll(graph.admin().getMetaRole().subs().collect(toSet())); allSchemaConcepts.addAll(graph.admin().getMetaRule().subs().collect(toSet())); return allSchemaConcepts; }
SchemaConcept schemaConcept = graph.getSchemaConcept(typeLabel); if(schemaConcept == null){ errors.add(ErrorMessage.VALIDATION_RULE_MISSING_ELEMENTS.getMessage(side, rule.label(), typeLabel)); } else { if(Schema.VertexProperty.RULE_WHEN.equals(side)){
/** * Make the second argument the super of the first argument * * @throws GraqlQueryException if the types are different, or setting the super to be a meta-type */ private static void setSuper(SchemaConcept subConcept, SchemaConcept superConcept) { if (superConcept.isEntityType()) { subConcept.asEntityType().sup(superConcept.asEntityType()); } else if (superConcept.isRelationshipType()) { subConcept.asRelationshipType().sup(superConcept.asRelationshipType()); } else if (superConcept.isRole()) { subConcept.asRole().sup(superConcept.asRole()); } else if (superConcept.isAttributeType()) { subConcept.asAttributeType().sup(superConcept.asAttributeType()); } else if (superConcept.isRule()) { subConcept.asRule().sup(superConcept.asRule()); } else { throw GraqlQueryException.insertMetaType(subConcept.getLabel(), superConcept); } } }
private Rule ruleType() { return random.choose(tx.admin().getMetaRule().subs().collect(toSet())); }
private static ReasonerQuery combinedRuleQuery(GraknTx graph, Rule rule){ ReasonerQuery bodyQuery = rule.when().admin().getDisjunctiveNormalForm().getPatterns().iterator().next().toReasonerQuery(graph); ReasonerQuery headQuery = rule.then().admin().getDisjunctiveNormalForm().getPatterns().iterator().next().toReasonerQuery(graph); return headQuery.conjunction(bodyQuery); }
@Override public String toString() { if (concept.isAttribute()) { return "hasValue(" + valueToString(concept.asAttribute().value()) + ")"; } else if (concept.isThing()) { Thing thing = concept.asThing(); Stream<Attribute<?>> resources = thing.attributes(); Optional<?> value = resources .filter(resource -> NAME_TYPES.contains(resource.type().label())) .map(Attribute::value).findFirst(); return "instance(" + value.map(StringUtil::valueToString).orElse("") + ") isa " + thing.type().label(); } else if (concept.isType()) { return "type(" + concept.asType().label() + ")"; } else if (concept.isRole()) { return "role(" + concept.asRole().label() + ")"; } else if (concept.isRule()) { return "rule(" + concept.asRule().label() + ")"; } else { throw CommonUtil.unreachableStatement("Unrecognised concept " + concept); } }
/** * Get all the {@link SchemaConcept}s in a graph. * @return a stream of all {@link SchemaConcept}s with non-reserved IDs */ private Stream<? extends SchemaConcept> schemaConcepts(){ Stream<? extends Type> types = tx.admin().getMetaConcept().subs(); Stream<Role> roles = tx.admin().getMetaRole().subs(); Stream<Rule> rules = tx.admin().getMetaRule().subs(); return Stream.of(types, roles, rules) .flatMap(Function.identity()) .filter(t -> !Schema.MetaSchema.isMetaLabel(t.label())); } }