Example #1
0
    def decompose(self, alist: Alist, map_op):
        """ Apply a decomposition rule to create successors of an alist

        Args
        ----
        alist : Alist to decompose

        map_op : str
            Name of the map operation to apply to the alist

        Return
        ------
        alist: Alist
            Successor h-node alist that has z-node child alists

        Notes
        -----
        z-nodes are alists that represent facts and contain variables that 
        are to be instantiated with data from knowledge bases.
        h-nodes are alists that have operations to aggregate their child z-nodes.
        Decompositions create z-node and specify the `h` operations for aggregating 
        the decomposed child nodes.

                    (alist)
                       |
                       |
                    (h-node)
                    / ... \\
                   /  ...  \\   
            (z-node1) ... (z-nodeN)

        """
        self.last_heartbeat = time.time()
        if alist.depth + 1 > config.config['max_depth']:
            print('max depth reached!\n')
            alist.state = states.IGNORE
            return alist

        self.write_trace(
            '{blue}{bold}T{thread}{reset} > {op}:{id}-{alist}{resetall}'.
            format(blue=pcol.BLUE,
                   reset=pcol.RESET,
                   bold=pcol.RESET,
                   resetall=pcol.RESETALL,
                   thread=threading.get_ident(),
                   op=map_op[1],
                   alist=alist,
                   id=alist.id))
        alist.branchType = br.OR
        child = map_op[0](alist, self.G)
        # check for query context
        context = alist.get(tt.CONTEXT)
        self.last_heartbeat = time.time()
        if child is not None:
            self.write_trace(
                f'{pcol.BLUE}>> {child.id}{pcol.RESET}-{str(child)}{pcol.RESETALL}'
            )
            succ = self.G.successors(child.id)
            for node_id1 in succ:
                grandchild = self.G.alist(node_id1)
                self.write_trace(
                    f'  {pcol.BLUE}>>> {grandchild.id}{pcol.RESET}-{str(grandchild)}{pcol.RESETALL}'
                )
                reducibleCtr = 0
                succ2 = self.G.successors(grandchild.id)
                for node_id2 in succ2:
                    ggchild = self.G.alist(node_id2)
                    self.write_trace(
                        f'  {pcol.BLUE}>>> {ggchild.id}{pcol.RESET}-{str(ggchild)}{pcol.RESETALL}'
                    )
                    if ggchild.state == states.REDUCIBLE:
                        self.G.add_alist(ggchild)
                        reducibleCtr += 1
            # generate the WHY explanation
            self.explainer.why(self.G, alist, map_op[1])
            return child
        else:
            return None
Example #2
0
def reduce(alist: Alist, children: List[Alist], G: InferenceGraph):
    if not children:
        return None

    nodes_enqueue = []
    nodes_enqueue_process = []

    # get intersection of child values
    common_items = set()
    head, *tail = children
    has_head_children = False
    has_tail_children = False
    for c in G.child_alists(head.id):
        has_head_children = True
        if c.get(tt.OP) != 'comp':
            if c.get(tt.OPVAR).startswith(vx.NESTING):
                common_items.add(str(c.instantiation_value(c.get(tt.OPVAR))))
            else:
                projVars = c.projection_variables()
                if projVars != None:
                    for pvkey, pvval in projVars.items():
                        common_items.add(c.instantiation_value(pvkey))

    for t in tail:
        c_items = set()
        for tc in G.child_alists(t.id):
            has_tail_children = True
            if tc.get(tt.OPVAR).startswith(vx.NESTING):
                c_items.add(str(c.instantiation_value(tc.get(tt.OPVAR))))
            projVars = tc.projection_variables()
            if projVars != None:
                for pvkey, pvval in projVars.items():
                    c_items.add(tc.instantiation_value(pvkey))
        common_items = common_items.intersection(c_items)

    if not common_items and not has_head_children and not has_tail_children:
        for c in children:
            if c.get(tt.OP) != 'comp':
                if c.get(tt.OPVAR).startswith(vx.NESTING):
                    common_items.add(
                        str(c.instantiation_value(c.get(tt.OPVAR))))
                else:
                    projVars = c.projection_variables()
                    if projVars != None:
                        for pvkey, pvval in projVars.items():
                            common_items.add(c.instantiation_value(pvkey))

    if not common_items:
        return None
    else:
        # if common items not empty, ignore existing siblings before creating new siblings
        sibs = G.child_alists(G.parent_alists(alist.id)[0].id)
        for x in sibs:
            if x.id != alist.id:
                # x.prune()
                G.prune(x.id)
                print(
                    f'{pcol.RED}sibling pruned {x.id}{pcol.RESET} {x}{pcol.RESETALL}'
                )

    # setup new sibling branch(s)
    parent = G.parent_alists(alist.id)[0]
    op_alist = parent.copy()
    op_alist.set(alist.get(tt.OPVAR), '')

    op_alist.set(tt.OP, parent.get(tt.OP))
    op_alist.set(tt.OPVAR, parent.get(tt.OPVAR))
    op_alist.set(op_alist.get(tt.OPVAR), '')
    op_alist.state = states.EXPLORED
    # set as an aggregation node to help with display rendering
    op_alist.node_type = nt.HNODE
    G.link(parent, op_alist, 'comp')
    G.link(alist, op_alist, 'set-comp', create_new_id=False)
    nodes_enqueue.append((op_alist, parent, False, 'comp'))
    print(
        f'{pcol.BLUE}set-comp >> {op_alist.id}{pcol.RESET} {op_alist}{pcol.RESETALL}'
    )
    if alist.children:
        nodes_enqueue.append((op_alist, alist, False, 'setcomp'))

    # create children of the new branch
    # copy to avoid using different version from another thread in loop
    op_alist_copy = op_alist.copy()
    for ff in common_items:
        new_sibling: Alist = op_alist_copy.copy()
        new_sibling.set(tt.OP, 'value')
        new_sibling.set(tt.OPVAR, op_alist_copy.get(tt.OPVAR))
        new_sibling.set(alist.get(tt.OPVAR), ff)
        new_sibling.instantiate_variable(alist.get(tt.OPVAR), ff)
        for ref in new_sibling.variable_references(alist.get(tt.OPVAR)):
            if ref not in [tt.OPVAR]:
                new_sibling.set(ref, ff)
        new_sibling.node_type = nt.ZNODE
        G.link(op_alist, new_sibling, 'comp_lookup')
        nodes_enqueue_process.append(
            (new_sibling, op_alist, True, 'comp_lookup'))
        print(
            f'{pcol.BLUE}  set-comp-child >>> {new_sibling.id}{pcol.RESET} {new_sibling}{pcol.RESETALL}'
        )

    alist.state = states.IGNORE
    alist.nodes_to_enqueue_only = nodes_enqueue
    alist.nodes_to_enqueue_and_process = nodes_enqueue_process
    return alist
Example #3
0
    def run_frank(self, alist: Alist):
        """ Run the FRANK algorithm for an alist

        Args
        ----
        alist : Alist

        Return
        ------
        Return True if the instantiation of the project variable of an alist 
        is propagated to the root node.

        Notes
        -----
        For the given alist, attempt to instantiate projection 
        variable. If instantiation is successful, attempt to reduce
        Else decompose and add new children to queue
        """
        self.last_heartbeat = time.time()
        curr_propagated_alists = []
        self.max_depth = alist.depth
        if alist.state is states.PRUNED:
            self.write_trace(
                f"{pcol.RED}ignore pruned {alist.id}{pcol.RESET}-{alist}{pcol.RESETALL}"
            )
            return propagated_alists

        bool_result = False
        proj_vars = alist.projection_variables()
        proj_instantiated = True
        if proj_vars:
            for p, _ in proj_vars.items():
                proj_instantiated = proj_instantiated and alist.is_instantiated(
                    p)

        agg_vars = alist.get(tt.OPVAR).split(' ')
        agg_instantiated = True
        if agg_vars:
            for v in agg_vars:
                agg_instantiated = agg_instantiated and alist.is_instantiated(
                    v)
            if agg_instantiated:
                bool_result = True

        if bool_result:
            alist.state = states.REDUCIBLE
            self.G.add_alist(alist)
        # if OPVAR not instantiated, search KB
        elif not bool_result:
            bool_result = self.search_kb(alist)
        # search kb for the variable instantiation

        if bool_result:
            is_propagated = False
            if alist.state != states.REDUCIBLE:  # if in reducible state, don't change
                alist.state = states.EXPLORED
                self.G.add_alist(alist)
            if agg_instantiated and proj_instantiated:
                alist.state = states.REDUCIBLE
                self.G.add_alist(alist)
            if self.G.child_ids(alist.id):
                is_propagated = self.propagate(self.G.child_ids(alist.id)[0])
            else:
                is_propagated = self.propagate(
                    self.G.child_ids(self.G.parent_ids(alist.id)[0])[0])

            if is_propagated:
                prop_alist = self.G.alist(self.root.id)
                self.write_trace(
                    f"{pcol.CYAN}intermediate ans: "
                    f"{pcol.RESET}-{prop_alist}{pcol.RESETALL}",
                    loglevel=processLog.LogLevel.ANSWER)
                curr_propagated_alists.append(prop_alist.copy())
                self.propagated_alists.append(prop_alist.copy())
        else:
            alist.state = states.EXPLORED
            self.G.add_alist(alist)
            for mapOp in self.get_map_strategy(alist):
                self.decompose(alist, mapOp)
        return curr_propagated_alists