/** Derefernce a node which may be a functor node */ public static Node derefPossFunctor(Node node) { if (node instanceof Node_RuleVariable) { Node dnode = ((Node_RuleVariable) node).deref(); if (dnode.isVariable()) { // Problem with variable in return result "should never happen" throw new ReasonerException("Internal error in LP reasoner: variable in triple result"); } if (Functor.isFunctor(dnode)) { Functor f = (Functor) dnode.getLiteralValue(); Node[] fargs = f.getArgs(); boolean needCopy = false; for (Node farg : fargs) { if (farg.isVariable()) { needCopy = true; break; } } if (needCopy) { Node[] newArgs = new Node[fargs.length]; for (int i = 0; i < fargs.length; i++) { newArgs[i] = deref(fargs[i]); } dnode = Functor.makeFunctorNode(f.getName(), newArgs); } return dnode; } else { return dnode; } } else { return node; } }
/** * Return true if the given predicated is tabled, currently this is true if the predicate is a * tabled predicate or the predicate is a wildcard and some tabled predictes exist. */ public boolean isTabled(Node predicate) { if (allTabled) return true; if (predicate.isVariable() && !tabledPredicates.isEmpty()) { return true; } else { return tabledPredicates.contains(predicate); } }
/** * Standardize a node by replacing instances of wildcard ANY by new distinct variables. This is * used in constructing the arguments to a top level call from a goal pattern. * * @param node the node to be standardized * @param mappedVars known mappings from input variables to local variables */ private Node standardize(Node node, Map<Node, Node> mappedVars) { Node dnode = deref(node); if (node == Node.ANY || node == Node_RuleVariable.WILD) { return new Node_RuleVariable(null, 0); } else if (dnode.isVariable()) { Node mnode = mappedVars.get(dnode); if (mnode == null) { mnode = new Node_RuleVariable(null, 0); mappedVars.put(dnode, mnode); } return mnode; } else { return dnode; } }
/** * Return an ordered list of RuleClauseCode objects to implement the given predicate. * * @param predicate the predicate node or Node_RuleVariable.WILD for wildcards. */ public List<RuleClauseCode> codeFor(Node predicate) { if (!isCompiled) { compileAll(); } if (predicate.isVariable()) { return allRuleClauseCodes; } else { List<RuleClauseCode> codeList = predicateToCodeMap.get(predicate); if (codeList == null) { // Uknown predicate, so only the wildcard rules apply codeList = predicateToCodeMap.get(Node_RuleVariable.WILD); } return codeList; } }
/** * Restore the current choice point and restart execution of the LP code until either find a * successful branch (in which case exit with StateFlag.ACTIVE and variables bound to the correct * results) or exhaust all choice points (in which case exit with StateFlag.FAIL and no bound * results). In future tabled version could also exit with StateFlag.SUSPEND in cases whether the * intepreter needs to suspend to await tabled results from a parallel proof tree. */ protected StateFlag run() { int pc = 0; // Program code counter int ac = 0; // Program arg code counter RuleClauseCode clause = null; // The clause being executed ChoicePointFrame choice = null; byte[] code; Object[] args; boolean traceOn = engine.isTraceOn(); boolean recordDerivations = engine.getDerivationLogging(); main: while (cpFrame != null) { // restore choice point if (cpFrame instanceof ChoicePointFrame) { choice = (ChoicePointFrame) cpFrame; if (!choice.hasNext()) { // No more choices left in this choice point cpFrame = choice.getLink(); if (traceOn) logger.info("FAIL in clause " + choice.envFrame.clause + " choices exhausted"); continue main; } clause = choice.nextClause(); // Create an execution environment for the new choice of clause if (recordDerivations) { envFrame = new EnvironmentFrameWithDerivation(clause); } else { envFrame = new EnvironmentFrame(clause); } envFrame.linkTo(choice.envFrame); envFrame.cpc = choice.cpc; envFrame.cac = choice.cac; // Restore the choice point state System.arraycopy(choice.argVars, 0, argVars, 0, RuleClauseCode.MAX_ARGUMENT_VARS); int trailMark = choice.trailIndex; if (trailMark < trail.size()) { unwindTrail(trailMark); } pc = ac = 0; if (recordDerivations) { ((EnvironmentFrameWithDerivation) envFrame).initDerivationRecord(argVars); } if (traceOn) logger.info("ENTER " + clause + " : " + getArgTrace()); // then fall through into the recreated execution context for the new call } else if (cpFrame instanceof TripleMatchFrame) { TripleMatchFrame tmFrame = (TripleMatchFrame) cpFrame; // Restore the calling context envFrame = tmFrame.envFrame; clause = envFrame.clause; int trailMark = tmFrame.trailIndex; if (trailMark < trail.size()) { unwindTrail(trailMark); } // Find the next choice result directly if (!tmFrame.nextMatch(this)) { // No more matches cpFrame = cpFrame.getLink(); if (traceOn) logger.info("TRIPLE match (" + tmFrame.goal + ") -> FAIL"); continue main; } if (traceOn) { logger.info("TRIPLE match (" + tmFrame.goal + ") -> " + getArgTrace()); logger.info("RENTER " + clause); } pc = tmFrame.cpc; ac = tmFrame.cac; if (recordDerivations) { if (envFrame instanceof EnvironmentFrameWithDerivation) { ((EnvironmentFrameWithDerivation) envFrame).noteMatch(tmFrame.goal, pc); } } // then fall through to the execution context in which the the match was called } else if (cpFrame instanceof TopLevelTripleMatchFrame) { TopLevelTripleMatchFrame tmFrame = (TopLevelTripleMatchFrame) cpFrame; // Find the next choice result directly if (!tmFrame.nextMatch(this)) { // No more matches cpFrame = cpFrame.getLink(); if (traceOn) logger.info("TRIPLE match (" + tmFrame.goal + ") -> FAIL"); continue main; } else { // Match but this is the top level so return the triple directly if (traceOn) logger.info("TRIPLE match (" + tmFrame.goal + ") ->"); return StateFlag.SATISFIED; } } else if (cpFrame instanceof ConsumerChoicePointFrame) { ConsumerChoicePointFrame ccp = (ConsumerChoicePointFrame) cpFrame; // Restore the calling context envFrame = ccp.envFrame; clause = envFrame.clause; if (traceOn) logger.info("RESTORE " + clause + ", due to tabled goal " + ccp.generator.goal); int trailMark = ccp.trailIndex; if (trailMark < trail.size()) { unwindTrail(trailMark); } // Find the next choice result directly StateFlag state = ccp.nextMatch(this); if (state == StateFlag.FAIL) { // No more matches cpFrame = cpFrame.getLink(); if (traceOn) logger.info("FAIL " + clause); continue main; } else if (state == StateFlag.SUSPEND) { // Require other generators to cycle before resuming this one preserveState(ccp); iContext.notifyBlockedOn(ccp); cpFrame = cpFrame.getLink(); if (traceOn) logger.info("SUSPEND " + clause); continue main; } pc = ccp.cpc; ac = ccp.cac; if (recordDerivations) { if (envFrame instanceof EnvironmentFrameWithDerivation) { ((EnvironmentFrameWithDerivation) envFrame).noteMatch(ccp.goal, pc); } } // then fall through to the execution context in which the the match was called } else { throw new ReasonerException( "Internal error in backward rule system, unrecognized choice point"); } engine.incrementProfile(clause); interpreter: while (envFrame != null) { // Start of bytecode intepreter loop // Init the state variables pVars = envFrame.pVars; int yi, ai, ti; Node arg, constant; code = clause.getCode(); args = clause.getArgs(); while (true) { switch (code[pc++]) { case RuleClauseCode.TEST_BOUND: ai = code[pc++]; if (deref(argVars[ai]).isVariable()) { if (traceOn) logger.info("FAIL " + clause); continue main; } break; case RuleClauseCode.TEST_UNBOUND: ai = code[pc++]; if (!deref(argVars[ai]).isVariable()) { if (traceOn) logger.info("FAIL " + clause); continue main; } break; case RuleClauseCode.ALLOCATE: int envSize = code[pc++]; envFrame.allocate(envSize); pVars = envFrame.pVars; break; case RuleClauseCode.GET_VARIABLE: yi = code[pc++]; ai = code[pc++]; pVars[yi] = argVars[ai]; break; case RuleClauseCode.GET_TEMP: ti = code[pc++]; ai = code[pc++]; tVars[ti] = argVars[ai]; break; case RuleClauseCode.GET_CONSTANT: ai = code[pc++]; arg = argVars[ai]; if (arg instanceof Node_RuleVariable) arg = ((Node_RuleVariable) arg).deref(); constant = (Node) args[ac++]; if (arg instanceof Node_RuleVariable) { bind(arg, constant); } else { if (!arg.sameValueAs(constant)) { if (traceOn) logger.info("FAIL " + clause); continue main; } } break; case RuleClauseCode.GET_FUNCTOR: Functor func = (Functor) args[ac++]; boolean match = false; Node o = argVars[2]; if (o instanceof Node_RuleVariable) o = ((Node_RuleVariable) o).deref(); if (Functor.isFunctor(o)) { Functor funcArg = (Functor) o.getLiteralValue(); if (funcArg.getName().equals(func.getName())) { if (funcArg.getArgLength() == func.getArgLength()) { Node[] fargs = funcArg.getArgs(); for (int i = 0; i < fargs.length; i++) { argVars[i + 3] = fargs[i]; } match = true; } } } else if (o.isVariable()) { // Construct a new functor in place Node[] fargs = new Node[func.getArgLength()]; Node[] templateArgs = func.getArgs(); for (int i = 0; i < fargs.length; i++) { Node template = templateArgs[i]; if (template.isVariable()) template = new Node_RuleVariable(null, i + 3); fargs[i] = template; argVars[i + 3] = template; } Node newFunc = Functor.makeFunctorNode(func.getName(), fargs); bind(((Node_RuleVariable) o).deref(), newFunc); match = true; } if (!match) { if (traceOn) logger.info("FAIL " + clause); continue main; // fail to unify functor shape } break; case RuleClauseCode.UNIFY_VARIABLE: yi = code[pc++]; ai = code[pc++]; if (!unify(argVars[ai], pVars[yi])) { if (traceOn) logger.info("FAIL " + clause); continue main; } break; case RuleClauseCode.UNIFY_TEMP: ti = code[pc++]; ai = code[pc++]; if (!unify(argVars[ai], tVars[ti])) { if (traceOn) logger.info("FAIL " + clause); continue main; } break; case RuleClauseCode.PUT_NEW_VARIABLE: yi = code[pc++]; ai = code[pc++]; argVars[ai] = pVars[yi] = new Node_RuleVariable(null, yi); break; case RuleClauseCode.PUT_VARIABLE: yi = code[pc++]; ai = code[pc++]; argVars[ai] = pVars[yi]; break; case RuleClauseCode.PUT_DEREF_VARIABLE: yi = code[pc++]; ai = code[pc++]; argVars[ai] = deref(pVars[yi]); break; case RuleClauseCode.PUT_TEMP: ti = code[pc++]; ai = code[pc++]; argVars[ai] = tVars[ti]; break; case RuleClauseCode.PUT_CONSTANT: ai = code[pc++]; argVars[ai] = (Node) args[ac++]; break; case RuleClauseCode.CLEAR_ARG: ai = code[pc++]; argVars[ai] = new Node_RuleVariable(null, ai); break; case RuleClauseCode.MAKE_FUNCTOR: Functor f = (Functor) args[ac++]; Node[] fargs = new Node[f.getArgLength()]; System.arraycopy(argVars, 3, fargs, 0, fargs.length); argVars[2] = Functor.makeFunctorNode(f.getName(), fargs); break; case RuleClauseCode.LAST_CALL_PREDICATE: // TODO: improved implementation of last call case case RuleClauseCode.CALL_PREDICATE: List<RuleClauseCode> clauses = ((RuleClauseCodeList) args[ac++]).getList(); // Check if this call is now grounded boolean groundCall = isGrounded(argVars[0]) && isGrounded(argVars[1]) && isGrounded(argVars[2]); setupClauseCall(pc, ac, clauses, groundCall); setupTripleMatchCall(pc, ac); continue main; case RuleClauseCode.CALL_PREDICATE_INDEX: // This code path is experimental, don't yet know if it has enough // performance benefit to justify the cost of maintaining it. clauses = ((RuleClauseCodeList) args[ac++]).getList(); // Check if we can futher index the clauses if (!argVars[2].isVariable()) { clauses = engine .getRuleStore() .codeFor(new TriplePattern(argVars[0], argVars[1], argVars[2])); } setupClauseCall(pc, ac, clauses, false); setupTripleMatchCall(pc, ac); continue main; case RuleClauseCode.CALL_TRIPLE_MATCH: setupTripleMatchCall(pc, ac); continue main; case RuleClauseCode.CALL_TABLED: setupTabledCall(pc, ac); continue main; case RuleClauseCode.CALL_WILD_TABLED: Node predicate = deref(argVars[1]); if (engine.getRuleStore().isTabled(predicate)) { setupTabledCall(pc, ac); } else { // normal call set up clauses = engine .getRuleStore() .codeFor(new TriplePattern(argVars[0], predicate, argVars[2])); if (clauses != null) setupClauseCall(pc, ac, clauses, false); setupTripleMatchCall(pc, ac); } continue main; case RuleClauseCode.PROCEED: pc = envFrame.cpc; ac = envFrame.cac; if (traceOn) logger.info("EXIT " + clause); if (choice != null) choice.noteSuccess(); if (recordDerivations && envFrame.getRule() != null) { if (envFrame instanceof EnvironmentFrameWithDerivation) { EnvironmentFrameWithDerivation efd = (EnvironmentFrameWithDerivation) envFrame; Triple result = efd.getResult(); List<Triple> matches = efd.getMatchList(); BackwardRuleInfGraphI infGraph = engine.getInfGraph(); RuleDerivation d = new RuleDerivation(envFrame.getRule(), result, matches, infGraph); infGraph.logDerivation(result, d); // Also want to record this result in the calling frame if (envFrame.link instanceof EnvironmentFrameWithDerivation) { EnvironmentFrameWithDerivation pefd = (EnvironmentFrameWithDerivation) envFrame.link; pefd.noteMatch(new TriplePattern(result), pc); } } } envFrame = (EnvironmentFrame) envFrame.link; if (envFrame != null) { clause = envFrame.clause; } continue interpreter; case RuleClauseCode.CALL_BUILTIN: Builtin builtin = (Builtin) args[ac++]; if (context == null) { BBRuleContext bbcontext = new BBRuleContext(engine.getInfGraph()); bbcontext.setEnv(new LPBindingEnvironment(this)); context = bbcontext; } context.setRule(clause.getRule()); if (!builtin.bodyCall(argVars, code[pc++], context)) { if (traceOn) logger.info("FAIL " + clause + ", due to " + builtin.getName()); continue main; } break; default: throw new ReasonerException( "Internal error in backward rule system\nIllegal op code"); } } // End of innter code loop } // End of bytecode interpreter loop, gets to here if we complete an AND chain return StateFlag.ACTIVE; } // Gets to here if we have run out of choice point frames return StateFlag.FAIL; }
@Override public QueryIterator exec( Binding binding, PropFuncArg argSubject, Node predicate, PropFuncArg argObject, ExecutionContext execCxt) { if (textIndex == null) { if (!warningIssued) { Log.warn(getClass(), "No text index - no text search performed"); warningIssued = true; } // Not a text dataset - no-op return IterLib.result(binding, execCxt); } DatasetGraph dsg = execCxt.getDataset(); argSubject = Substitute.substitute(argSubject, binding); argObject = Substitute.substitute(argObject, binding); Node s = null; Node score = null; Node literal = null; if (argSubject.isList()) { // Length checked in build() s = argSubject.getArg(0); score = argSubject.getArg(1); if (!score.isVariable()) throw new QueryExecException("Hit score is not a variable: " + argSubject); if (argSubject.getArgListSize() > 2) { literal = argSubject.getArg(2); if (!literal.isVariable()) throw new QueryExecException("Hit literal is not a variable: " + argSubject); } } else { s = argSubject.getArg(); } if (s.isLiteral()) // Does not match return IterLib.noResults(execCxt); StrMatch match = objectToStruct(argObject, true); if (match == null) { // can't match return IterLib.noResults(execCxt); } // ---- QueryIterator qIter = (Var.isVar(s)) ? variableSubject(binding, s, score, literal, match, execCxt) : concreteSubject(binding, s, score, literal, match, execCxt); if (match.getLimit() >= 0) qIter = new QueryIterSlice(qIter, 0, match.getLimit(), execCxt); return qIter; }
/** * Compile all the rules in a table. initially just indexed on predicate but want to add better * indexing for the particular cases of wildcard rules and type rules. */ protected void compileAll() { isCompiled = true; predicateToCodeMap = new HashMap<>(); allRuleClauseCodes = new ArrayList<>(); indexPredicateToCodeMap = new HashMap<>(); for (Rule r : getAllRules()) { ClauseEntry term = r.getHeadElement(0); if (term instanceof TriplePattern) { RuleClauseCode code = new RuleClauseCode(r); allRuleClauseCodes.add(code); Node predicate = ((TriplePattern) term).getPredicate(); if (predicate.isVariable()) { predicate = Node_RuleVariable.WILD; } List<RuleClauseCode> predicateCode = predicateToCodeMap.get(predicate); if (predicateCode == null) { predicateCode = new ArrayList<>(); predicateToCodeMap.put(predicate, predicateCode); } predicateCode.add(code); if (predicateCode.size() > INDEX_THRESHOLD) { indexPredicateToCodeMap.put(predicate, new HashMap<Node, List<RuleClauseCode>>()); } } } // Now add the wild card rules into the list for each non-wild predicate) List<RuleClauseCode> wildRules = predicateToCodeMap.get(Node_RuleVariable.WILD); if (wildRules != null) { for (Map.Entry<Node, List<RuleClauseCode>> entry : predicateToCodeMap.entrySet()) { Node predicate = entry.getKey(); List<RuleClauseCode> predicateCode = entry.getValue(); if (predicate != Node_RuleVariable.WILD) { predicateCode.addAll(wildRules); } } } indexPredicateToCodeMap.put(Node_RuleVariable.WILD, new HashMap<Node, List<RuleClauseCode>>()); // Now built any required two level indices for (Map.Entry<Node, Map<Node, List<RuleClauseCode>>> entry : indexPredicateToCodeMap.entrySet()) { Node predicate = entry.getKey(); Map<Node, List<RuleClauseCode>> predicateMap = entry.getValue(); List<RuleClauseCode> wildRulesForPredicate = new ArrayList<>(); List<RuleClauseCode> allRulesForPredicate = predicate.isVariable() ? allRuleClauseCodes : predicateToCodeMap.get(predicate); for (Iterator<RuleClauseCode> j = allRulesForPredicate.iterator(); j.hasNext(); ) { RuleClauseCode code = j.next(); ClauseEntry head = code.getRule().getHeadElement(0); boolean indexed = false; if (head instanceof TriplePattern) { Node objectPattern = ((TriplePattern) head).getObject(); if (!objectPattern.isVariable() && !Functor.isFunctor(objectPattern)) { // Index against object List<RuleClauseCode> indexedCode = predicateMap.get(objectPattern); if (indexedCode == null) { indexedCode = new ArrayList<>(); predicateMap.put(objectPattern, indexedCode); } indexedCode.add(code); indexed = true; } } if (!indexed) { wildRulesForPredicate.add(code); } } // Now fold the rules that apply to any index entry into all the indexed entries for (Iterator<Map.Entry<Node, List<RuleClauseCode>>> k = predicateMap.entrySet().iterator(); k.hasNext(); ) { Map.Entry<Node, List<RuleClauseCode>> ent = k.next(); List<RuleClauseCode> predicateCode = ent.getValue(); predicateCode.addAll(wildRulesForPredicate); } } // Now compile all the clauses for (RuleClauseCode code : allRuleClauseCodes) { code.compile(this); } }
/** * Register variables. * * <p>Registers n as a variable if it is one. * * @param n the node to check * @param variables the list of variable nodes @Return n for chaining. */ private Node registerVariables(final Node n, final List<Node> variables) { if (n.isVariable() && !variables.contains(n)) { variables.add(n); } return n; }
public static Expr nodeToExpr(Node n) { if (n.isVariable()) return new ExprVar(n); return NodeValue.makeNode(n); }