示例#1
0
 def hybridPredQueryPreparation(self, tp):
     lit = BuildUnitermFromTuple(tp)
     op = GetOp(lit)
     if op in self.hybridPredicates:
         lit.setOperator(URIRef(op + '_derived'))
         return lit.toRDFTuple()
     else:
         return tp
示例#2
0
 def hybridPredQueryPreparation(self, tp):
     lit = BuildUnitermFromTuple(tp, newNss=self.nsBindings)
     op = GetOp(lit)
     if op in self.hybridPredicates:
         lit.setOperator(URIRef(op + u'_derived'))
         return lit.toRDFTuple()
     else:
         return tp
示例#3
0
def SetupMetaInterpreter(tBoxGraph, goal, useThingRule=True):
    from pprint import pprint
    from FuXi.LP.BackwardFixpointProcedure import BackwardFixpointProcedure
    from FuXi.Rete.Magic import SetupDDLAndAdornProgram  # , PrettyPrintRule
    from FuXi.Horn.PositiveConditions import BuildUnitermFromTuple  # , Exists
    from FuXi.Rete.TopDown import PrepareSipCollection
    from FuXi.DLP import LloydToporTransformation, makeRule
    from FuXi.Rete.SidewaysInformationPassing import GetOp

    owlThingAppears = False
    if useThingRule and OWL.Thing in tBoxGraph.all_nodes():
        owlThingAppears = True
    completionRules = HornFromN3(StringIO(RULES))
    if owlThingAppears:
        completionRules.formulae.extend(
            HornFromN3(StringIO(CONDITIONAL_THING_RULE)))
    reducedCompletionRules = set()
    for rule in completionRules:
        for clause in LloydToporTransformation(rule.formula):
            rule = makeRule(clause, {})
            # _debug(rule)
            # PrettyPrintRule(rule)
            reducedCompletionRules.add(rule)

    network = SetupRuleStore(makeNetwork=True)[-1]
    SetupDDLAndAdornProgram(
        tBoxGraph,
        reducedCompletionRules,
        [goal],
        derivedPreds=derivedPredicates,
        ignoreUnboundDPreds=True,
        hybridPreds2Replace=hybridPredicates)

    lit = BuildUnitermFromTuple(goal)
    op = GetOp(lit)
    lit.setOperator(URIRef(op + '_derived'))
    goal = lit.toRDFTuple()

    sipCollection = PrepareSipCollection(reducedCompletionRules)
    tBoxGraph.templateMap = {}
    bfp = BackwardFixpointProcedure(
        tBoxGraph,
        network,
        derivedPredicates,
        goal,
        sipCollection,
        hybridPredicates=hybridPredicates,
        debug=True)
    bfp.createTopDownReteNetwork(True)
    _debug(pformat(reducedCompletionRules))
    rt = bfp.answers(debug=True)
    _debug(pformat(rt))
    _debug(bfp.metaInterpNetwork)
    bfp.metaInterpNetwork.reportConflictSet(True, sys.stderr)
    for query in bfp.edbQueries:
        _debug("Dispatched query against dataset: %s" % query.asSPARQL())
    _debug(pformat(list(bfp.goalSolutions)))
示例#4
0
def SetupMetaInterpreter(tBoxGraph, goal, useThingRule=True):
    from FuXi.LP.BackwardFixpointProcedure import BackwardFixpointProcedure
    from FuXi.Rete.Magic import SetupDDLAndAdornProgram
    from FuXi.Horn.PositiveConditions import BuildUnitermFromTuple
    from FuXi.Rete.TopDown import PrepareSipCollection
    from FuXi.DLP import LloydToporTransformation, makeRule
    from FuXi.Rete.SidewaysInformationPassing import GetOp

    owlThingAppears = False
    if useThingRule and OWL.Thing in tBoxGraph.all_nodes():
        owlThingAppears = True
    completionRules = HornFromN3(StringIO(RULES))
    if owlThingAppears:
        completionRules.formulae.extend(
            HornFromN3(StringIO(CONDITIONAL_THING_RULE)))
    reducedCompletionRules = set()
    for rule in completionRules:
        for clause in LloydToporTransformation(rule.formula):
            rule = makeRule(clause, {})
            # log.debug(rule)
            # PrettyPrintRule(rule)
            reducedCompletionRules.add(rule)

    network = SetupRuleStore(makeNetwork=True)[-1]
    SetupDDLAndAdornProgram(
        tBoxGraph,
        reducedCompletionRules,
        [goal],
        derivedPreds=derivedPredicates,
        ignoreUnboundDPreds=True,
        hybridPreds2Replace=hybridPredicates)

    lit = BuildUnitermFromTuple(goal)
    op = GetOp(lit)
    lit.setOperator(URIRef(op + u'_derived'))
    goal = lit.toRDFTuple()

    sipCollection = PrepareSipCollection(reducedCompletionRules)
    tBoxGraph.templateMap = {}
    bfp = BackwardFixpointProcedure(
        tBoxGraph,
        network,
        derivedPredicates,
        goal,
        sipCollection,
        hybridPredicates=hybridPredicates,
        debug=True)
    bfp.createTopDownReteNetwork(True)
    log.debug(reducedCompletionRules)
    rt = bfp.answers(debug=True)
    log.debug(rt)
    log.debug(bfp.metaInterpNetwork)
    bfp.metaInterpNetwork.reportConflictSet(True, sys.stderr)
    for query in bfp.edbQueries:
        log.debug("Dispatched query against dataset: ", query.asSPARQL())
    log.debug(list(bfp.goalSolutions))
示例#5
0
文件: TopDown.py 项目: gjhiggins/FuXi
def SipStrategy(query,
                sipCollection,
                factGraph,
                derivedPreds,
                bindings={},
                processedRules=None,
                network=None,
                debug=False,
                buildProof=False,
                memoizeMemory=None,
                proofLevel=1):
    """
    Accordingly, we define a sip-strategy for computing the answers to a query
    expressed using a set of Datalog rules, and a set of sips, one for each
    adornment of a rule head, as follows...

    Each evaluation uses memoization (via Python decorators) but also relies on well-formed
    rewrites for using semi-naive bottom up method over large SPARQL data.

    """
    memoizeMemory = memoizeMemory and memoizeMemory or {}
    queryLiteral = BuildUnitermFromTuple(query)
    processedRules = processedRules and processedRules or set()
    if bindings:
        # There are bindings.  Apply them to the terms in the query
        queryLiteral.ground(bindings)

    if debug:
        print("%sSolving" % ('\t' * proofLevel), queryLiteral, bindings)
    # Only consider ground triple pattern isomorphism with matching bindings
    goalRDFStatement = queryLiteral.toRDFTuple()

    if queryLiteral in memoizeMemory:
        if debug:
            print("%sReturning previously calculated results for " %
                  ('\t' * proofLevel), queryLiteral)
        for answers in memoizeMemory[queryLiteral]:
            yield answers
    elif AlphaNode(goalRDFStatement).alphaNetworkHash(
        True,
        skolemTerms=list(bindings.values())) in \
        [AlphaNode(r.toRDFTuple()).alphaNetworkHash(True,
                                                    skolemTerms=list(bindings.values()))
            for r in processedRules
         if AdornLiteral(goalRDFStatement).adornment ==
         r.adornment]:
        if debug:
            print("%s Goal already processed..." %
                  ('\t' * proofLevel))
    else:
        isGround = literalIsGround(queryLiteral)
        if buildProof:
            ns = NodeSet(goalRDFStatement,
                         network=network, identifier=BNode())
        else:
            ns = None
        # adornedProgram = factGraph.adornedProgram
        queryPred = GetOp(queryLiteral)
        if sipCollection is None:
            rules = []
        else:
            # For every rule head matching the query, we invoke the rule,
            # thus determining an adornment, and selecting a sip to follow
            rules = sipCollection.headToRule.get(queryPred, set())
            if None in sipCollection.headToRule:
                # If there are second order rules, we add them
                # since they are a 'wildcard'
                rules.update(sipCollection.headToRule[None])

        # maintained list of rules that haven't been processed before and
        # match the query
        validRules = []

        # each subquery contains values for the bound arguments that are passed
        # through the sip arcs entering the node corresponding to that literal. For
        # each subquery generated, there is a set of answers.
        answers = []

        # variableMapping = {}

        # Some TBox queries can be 'joined' together into SPARQL queries against
        # 'base' predicates via an RDF dataset
        # These atomic concept inclusion axioms can be evaluated together
        # using a disjunctive operator at the body of a horn clause
        # where each item is a query of the form uniPredicate(?X):
        # Or( uniPredicate1(?X1), uniPredicate2(?X), uniPredicate3(?X), ..)
        # In this way massive, conjunctive joins can be 'mediated'
        # between the stated facts and the top-down solver
        @parameterizedPredicate([i for i in derivedPreds])
        def IsAtomicInclusionAxiomRHS(rule, dPreds):
            """
            This is an atomic inclusion axiom with
            a variable (or bound) RHS:  uniPred(?ENTITY)
            """
            bodyList = list(iterCondition(rule.formula.body))
            body = first(bodyList)
            return GetOp(body) not in dPreds and \
                len(bodyList) == 1 and \
                body.op == RDF.type

        atomicInclusionAxioms = list(filter(
            IsAtomicInclusionAxiomRHS, rules))
        if atomicInclusionAxioms and len(atomicInclusionAxioms) > 1:
            if debug:
                print("\tCombining atomic inclusion axioms: ")
                pprint(atomicInclusionAxioms, sys.stderr)
            if buildProof:
                factStep = InferenceStep(ns, source='some RDF graph')
                ns.steps.append(factStep)

            axioms = [rule.formula.body
                      for rule in atomicInclusionAxioms]

            # attempt to exaustively apply any available substitutions
            # and determine if query if fully ground
            vars = [v for v in GetArgs(queryLiteral,
                                       secondOrder=True)
                    if isinstance(v, Variable)]
            openVars, axioms, _bindings = \
                normalizeBindingsAndQuery(vars,
                                          bindings,
                                          axioms)
            if openVars:
                # mappings = {}
                # See if we need to do any variable mappings from the query literals
                # to the literals in the applicable rules
                query, rt = EDBQuery(axioms,
                                     factGraph,
                                     openVars,
                                     _bindings).evaluate(debug,
                                                         symmAtomicInclusion=True)
                if buildProof:
                    # FIXME: subquery undefined
                    factStep.groundQuery = subquery
                for ans in rt:
                    if buildProof:
                        factStep.bindings.update(ans)
                    memoizeMemory.setdefault(queryLiteral, set()).add(
                        (prepMemiozedAns(ans), ns))
                    yield ans, ns
            else:
                # All the relevant derivations have been explored and the result
                # is a ground query we can directly execute against the facts
                if buildProof:
                    factStep.bindings.update(bindings)
                query, rt = EDBQuery(axioms,
                                     factGraph,
                                     _bindings).evaluate(debug,
                                                         symmAtomicInclusion=True)
                if buildProof:
                    # FIXME: subquery undefined
                    factStep.groundQuery = subquery
                memoizeMemory.setdefault(queryLiteral, set()).add(
                    (prepMemiozedAns(rt), ns))
                yield rt, ns
            rules = filter(
                lambda i: not IsAtomicInclusionAxiomRHS(i), rules)
        for rule in rules:
            # An exception is the special predicate ph; it is treated as a base
            # predicate and the tuples in it are those supplied for qb by
            # unification.
            headBindings = getBindingsFromLiteral(
                goalRDFStatement, rule.formula.head)
            # comboBindings = dict([(k, v) for k, v in itertools.chain(
            #                                           bindings.items(),
            #                                           headBindings.items())])
            varMap = rule.formula.head.getVarMapping(queryLiteral)
            if headBindings and\
                [term for term in rule.formula.head.getDistinguishedVariables(True)
                 if varMap.get(term, term) not in headBindings]:
                continue
            # subQueryAnswers = []
            # dontStop = True
            # projectedBindings = comboBindings.copy()
            if debug:
                print("%sProcessing rule" %
                      ('\t' * proofLevel), rule.formula)
                if debug and sipCollection:
                    print(
                        "Sideways Information Passing (sip) graph for %s: " % queryLiteral)
                    print(sipCollection.serialize(format='n3'))
                    for sip in SIPRepresentation(sipCollection):
                        print(sip)
            try:
                # Invoke the rule
                if buildProof:
                    step = InferenceStep(ns, rule.formula)
                else:
                    step = None
                for rt, step in\
                    invokeRule([headBindings],
                               iter(iterCondition(rule.formula.body)),
                               rule.sip,
                               (proofLevel + 1,
                                memoizeMemory,
                                sipCollection,
                                factGraph,
                                derivedPreds,
                                processedRules.union([
                                    AdornLiteral(query)])),
                               step=step,
                               debug=debug):
                    if rt:
                        if isinstance(rt, dict):
                            # We received a mapping and must rewrite it via
                            # correlation between the variables in the rule head
                            # and the variables in the original query (after applying
                            # bindings)
                            varMap = rule.formula.head.getVarMapping(
                                queryLiteral)
                            if varMap:
                                rt = MakeImmutableDict(
                                    refactorMapping(varMap, rt))
                            if buildProof:
                                step.bindings = rt
                        else:
                            if buildProof:
                                step.bindings = headBindings
                        validRules.append(rule)
                        if buildProof:
                            ns.steps.append(step)
                        if isGround:
                            yield True, ns
                        else:
                            memoizeMemory.setdefault(queryLiteral, set()).add(
                                (prepMemiozedAns(rt),
                                 ns))
                            yield rt, ns

            except RuleFailure:
                # Clean up failed antecedents
                if buildProof:
                    if ns in step.antecedents:
                        step.antecedents.remove(ns)
        if not validRules:
            # No rules matching, query factGraph for answers
            successful = False
            if buildProof:
                factStep = InferenceStep(ns, source='some RDF graph')
                ns.steps.append(factStep)
            if not isGround:
                subquery, rt = EDBQuery([queryLiteral],
                                        factGraph,
                                        [v for v in GetArgs(queryLiteral,
                                                            secondOrder=True)
                                         if isinstance(v, Variable)],

                                        bindings).evaluate(debug)
                if buildProof:
                    factStep.groundQuery = subquery
                for ans in rt:
                    successful = True
                    if buildProof:
                        factStep.bindings.update(ans)
                    memoizeMemory.setdefault(queryLiteral, set()).add(
                        (prepMemiozedAns(ans),
                         ns))
                    yield ans, ns
                if not successful and queryPred not in derivedPreds:
                    # Open query didn't return any results and the predicate
                    # is ostensibly marked as derived predicate, so we have
                    # failed
                    memoizeMemory.setdefault(
                        queryLiteral, set()).add((False, ns))
                    yield False, ns
            else:
                # All the relevant derivations have been explored and the result
                # is a ground query we can directly execute against the facts
                if buildProof:
                    factStep.bindings.update(bindings)

                subquery, rt = EDBQuery([queryLiteral],
                                        factGraph,
                                        bindings).evaluate(debug)
                if buildProof:
                    factStep.groundQuery = subquery
                memoizeMemory.setdefault(queryLiteral, set()).add(
                    (prepMemiozedAns(rt),
                     ns))
                yield rt, ns
示例#6
0
    def sparql_query(self,
                     queryString,
                     queryObj,
                     graph,
                     dataSetBase,
                     extensionFunctions,
                     initBindings={},
                     initNs={},
                     DEBUG=False):
        """
        The default 'native' SPARQL implementation is based on sparql-p's expansion trees
        layered on top of the read-only RDF APIs of the underlying store
        """
        from rdflib.sparql.Algebra import TopEvaluate
        from rdflib.QueryResult import QueryResult
        from rdflib import plugin
        from rdflib.sparql.bison.Query import AskQuery
        _expr = self.isaBaseQuery(None,queryObj)
        if isinstance(queryObj.query,AskQuery) and \
           isinstance(_expr,BasicGraphPattern):
            #This is a ground, BGP, involving IDB and can be solved directly
            #using top-down decision procedure
            #First separate out conjunct into EDB and IDB predicates
            #(solving the former first)
            from FuXi.SPARQL import EDBQuery
            groundConjunct  = []
            derivedConjunct = []
            for s,p,o,func in _expr.patterns:
                if self.derivedPredicateFromTriple((s,p,o)) is None:
                    groundConjunct.append(BuildUnitermFromTuple((s,p,o)))
                else:
                    derivedConjunct.append(BuildUnitermFromTuple((s,p,o)))
            if groundConjunct:
                baseEDBQuery = EDBQuery(groundConjunct,self.edb)
                subQuery,ans = baseEDBQuery.evaluate(DEBUG)
                assert isinstance(ans,bool),ans
            if groundConjunct and not ans:
                askResult = False
            else:
                askResult = True
                for derivedLiteral in derivedConjunct:
                    goal = derivedLiteral.toRDFTuple()
                    #Solve ground, derived goal directly
                    SetupDDLAndAdornProgram(
                        self.edb,
                        self.idb,
                        [goal],
                        derivedPreds=self.derivedPredicates,
                        ignoreUnboundDPreds = True,
                        hybridPreds2Replace=self.hybridPredicates)

                    if self.hybridPredicates:
                        lit = BuildUnitermFromTuple(goal)
                        op = GetOp(lit)
                        if op in self.hybridPredicates:
                            lit.setOperator(URIRef(op+u'_derived'))
                            goal = lit.toRDFTuple()

                    sipCollection=PrepareSipCollection(self.edb.adornedProgram)
                    if self.DEBUG and sipCollection:
                        for sip in SIPRepresentation(sipCollection):
                            print >>sys.stderr,sip
                        pprint(list(self.edb.adornedProgram),sys.stderr)
                    elif self.DEBUG:
                        print >> sys.stderr, "No SIP graph!"

                    rt,node = first(self.invokeDecisionProcedure(
                            goal,
                            self.edb,
                            {},
                            self.DEBUG,
                            sipCollection))
                    if not rt:
                        askResult = False
                        break
            return plugin.get('SPARQLQueryResult',QueryResult)(askResult)
        else:
            rt =   TopEvaluate(queryObj,
                               graph,
                               initBindings,
                               DEBUG=self.DEBUG,
                               dataSetBase=dataSetBase,
                               extensionFunctions=extensionFunctions)
            return plugin.get('SPARQLQueryResult',QueryResult)(rt)
示例#7
0
    def conjunctiveSipStrategy(self,goalsRemaining,factGraph,bindings=None):
        """
        Given a conjunctive set of triples, invoke sip-strategy passing
        on intermediate solutions to facilitate 'join' behavior
        """
        bindings = bindings if bindings else {}
        try:
            tp = goalsRemaining.next()
            assert isinstance(bindings,dict)
            dPred = self.derivedPredicateFromTriple(tp)
            if dPred is None:
                baseEDBQuery = EDBQuery([BuildUnitermFromTuple(tp)],
                                        self.edb,
                                        bindings=bindings)
                if self.DEBUG:
                    print >>sys.stderr,"Evaluating TP against EDB: ",\
                    baseEDBQuery.asSPARQL()
                query,rt = baseEDBQuery.evaluate()
                # _vars = baseEDBQuery.returnVars
                for item in rt:
                    bindings.update(item)
                for ansDict in self.conjunctiveSipStrategy(
                                         goalsRemaining,
                                         factGraph,
                                         bindings):
                    yield ansDict

            else:
                queryLit = BuildUnitermFromTuple(tp)
                currentOp = GetOp(queryLit)
                queryLit.setOperator(currentOp)
                query=EDBQuery([queryLit],
                               self.edb,
                               bindings=bindings)
                if bindings:
                    tp=first(query.formulae).toRDFTuple()
                if self.DEBUG:
                    print >>sys.stderr,"Goal/Query: ", query.asSPARQL()
                SetupDDLAndAdornProgram(
                    self.edb,
                    self.idb,
                    [tp],
                    derivedPreds=self.derivedPredicates,
                    ignoreUnboundDPreds = True,
                    hybridPreds2Replace=self.hybridPredicates)

                if self.hybridPredicates:
                    lit = BuildUnitermFromTuple(tp)
                    op = GetOp(lit)
                    if op in self.hybridPredicates:
                        lit.setOperator(URIRef(op+u'_derived'))
                        tp = lit.toRDFTuple()

                sipCollection=PrepareSipCollection(self.edb.adornedProgram)
                if self.DEBUG and sipCollection:
                    for sip in SIPRepresentation(sipCollection):
                        print >>sys.stderr,sip
                    pprint(list(self.edb.adornedProgram),sys.stderr)
                elif self.DEBUG:
                    print >> sys.stderr, "No SIP graph!"
                for nextAnswer,ns in self.invokeDecisionProcedure(
                                            tp,
                                            factGraph,
                                            bindings,
                                            self.DEBUG,
                                            sipCollection):
                    nonGroundGoal = isinstance(nextAnswer,dict)
                    if nonGroundGoal or nextAnswer:
                        #Either we recieved bindings from top-down evaluation
                        #or we (successfully) proved a ground query
                        if not nonGroundGoal:
                            #Attempt to prove a ground query, return the response
                            rt = nextAnswer
                        else:
                            #Recieved solutions to 'open' query, merge with given bindings
                            #and continue
                            rt = mergeMappings1To2(bindings,nextAnswer)
                        #either answers were provided (the goal wasn't grounded) or
                        #the goal was ground and successfully proved
                        for ansDict in self.conjunctiveSipStrategy(
                                                 goalsRemaining,
                                                 factGraph,
                                                 rt):
                            yield ansDict
        except StopIteration:
            yield bindings
示例#8
0
def SipStrategy(query,
                sipCollection,
                factGraph,
                derivedPreds,
                bindings={},
                processedRules=None,
                network=None,
                debug=False,
                buildProof=False,
                memoizeMemory=None,
                proofLevel=1):
    """
    Accordingly, we define a sip-strategy for computing the answers to a query
    expressed using a set of Datalog rules, and a set of sips, one for each
    adornment of a rule head, as follows...

    Each evaluation uses memoization (via Python decorators) but also relies on well-formed
    rewrites for using semi-naive bottom up method over large SPARQL data.

    """
    memoizeMemory = memoizeMemory and memoizeMemory or {}
    queryLiteral = BuildUnitermFromTuple(query)
    processedRules = processedRules and processedRules or set()
    if bindings:
        #There are bindings.  Apply them to the terms in the query
        queryLiteral.ground(bindings)

    if debug:
        print("%sSolving" % ('\t' * proofLevel), queryLiteral, bindings)
    # Only consider ground triple pattern isomorphism with matching bindings
    goalRDFStatement = queryLiteral.toRDFTuple()

    if queryLiteral in memoizeMemory:
        if debug:
            print("%sReturning previously calculated results for " % \
                        ('\t' * proofLevel), queryLiteral)
        for answers in memoizeMemory[queryLiteral]:
            yield answers
    elif AlphaNode(goalRDFStatement).alphaNetworkHash(
                                      True,
                                      skolemTerms=list(bindings.values())) in \
        [AlphaNode(r.toRDFTuple()).alphaNetworkHash(True,
                                                    skolemTerms=list(bindings.values()))
            for r in processedRules
                if AdornLiteral(goalRDFStatement).adornment == \
                   r.adornment]:
        if debug:
            print("%s Goal already processed..." % \
                ('\t' * proofLevel))
    else:
        isGround = literalIsGround(queryLiteral)
        if buildProof:
            ns = NodeSet(goalRDFStatement, network=network, identifier=BNode())
        else:
            ns = None
        # adornedProgram = factGraph.adornedProgram
        queryPred = GetOp(queryLiteral)
        if sipCollection is None:
            rules = []
        else:
            #For every rule head matching the query, we invoke the rule,
            #thus determining an adornment, and selecting a sip to follow
            rules = sipCollection.headToRule.get(queryPred, set())
            if None in sipCollection.headToRule:
                #If there are second order rules, we add them
                #since they are a 'wildcard'
                rules.update(sipCollection.headToRule[None])

        #maintained list of rules that haven't been processed before and
        #match the query
        validRules = []

        #each subquery contains values for the bound arguments that are passed
        #through the sip arcs entering the node corresponding to that literal. For
        #each subquery generated, there is a set of answers.
        answers = []

        # variableMapping = {}

        #Some TBox queries can be 'joined' together into SPARQL queries against
        #'base' predicates via an RDF dataset
        #These atomic concept inclusion axioms can be evaluated together
        #using a disjunctive operator at the body of a horn clause
        #where each item is a query of the form uniPredicate(?X):
        #Or( uniPredicate1(?X1), uniPredicate2(?X), uniPredicate3(?X), ..)
        #In this way massive, conjunctive joins can be 'mediated'
        #between the stated facts and the top-down solver
        @parameterizedPredicate([i for i in derivedPreds])
        def IsAtomicInclusionAxiomRHS(rule, dPreds):
            """
            This is an atomic inclusion axiom with
            a variable (or bound) RHS:  uniPred(?ENTITY)
            """
            bodyList = list(iterCondition(rule.formula.body))
            body = first(bodyList)
            return GetOp(body) not in dPreds and \
                   len(bodyList) == 1 and \
                   body.op == RDF.type

        atomicInclusionAxioms = list(filter(IsAtomicInclusionAxiomRHS, rules))
        if atomicInclusionAxioms and len(atomicInclusionAxioms) > 1:
            if debug:
                print("\tCombining atomic inclusion axioms: ")
                pprint(atomicInclusionAxioms, sys.stderr)
            if buildProof:
                factStep = InferenceStep(ns, source='some RDF graph')
                ns.steps.append(factStep)

            axioms = [rule.formula.body for rule in atomicInclusionAxioms]

            #attempt to exaustively apply any available substitutions
            #and determine if query if fully ground
            vars = [
                v for v in GetArgs(queryLiteral, secondOrder=True)
                if isinstance(v, Variable)
            ]
            openVars, axioms, _bindings = \
                    normalizeBindingsAndQuery(vars,
                                              bindings,
                                              axioms)
            if openVars:
                # mappings = {}
                #See if we need to do any variable mappings from the query literals
                #to the literals in the applicable rules
                query, rt = EDBQuery(axioms, factGraph, openVars,
                                     _bindings).evaluate(
                                         debug, symmAtomicInclusion=True)
                if buildProof:
                    factStep.groundQuery = subquery
                for ans in rt:
                    if buildProof:
                        factStep.bindings.update(ans)
                    memoizeMemory.setdefault(queryLiteral, set()).add(
                        (prepMemiozedAns(ans), ns))
                    yield ans, ns
            else:
                #All the relevant derivations have been explored and the result
                #is a ground query we can directly execute against the facts
                if buildProof:
                    factStep.bindings.update(bindings)
                query, rt = EDBQuery(axioms, factGraph, _bindings).evaluate(
                    debug, symmAtomicInclusion=True)
                if buildProof:
                    factStep.groundQuery = subquery
                memoizeMemory.setdefault(queryLiteral, set()).add(
                    (prepMemiozedAns(rt), ns))
                yield rt, ns
            rules = filter(lambda i: not IsAtomicInclusionAxiomRHS(i), rules)
        for rule in rules:
            #An exception is the special predicate ph; it is treated as a base
            #predicate and the tuples in it are those supplied for qb by unification.
            headBindings = getBindingsFromLiteral(goalRDFStatement,
                                                  rule.formula.head)
            # comboBindings = dict([(k, v) for k, v in itertools.chain(
            #                                           bindings.items(),
            #                                           headBindings.items())])
            varMap = rule.formula.head.getVarMapping(queryLiteral)
            if headBindings and\
                [term for term in rule.formula.head.getDistinguishedVariables(True)
                        if varMap.get(term, term) not in headBindings]:
                continue
            # subQueryAnswers = []
            # dontStop = True
            # projectedBindings = comboBindings.copy()
            if debug:
                print("%sProcessing rule" % \
                            ('\t' * proofLevel), rule.formula)
                if debug and sipCollection:
                    print("Sideways Information Passing (sip) graph for %s: " %
                          queryLiteral)
                    print(sipCollection.serialize(format='n3'))
                    for sip in SIPRepresentation(sipCollection):
                        print(sip)
            try:
                # Invoke the rule
                if buildProof:
                    step = InferenceStep(ns, rule.formula)
                else:
                    step = None
                for rt, step in\
                  invokeRule([headBindings],
                              iter(iterCondition(rule.formula.body)),
                              rule.sip,
                              (proofLevel + 1,
                               memoizeMemory,
                               sipCollection,
                               factGraph,
                               derivedPreds,
                               processedRules.union([
                                 AdornLiteral(query)])),
                              step=step,
                              debug=debug):
                    if rt:
                        if isinstance(rt, dict):
                            #We received a mapping and must rewrite it via
                            #correlation between the variables in the rule head
                            #and the variables in the original query (after applying
                            #bindings)
                            varMap = rule.formula.head.getVarMapping(
                                queryLiteral)
                            if varMap:
                                rt = MakeImmutableDict(
                                    refactorMapping(varMap, rt))
                            if buildProof:
                                step.bindings = rt
                        else:
                            if buildProof:
                                step.bindings = headBindings
                        validRules.append(rule)
                        if buildProof:
                            ns.steps.append(step)
                        if isGround:
                            yield True, ns
                        else:
                            memoizeMemory.setdefault(queryLiteral, set()).add(
                                (prepMemiozedAns(rt), ns))
                            yield rt, ns

            except RuleFailure:
                # Clean up failed antecedents
                if buildProof:
                    if ns in step.antecedents:
                        step.antecedents.remove(ns)
        if not validRules:
            #No rules matching, query factGraph for answers
            successful = False
            if buildProof:
                factStep = InferenceStep(ns, source='some RDF graph')
                ns.steps.append(factStep)
            if not isGround:
                subquery, rt = EDBQuery([queryLiteral], factGraph, [
                    v for v in GetArgs(queryLiteral, secondOrder=True)
                    if isinstance(v, Variable)
                ], bindings).evaluate(debug)
                if buildProof:
                    factStep.groundQuery = subquery
                for ans in rt:
                    successful = True
                    if buildProof:
                        factStep.bindings.update(ans)
                    memoizeMemory.setdefault(queryLiteral, set()).add(
                        (prepMemiozedAns(ans), ns))
                    yield ans, ns
                if not successful and queryPred not in derivedPreds:
                    #Open query didn't return any results and the predicate
                    #is ostensibly marked as derived predicate, so we have failed
                    memoizeMemory.setdefault(queryLiteral, set()).add(
                        (False, ns))
                    yield False, ns
            else:
                #All the relevant derivations have been explored and the result
                #is a ground query we can directly execute against the facts
                if buildProof:
                    factStep.bindings.update(bindings)

                subquery, rt = EDBQuery([queryLiteral], factGraph,
                                        bindings).evaluate(debug)
                if buildProof:
                    factStep.groundQuery = subquery
                memoizeMemory.setdefault(queryLiteral, set()).add(
                    (prepMemiozedAns(rt), ns))
                yield rt, ns
示例#9
0
    def sparql_query(self,
                     queryString,
                     queryObj,
                     graph,
                     dataSetBase,
                     extensionFunctions,
                     initBindings={},
                     initNs={},
                     DEBUG=False):
        """
        The default 'native' SPARQL implementation is based on sparql-p's expansion trees
        layered on top of the read-only RDF APIs of the underlying store
        """
        from rdflib.sparql.Algebra import TopEvaluate
        from rdflib.QueryResult import QueryResult
        from rdflib import plugin
        from rdflib.sparql.bison.Query import AskQuery
        _expr = self.isaBaseQuery(None, queryObj)
        if isinstance(queryObj.query, AskQuery) and \
          _expr.name == 'BGP':
            # isinstance(_expr, BasicGraphPattern):
            #This is a ground, BGP, involving IDB and can be solved directly
            #using top-down decision procedure
            #First separate out conjunct into EDB and IDB predicates
            #(solving the former first)
            from FuXi.SPARQL import EDBQuery
            groundConjunct = []
            derivedConjunct = []
            for s, p, o, func in _expr.patterns:
                if self.derivedPredicateFromTriple((s, p, o)) is None:
                    groundConjunct.append(BuildUnitermFromTuple((s, p, o)))
                else:
                    derivedConjunct.append(BuildUnitermFromTuple((s, p, o)))
            if groundConjunct:
                baseEDBQuery = EDBQuery(groundConjunct, self.edb)
                subQuery, ans = baseEDBQuery.evaluate(DEBUG)
                assert isinstance(ans, bool), ans
            if groundConjunct and not ans:
                askResult = False
            else:
                askResult = True
                for derivedLiteral in derivedConjunct:
                    goal = derivedLiteral.toRDFTuple()
                    #Solve ground, derived goal directly
                    SetupDDLAndAdornProgram(
                        self.edb,
                        self.idb, [goal],
                        derivedPreds=self.derivedPredicates,
                        ignoreUnboundDPreds=True,
                        hybridPreds2Replace=self.hybridPredicates)

                    if self.hybridPredicates:
                        lit = BuildUnitermFromTuple(goal)
                        op = GetOp(lit)
                        if op in self.hybridPredicates:
                            lit.setOperator(URIRef(op + u'_derived'))
                            goal = lit.toRDFTuple()

                    sipCollection = PrepareSipCollection(
                        self.edb.adornedProgram)
                    if self.DEBUG and sipCollection:
                        for sip in SIPRepresentation(sipCollection):
                            print(sip)
                        pprint(list(self.edb.adornedProgram))
                    elif self.DEBUG:
                        print("No SIP graph.")

                    rt, node = first(
                        self.invokeDecisionProcedure(goal, self.edb, {},
                                                     self.DEBUG,
                                                     sipCollection))
                    if not rt:
                        askResult = False
                        break
            return plugin.get('SPARQLQueryResult', QueryResult)(askResult)
        else:
            rt = TopEvaluate(queryObj,
                             graph,
                             initBindings,
                             DEBUG=self.DEBUG,
                             dataSetBase=dataSetBase,
                             extensionFunctions=extensionFunctions)
            return plugin.get('SPARQLQueryResult', QueryResult)(rt)
示例#10
0
    def conjunctiveSipStrategy(self, goalsRemaining, factGraph, bindings=None):
        """
        Given a conjunctive set of triples, invoke sip-strategy passing
        on intermediate solutions to facilitate 'join' behavior
        """
        bindings = bindings if bindings else {}
        try:
            tp = next(goalsRemaining)
            assert isinstance(bindings, dict)
            dPred = self.derivedPredicateFromTriple(tp)
            if dPred is None:
                baseEDBQuery = EDBQuery([BuildUnitermFromTuple(tp)],
                                        self.edb,
                                        bindings=bindings)
                if self.DEBUG:
                    print("Evaluating TP against EDB:%s" %
                          baseEDBQuery.asSPARQL())
                query, rt = baseEDBQuery.evaluate()
                # _vars = baseEDBQuery.returnVars
                for item in rt:
                    bindings.update(item)
                for ansDict in self.conjunctiveSipStrategy(
                        goalsRemaining, factGraph, bindings):
                    yield ansDict

            else:
                queryLit = BuildUnitermFromTuple(tp)
                currentOp = GetOp(queryLit)
                queryLit.setOperator(currentOp)
                query = EDBQuery([queryLit], self.edb, bindings=bindings)
                if bindings:
                    tp = first(query.formulae).toRDFTuple()
                if self.DEBUG:
                    print("Goal/Query: ", query.asSPARQL())
                SetupDDLAndAdornProgram(
                    self.edb,
                    self.idb, [tp],
                    derivedPreds=self.derivedPredicates,
                    ignoreUnboundDPreds=True,
                    hybridPreds2Replace=self.hybridPredicates)

                if self.hybridPredicates:
                    lit = BuildUnitermFromTuple(tp)
                    op = GetOp(lit)
                    if op in self.hybridPredicates:
                        lit.setOperator(URIRef(op + u'_derived'))
                        tp = lit.toRDFTuple()

                sipCollection = PrepareSipCollection(self.edb.adornedProgram)
                if self.DEBUG and sipCollection:
                    for sip in SIPRepresentation(sipCollection):
                        print(sip)
                    pprint(list(self.edb.adornedProgram), sys.stderr)
                elif self.DEBUG:
                    print("No SIP graph.")
                for nextAnswer, ns in self.invokeDecisionProcedure(
                        tp, factGraph, bindings, self.DEBUG, sipCollection):
                    nonGroundGoal = isinstance(nextAnswer, dict)
                    if nonGroundGoal or nextAnswer:
                        #Either we recieved bindings from top-down evaluation
                        #or we (successfully) proved a ground query
                        if not nonGroundGoal:
                            #Attempt to prove a ground query, return the response
                            rt = nextAnswer
                        else:
                            #Recieved solutions to 'open' query, merge with given bindings
                            #and continue
                            rt = mergeMappings1To2(bindings, nextAnswer)
                        #either answers were provided (the goal wasn't grounded) or
                        #the goal was ground and successfully proved
                        for ansDict in self.conjunctiveSipStrategy(
                                goalsRemaining, factGraph, rt):
                            yield ansDict
        except StopIteration:
            yield bindings
示例#11
0
class BackwardFixpointProcedure(object):
    """
    Uses RETE-UL as a production rule system implementation of
    a meta-interpreter of an adorned RIF Core ruleset that builds solves conjunctive (BGPs)
    SPARQL queries.

    Facts are only generated in a bottom up evaluation of the interpreter if a
    query has been issued for that fact or if an appropriate sub-query has been generated.
    Sub-queries for rule bodies (conditions) are generated if a sub-query for
    the corresponding rule head already exists. Sub-queries for conjuncts are
    generated from sub-queries of conjunctions they appear in (queries are collected).
    """

    def __init__(
        self,
        factGraph,
        network,
        derivedPredicates,
        goal,
        sipCollection=[],
        hybridPredicates=None,
        debug=False,
        pushDownMDBQ=True,
        specialBNodeHandling=None,
    ):
        self.specialBNodeHandling = specialBNodeHandling
        self.debug = debug
        self.metaRule2Network = {}
        self.pushDownMDBQ = pushDownMDBQ
        self.pushDownQueries = {}
        self.maxEDBFront2End = {}
        self.queryPredicates = set()
        self.sipCollection = sipCollection
        self.goal = BuildUnitermFromTuple(goal)
        self.factGraph = factGraph
        self.rules = list(factGraph.adornedProgram)
        self.discardedRules = set()
        self.ruleLabels = {}
        self.bfpLookup = {}
        self.actionHash = {}
        self.namespaces = {u"bfp": BFP_NS, u"rule": BFP_RULE}
        self.metaInterpNetwork = network
        self.derivedPredicates = set(derivedPredicates) if isinstance(derivedPredicates, list) else derivedPredicates
        self.hybridPredicates = hybridPredicates if hybridPredicates else []
        self.edbQueries = set()
        self.goalSolutions = set()

    def answers(self, debug=False, solutionCallback=NoopCallbackFn):
        """
        Takes a conjunctive query, a sip collection
        and initiates the meta-interpreter for a given
        goal (at a time), propagating evaluate procedures
        explicitely if no bindings are given from the query
        to trigger subsequent subqueries via EDB predicates

        @TODO:
        Add a PRD externally defined action to the
        production of rules that produce answers
        for the query predicate.
        The action is a user specified callback that can be used
        to signal InferredGoal and halt RETE/UL evaluation prematurely
        otherwise, it is left to reach a stable state and the
        answers collected along the way are added and returned

        """
        # solutions = []

        # queryOp = GetOp(self.goal)
        if self.goal.isGround():
            # Mark ground goal so, production rule engine
            # halts when goal is inferred
            self.metaInterpNetwork.goal = self.goal.toRDFTuple()

        adornment = ["f" if isinstance(v, Variable) else "b" for v in GetArgs(self.goal, secondOrder=True)]
        adornment = reduce(lambda x, y: x + y, adornment)
        adornedQuery = AdornedUniTerm(self.goal, adornment)
        bfpTopQuery = self.makeDerivedQueryPredicate(adornedQuery)
        if debug:
            print("Asserting initial BFP query ", bfpTopQuery)

        assert bfpTopQuery.isGround()
        # Add BFP query atom to working memory, triggering procedure
        try:
            self.metaInterpNetwork.feedFactsToAdd(
                generateTokenSet([bfpTopQuery.toRDFTuple()], debugTriples=[bfpTopQuery.toRDFTuple()] if debug else [])
            )
        except InferredGoal:
            if debug:
                print("Reached ground goal. Terminated BFP.")
            return True
        else:
            if self.goal.isGround():
                # Ground goal, but didn't trigger it, response must be negative
                return False

    def specializeConjuncts(self, rule, idx, evalVars):
        """
        Extends the (vanilla) meta-interpreter for magic set and alexander rewriting
        sip strategies with capabilities for collapsing chains of extensional
        predicate queries (frames whose attributes are in EDB and externally-defined
        predicates) into a single SPARQL query
        """
        # _len = len(rule.formula.body)
        body = list(iterCondition(rule.formula.body))

        skipMDBQCount = 0
        for bodyIdx, bodyLiteral in enumerate(body):
            conjunct = []
            # V_{j} = V_{j-1} UNION vars(Literal(..)) where j <> 0
            evalVars[(idx + 1, bodyIdx + 1)] = (
                list(GetVariables(bodyLiteral, secondOrder=True)) + evalVars[(idx + 1, bodyIdx)]
            )
            if skipMDBQCount > 0:
                skipMDBQCount -= 1
                continue

            # remainingBodyList = body[bodyIdx+1:] if bodyIdx+1<_len else []
            conjunct = EDBQueryFromBodyIterator(
                self.factGraph, rule.formula.body.formulae[bodyIdx:], self.derivedPredicates, self.hybridPredicates
            )

            lazyBaseConjunct = self.pushDownMDBQ and conjunct
            pattern = HashablePatternList([(BFP_RULE[str(idx + 1)], BFP_NS.evaluate, Literal(bodyIdx))])
            pattern2 = HashablePatternList(
                [None, (BFP_RULE[str(idx + 1)], BFP_NS.evaluate, Literal(bodyIdx)), bodyLiteral.toRDFTuple()]
            )
            aNodeDk = self.metaInterpNetwork.nodes[pattern]

            # Rule d^k
            # query_Literal(x0, ..., xj) :- evaluate(ruleNo, j, X)
            # query invokation
            tNode = first(aNodeDk.descendentBetaNodes)
            assert len(aNodeDk.descendentBetaNodes) == 1
            newEvalMemory = SetupEvaluationBetaNode(tNode, rule, self.metaInterpNetwork)

            isBase = bodyLiteral.adornment is None if isinstance(bodyLiteral, AdornedUniTerm) else True
            if isinstance(bodyLiteral, N3Builtin):
                if aNodeDk in self.metaInterpNetwork.alphaNodes:
                    self.metaInterpNetwork.alphaNodes.remove(aNodeDk)
                # evalTerm = (BFP_RULE[str(idx+1)], BFP_NS.evaluate, Literal(bodyIdx))
                del aNodeDk

                executeAction = EvaluateExecution((idx + 1, bodyIdx), self, [])
                builtInNode = self.metaRule2Network[self.bfpLookup[("c", idx + 1, bodyIdx + 1)]]
                assert isinstance(builtInNode.rightNode, BuiltInAlphaNode)
                builtInNode.executeActions[bodyLiteral.toRDFTuple()] = (True, executeAction)

                # We bypass d^k, so when evaluate(ruleNo, j, X) is inferred
                # it is added to left memory of pNode associated with c^k
                self.evalHash.setdefault((idx + 1, bodyIdx), []).append(builtInNode.memories[LEFT_MEMORY])

                self.actionHash.setdefault((idx + 1, bodyIdx + 1), set()).add(builtInNode)
            elif conjunct and (isBase or lazyBaseConjunct):
                singleConj = EDBQuery([copy.deepcopy(item) for item in conjunct], self.factGraph)
                matchingTriple = first(tNode.consequent)
                assert len(tNode.consequent) == 1
                newAction = QueryExecution(
                    self,
                    bodyLiteral,
                    conjoinedTokenMem=self.maxEDBFront2End[(idx + 1, bodyIdx + 1)] if lazyBaseConjunct else None,
                    edbConj=conjunct if lazyBaseConjunct else singleConj,
                )

                self.evalHash.setdefault((idx + 1, bodyIdx), []).append(newEvalMemory)
                # If the body predicate is a 2nd order predicate, we don't infer the
                # second order query predicate (since it will trigger a query into
                # the EDB and thus there is no need to trigger subsequent rules)
                tNode.executeActions[matchingTriple] = (True, newAction)
            else:
                self.evalHash.setdefault((idx + 1, bodyIdx), []).append(newEvalMemory)
            if IsHybridPredicate(bodyLiteral, self.hybridPredicates) or (
                (GetOp(bodyLiteral) in self.derivedPredicates) and not (isBase and len(conjunct) > 1)
            ):
                # if pattern2 not in self.metaInterpNetwork.nodes: import pdb;pdb.set_trace()
                assert pattern2 in self.metaInterpNetwork.nodes
                termNodeCk = self.metaInterpNetwork.nodes[pattern2]
                # Rule c^k
                # evaluate(ruleNo, j+1, X) :- evaluate(ruleNo, j, X), bodyLiteral
                self.actionHash.setdefault((idx + 1, bodyIdx + 1), set()).add(termNodeCk)

                assert isinstance(termNodeCk.rightNode, AlphaNode)
                termNodeCk.leftVariables = set(evalVars[(idx + 1, bodyIdx)])
                termNodeCk.rightVariables = set(GetVariables(bodyLiteral, secondOrder=True))
                termNodeCk.commonVariables = termNodeCk.rightVariables.intersection(termNodeCk.leftVariables)
            if lazyBaseConjunct and len(conjunct) > 1:
                endIdx = self.maxEDBFront2End[(idx + 1, bodyIdx + 1)][-1]
                skipMDBQCount = endIdx - bodyIdx - 1

    def checkNetworkWellformedness(self):
        for key, rule in list(self.bfpLookup.items()):
            if len(key) == 2:
                ruleType, ruleIdx = key
                bodyIdx = None
            else:
                ruleType, ruleIdx, bodyIdx = key

            termNode = self.metaRule2Network[rule]
            headTuple = rule.formula.head.toRDFTuple()

            # p(..) :- q_1(..), q_2(..), ..., q_n(..)
            if GetOp(rule.formula.head) == BFP_NS.evaluate:
                # evaluate(.., ..) :-

                override, executeFn = termNode.executeActions.get(headTuple, (None, None))
                assert override and isinstance(executeFn, EvaluateExecution), termNode
                assert executeFn.ruleNo == ruleIdx
                assert executeFn.bodyIdx == headTuple[-1]
                assert bodyIdx is None or executeFn.bodyIdx == int(bodyIdx), bodyIdx

                if isinstance(rule.formula.body, And):
                    # c^{ruleIdx}_{bodyIdx}
                    # evaluate(.., j+1) :- evaluate(.., j), q_{j+1}(..)
                    # @@ force check builtins or derived predicate c rules
                    if isinstance(termNode.leftNode, AlphaNode):
                        # alphaNode = termNode.leftNode
                        betaNode = termNode.rightNode
                        assert isinstance(termNode.memories[RIGHT_MEMORY], EvaluateConjunctiveQueryMemory), termNode
                        assert isinstance(betaNode, BetaNode)
                    elif not termNode.fedByBuiltin:
                        # alphaNode = termNode.rightNode
                        betaNode = termNode.leftNode

                        assert (
                            isinstance(termNode.memories[LEFT_MEMORY], EvaluateConjunctiveQueryMemory)
                            or self.evalHash[(ruleIdx, bodyIdx - 1)][0].successor != termNode
                        ), termNode
                        assert isinstance(betaNode, BetaNode)
                else:
                    # b^{ruleIdx}
                    # evaluate(.., j+1) :- query-p(..)
                    assert termNode.aPassThru

            elif isinstance(rule.formula.body, And):
                # a^{ruleIdx}
                # p(..) :- query-p(..), evaluate(.., n)
                assert isinstance(termNode.memories[RIGHT_MEMORY], EvaluateConjunctiveQueryMemory)
            else:
                # d^{ruleIdx}_{bodyIdx}
                # query-q_{j+1}(..) :- evaluate(.., j)
                queryLiteral = list(iterCondition(self.rules[ruleIdx - 1].formula.body))[bodyIdx - 1]
                if isinstance(queryLiteral, N3Builtin):
                    headTuple = queryLiteral.toRDFTuple()
                    executeKind = EvaluateExecution
                else:
                    executeKind = QueryExecution
                if GetOp(queryLiteral) not in self.derivedPredicates:
                    override, executeFn = termNode.executeActions.get(headTuple, (None, None))
                    assert override and isinstance(executeFn, executeKind), "%s %s %s" % (
                        termNode.consequent,
                        termNode.executeActions,
                        termNode,
                    )
                    if not isinstance(queryLiteral, N3Builtin):
                        assert executeFn.queryLiteral == queryLiteral
                        # self.bfp.evalHash[self.conjoinedTokenMem]
                        assert executeFn.conjoinedTokenMem[0] == int(ruleIdx), "%s %s %s" % (termNode, executeFn, key)

    def createTopDownReteNetwork(self, debug=False):
        """
        Uses the specialized BFP meta-interpretation rules to build a RETE-UL decision
        network that is modified to support the propagation of bindings from the evaluate
        predicates into a supplimental magic set sip strategy and the generation of subqueries.
        The end result is a bottom-up simulation of SLD resolution with complete, sound, and safe
        memoization in the face of recursion
        """
        for rule in set(self.specializeAdornedRuleset(debug)):
            self.metaRule2Network[rule] = self.metaInterpNetwork.buildNetworkFromClause(rule)
        if debug:
            sortedBFPRules = [
                str("%s : %s") % (key, self.bfpLookup[key])
                for key in sorted(self.bfpLookup, key=lambda items: str(items[1]) + items[0])
            ]
            for _ruleStr in sortedBFPRules:
                print(_ruleStr)

        self.evalHash = {}
        self.actionHash = {}
        evalVars = {}
        self.productions = {}
        for idx, rule in enumerate(self.rules):
            if rule in self.discardedRules:
                continue

            # label = BFP_RULE[str(idx+1)]
            conjunctLength = len(list(iterCondition(rule.formula.body)))

            # Rule a^k
            # p(x0, ..., xn) :- And(query_p(x0, ..., xn) evaluate(ruleNo, n, X))
            currentPattern = HashablePatternList([(BFP_RULE[str(idx + 1)], BFP_NS.evaluate, Literal(conjunctLength))])
            assert rule.declare
            # Find alpha node associated with evaluate condition
            node = self.metaInterpNetwork.nodes[currentPattern]
            # evaluate(k, n, X) is a condition in only 1 bfp rule
            assert len(node.descendentBetaNodes) == 1
            bNode = first(node.descendentBetaNodes)
            assert bNode.leftNode.aPassThru
            assert len(bNode.consequent) == 1
            newEvalMemory = SetupEvaluationBetaNode(bNode, rule, self.metaInterpNetwork)
            self.evalHash.setdefault((idx + 1, conjunctLength), []).append(newEvalMemory)

            if GetOp(rule.formula.head) == GetOp(self.goal):
                # This rule matches a goal, add a solution collecting action
                goalSolutionAction = GoalSolutionAction(self, rule.formula.head.getVarMapping(self.goal))
                bNode.executeActions[rule.formula.head.toRDFTuple()] = (False, goalSolutionAction)

            self.productions.setdefault(GetOp(rule.formula.head), []).append((idx, bNode))

            # Rule b^k
            # evaluate(ruleNo, 0, X) :- query_p(x0, ..., xn)
            _rule = self.bfpLookup[("b", idx + 1)]
            # alpha node associated with query predicate for head of original rule
            _bodyAlphaNode = self.metaInterpNetwork.nodes[HashablePatternList([_rule.formula.body.toRDFTuple()])]

            assert len(_bodyAlphaNode.descendentBetaNodes) == 1
            tNode = first(_bodyAlphaNode.descendentBetaNodes)
            self.actionHash.setdefault((idx + 1, 0), set()).add(tNode)

            # V_{0} = vars(query_p(..))
            headQueryPred = list(iterCondition(_rule.formula.body))[0]
            try:
                evalVars[(idx + 1, 0)] = list(headQueryPred.terms)
            except IndexError:
                raise
                self.discardedRules.add(rule)
                continue

            self.specializeConjuncts(rule, idx, evalVars)

        for (ruleNo, bodyIdx), tNodes in list(self.actionHash.items()):
            # Attach evaluate action to p-node that propagates
            # token to beta memory associated with evaluate(ruleNo, bodyIdx)
            executeAction = EvaluateExecution((ruleNo, bodyIdx), self, tNodes)
            evaluationStmt = (BFP_RULE[str(ruleNo)], BFP_NS.evaluate, Literal(bodyIdx))
            for tNode in tNodes:
                tNode.executeActions[evaluationStmt] = (True, executeAction)
                # executeAction = EvaluateExecution(evalHash, (idx+1, bodyIdx+1), self, termNodeCk)
                # assert len(termNodeCk.consequent)==1
                # termNodeCk.executeAction = (None, executeAction)

        # Fix join variables for BetaNodes involving evaluate predicates
        for idx, rule in enumerate(self.rules):
            if rule in self.discardedRules:
                continue

            # Rule a^k
            # p(x0, ..., xn) :- And(query_p(x0, ..., xn) evaluate(ruleNo, n, X))
            # Join vars = vars(query_p) AND V_{n}
            headQueryPred = self.bfpLookup[("b", idx + 1)].formula.body
            ruleBodyLen = len(list(iterCondition(rule.formula.body)))
            termNode = first(self.evalHash[idx + 1, ruleBodyLen]).successor
            termNode.commonVariables = list(
                set(evalVars[(idx + 1, ruleBodyLen)]).intersection(GetVariables(headQueryPred, secondOrder=True))
            )
            skipMDBQCount = 0
            for bodyIdx, bodyLiteral in enumerate(iterCondition(rule.formula.body)):
                if skipMDBQCount > 0:
                    skipMDBQCount -= 1
                    continue

                if (idx + 1, bodyIdx + 1) in self.actionHash:
                    # Rule c^k
                    # evaluate(ruleNo, j+1, X) :- And(evaluate(ruleNo, j, X) Literal(x0, ..., xj))
                    # Join vars = vars(Literal) AND V_{j}
                    termNode2 = self.actionHash[(idx + 1, bodyIdx + 1)]
                    assert len(termNode2) == 1
                    termNode2 = first(termNode2)
                    termNode2.commonVariables = list(
                        set(evalVars[(idx + 1, bodyIdx)]).intersection(GetVariables(bodyLiteral, secondOrder=True))
                    )
                if (idx + 1, bodyIdx + 1) in self.maxEDBFront2End:
                    endIdx = self.maxEDBFront2End[(idx + 1, bodyIdx + 1)][-1]
                    skipMDBQCount = endIdx - bodyIdx - 1

    def makeDerivedQueryPredicate(self, predicate):
        if isinstance(predicate, AdornedUniTerm):
            newAdornedPred = BFPQueryTerm(predicate, predicate.adornment)
        elif isinstance(predicate, N3Builtin):
            newAdornedPred = BFPQueryTerm(predicate, builtin=predicate)
        else:
            newAdornedPred = BFPQueryTerm(predicate, None)
        if isinstance(newAdornedPred, Uniterm):
            if isinstance(GetOp(newAdornedPred), Variable):
                newAdornedPred.setOperator(HIGHER_ORDER_QUERY)
            newAdornedPred.finalize()
        self.queryPredicates.add(newAdornedPred)
        return newAdornedPred

    def specializeAdornedRuleset(self, debug=False):
        """
        Specialization is applied to the BFP meta-interpreter with respect to the
        rules of the object program. For each rule of the meta-interpreter
        that includes a premise referring to a rule of the object program, one
        specialized version is created for each rule of the object program.

        """
        rules = set()
        for idx, rule in enumerate(self.rules):
            label = BFP_RULE[str(idx + 1)]
            ruleBodyLen = len(list(iterCondition(rule.formula.body)))

            # if rule.isSecondOrder():
            #     if debug:
            #         print("Skipping second order rule (%s): %s" % (idx+1, rule))
            #     continue
            if debug:
                print("\t%s. %s" % (idx + 1, rule))
                for _sip in SIPRepresentation(rule.sip):
                    print("\t\t", _sip)
            newRule1 = self.rule1(rule, label, ruleBodyLen)
            self.bfpLookup[("a", idx + 1)] = newRule1
            rules.add(newRule1)
            newRule2 = self.rule2(rule, label, ruleBodyLen)
            self.bfpLookup[("b", idx + 1)] = newRule2
            rules.add(newRule2)

            # indicate no skipping is ongoing
            skipMDBQCount = -1
            mDBConjFront = None
            # _len = len(rule.formula.body)
            for bodyIdx, bodyLiteral in enumerate(iterCondition(rule.formula.body)):
                bodyPredSymbol = GetOp(bodyLiteral)
                if skipMDBQCount > 0:
                    skipMDBQCount -= 1
                    continue
                elif skipMDBQCount == 0:
                    # finished skipping maximal db conjuncts, mark end of skipped
                    # conjuncts and indicate that no skipping is ongoing
                    self.maxEDBFront2End[mDBConjFront] = (idx + 1, bodyIdx)
                    mDBConjFront = None
                    skipMDBQCount = -1

                evaluateTerm = Uniterm(BFP_NS.evaluate, [label, Literal(bodyIdx + 1)], newNss=self.namespaces)
                priorEvaluateTerm = Uniterm(BFP_NS.evaluate, [label, Literal(bodyIdx)], newNss=self.namespaces)
                conj = EDBQueryFromBodyIterator(
                    self.factGraph, rule.formula.body.formulae[bodyIdx:], self.derivedPredicates, self.hybridPredicates
                )
                if self.pushDownMDBQ and conj:
                    # There is a maximal db conjunction, indicate skipping of rules involving
                    # conjuncts
                    mDBConjFront = (idx + 1, bodyIdx + 1)
                    if len(conj) > 1:
                        skipMDBQCount = len(conj) - 1
                        self.pushDownQueries[mDBConjFront] = EDBQuery(
                            [copy.deepcopy(item) for item in conj], self.factGraph
                        )
                    else:
                        self.maxEDBFront2End[mDBConjFront] = (idx + 1, bodyIdx + 1)
                    if debug and skipMDBQCount > 0:
                        print("maximal db query: ", self.pushDownQueries[mDBConjFront])
                        print("skipping %s literals, starting from the %s" % (bodyIdx + 1, skipMDBQCount))
                    if len(conj) + bodyIdx == len(rule.formula.body):
                        # maximal db conjunction takes up rest of body
                        # tokens should go into (k, n) - where n is the body length
                        self.maxEDBFront2End[mDBConjFront] = (idx + 1, len(rule.formula.body))
                if (
                    not self.pushDownMDBQ
                    or (
                        (bodyPredSymbol in FILTERS and len(conj) == 1)
                        or (
                            bodyPredSymbol in self.derivedPredicates
                            or IsHybridPredicate(bodyLiteral, self.hybridPredicates)
                        )  # and
                        # bodyPredSymbol not in self.hybridPredicates) or (
                        # bodyPredSymbol not in FILTERS and bodyIdx+1 == _len)
                    )
                ) and skipMDBQCount in (1, -1):
                    # Either not pushing down or:
                    # 1. It is a lone filter
                    # 2. It is a derived predicate (need continuation BFP rule)
                    # evaluate(ruleNo, j+1, X) :- evaluate(ruleNo, j, X) bodyLiteral(..)
                    newRule = self.makeAdornedRule(And([priorEvaluateTerm, bodyLiteral]), evaluateTerm)
                    self.bfpLookup[("c", idx + 1, bodyIdx + 1)] = newRule
                    rules.add(newRule)
                elif bodyPredSymbol in FILTERS and len(conj) > 2:
                    raise NotImplementedError(repr(rule))
                if bodyPredSymbol not in FILTERS:
                    # query_Literal(x0, ..., xj) :- evaluate(ruleNo, j, X)
                    # OpenQuery(query_Literal)
                    newRule = self.makeAdornedRule(priorEvaluateTerm, self.makeDerivedQueryPredicate(bodyLiteral))
                    self.bfpLookup[("d", idx + 1, bodyIdx + 1)] = newRule
                    rules.add(newRule)
        return rules

    def makeAdornedRule(self, body, head):
        allVars = set()
        # first we identify body variables
        # bodyVars = set(reduce(lambda x, y:x+y,
        #                       [ list(extractVariables(i, existential=False))
        #                                 for i in iterCondition(body) ]))
        # #then we identify head variables
        # headVars = set(reduce(lambda x, y:x+y,
        #                       [ list(extractVariables(i, existential=False))
        #                                 for i in iterCondition(head) ]))

        return AdornedRule(Clause(body, head), declare=allVars)

    def rule1(self, rule, label, bodyLen):
        """
        'Facts are only generated in a bottom up evaluation of the interpreter if a query has been issued
         for that fact or if an appropriate sub-query has been generated by the metainterpreter
         itself.'

        a^{k}

        p(x0, ..., xn) :- And(query_p(x0, ..., xn) evaluate(ruleNo, n, X))
                            OpenQuery(query_p)

        If there are no bindings posed with the query, then OpenQuery(query_p)
        is used instead of query_p(x0, ..., xn), indicating that there are no bindings
        but we wish to evaluate this derived predicate.  However, despite the fact
        that it has no bindings, we want to continue to (openly) solve predicates
        in a depth-first fashion until we hit an EDB query.

        """
        evaluateTerm = Uniterm(BFP_NS.evaluate, [label, Literal(bodyLen)], newNss=self.namespaces)
        return self.makeAdornedRule(
            And([self.makeDerivedQueryPredicate(rule.formula.head), evaluateTerm]), rule.formula.head
        )

    def rule2(self, rule, label, bodyLen):
        """
        When a query is matched, collect answers (begin to evaluate)

        b^{k}

        evaluate(ruleNo, 0, X) :- query_p(x0, ..., xn)
                                OpenQuery(query_p)

        If there are no bindings posed with the query, then OpenQuery(query_p)
        is used instead of query_p(x0, ..., xn), indicating that there are no bindings
        but we wish to evaluate this derived predicate.  However, despite the fact
        that it has no bindings, we want to continue to (openly) solve predicates
        in a depth-first fashion until we hit an EDB query.

        """
        evaluateTerm = Uniterm(BFP_NS.evaluate, [label, Literal(0)], newNss=self.namespaces)
        return self.makeAdornedRule(self.makeDerivedQueryPredicate(rule.formula.head), evaluateTerm)