Exemplo n.º 1
0
    def initialize_arrows_backwards(self):
        for (node_1, node_2) in self.graph.edges():
            if self.knowledge is not None and not self.knowledge.no_edge_required(
                    node_1, node_2):
                continue
            self.clear_arrow(node_1, node_2)
            self.clear_arrow(node_2, node_1)

            self.calculate_arrows_backward(node_1, node_2)

            self.stored_neighbors[node_1] = graph_util.neighbors(
                self.graph, node_1)
            self.stored_neighbors[node_2] = graph_util.neighbors(
                self.graph, node_2)
Exemplo n.º 2
0
    def bes(self):
        """BES removes edges from the graph generated by FGES, as added edges can now have negative bump in light
        of the additions to the graph after those edges were added."""

        while len(self.sorted_arrows) > 0:
            if self.checkpoint_frequency > 0 and (
                    time.time() -
                    self.last_checkpoint) > self.checkpoint_frequency:
                self.create_checkpoint()
                self.last_checkpoint = time.time()

            arrow = self.sorted_arrows.pop(0)
            x = arrow.a
            y = arrow.b

            if (not (arrow.na_y_x == graph_util.get_na_y_x(self.graph, x, y))) or \
                    (not graph_util.adjacent(self.graph, x, y)) or (graph_util.has_dir_edge(self.graph, y, x)):
                continue

            if not self.valid_delete(x, y, arrow.h_or_t, arrow.na_y_x):
                continue

            H = arrow.h_or_t
            bump = arrow.bump

            self.delete(x, y, H)

            meek_rules = MeekRules(knowledge=self.knowledge)
            meek_rules.orient_implied_subset(self.graph, set([x, y]))

            self.total_score += bump
            self.clear_arrow(x, y)
            if self.verbose:
                print("BES: Removed arrow " + str(x) + " -> " + str(y) +
                      " with bump -" + str(bump))
            visited = self.reapply_orientation(x, y, H)

            to_process = set()

            for node in visited:
                neighbors = graph_util.neighbors(self.graph, node)
                str_neighbors = self.stored_neighbors[node]

                if str_neighbors != neighbors:
                    to_process.update([node])

            to_process.add(x)
            to_process.add(y)
            to_process.update(graph_util.get_common_adjacents(
                self.graph, x, y))

            # TODO: Store graph
            self.reevaluate_backward(to_process)
Exemplo n.º 3
0
    def reevaluate_backward(self, to_process):
        for node in to_process:
            self.stored_neighbors[node] = graph_util.neighbors(
                self.graph, node)
            adjacent_nodes = graph_util.adjacent_nodes(self.graph, node)

            for adj_node in adjacent_nodes:
                if graph_util.has_dir_edge(self.graph, adj_node, node):
                    self.clear_arrow(adj_node, node)
                    self.clear_arrow(node, adj_node)

                    self.calculate_arrows_backward(adj_node, node)
                elif graph_util.has_undir_edge(self.graph, adj_node, node):
                    self.clear_arrow(adj_node, node)
                    self.clear_arrow(node, adj_node)
                    self.calculate_arrows_backward(adj_node, node)
                    self.calculate_arrows_backward(node, adj_node)
Exemplo n.º 4
0
    def calculate_arrows_forward(self, a, b):
        # print("Calculate Arrows Forward: " + str(a) + " " + str(b))
        if b not in self.effect_edges_graph[a] and self.mode == "heuristic":
            print("Returning early...")
            return

        if self.knowledge is not None and self.knowledge.is_forbidden(a, b):
            return

        # print("Get neighbors for " + str(b) + " returns " + str(graph_util.neighbors(self.graph, b)))

        self.stored_neighbors[b] = graph_util.neighbors(self.graph, b)

        na_y_x = graph_util.get_na_y_x(self.graph, a, b)
        _na_y_x = list(na_y_x)

        if not graph_util.is_clique(self.graph, na_y_x):
            return

        t_neighbors = list(graph_util.get_t_neighbors(self.graph, a, b))
        # print("tneighbors for " + str(a) + ", " + str(b) + " returns " + str(t_neighbors))
        len_T = len(t_neighbors)

        def outer_loop():
            previous_cliques = set()  # set of sets of nodes
            previous_cliques.add(frozenset())
            new_cliques = set()  # set of sets of nodes
            for i in range(len_T + 1):

                choices = itertools.combinations(range(len_T), i)
                choices2 = itertools.combinations(range(len_T), i)
                # print("All choices: ", list(choices2), " TNeighbors: ", t_neighbors)
                for choice in choices:
                    T = frozenset([t_neighbors[k] for k in choice])
                    # print("Choice:", T)
                    union = set(na_y_x)
                    union.update(T)

                    found_a_previous_clique = False

                    for clique in previous_cliques:
                        # basically if clique is a subset of union
                        if union >= clique:
                            found_a_previous_clique = True
                            break

                    if not found_a_previous_clique:
                        # Break out of the outer for loop
                        return

                    if not graph_util.is_clique(self.graph, union):
                        continue

                    new_cliques.add(frozenset(union))

                    bump = self.insert_eval(a, b, T, na_y_x)
                    # print("Evaluated arrow " + str(a) + " -> " + str(b) + " with T: " + str(T) + " and bump: " + str(bump));

                    if bump > 0:
                        self.add_arrow(a, b, na_y_x, T, bump)

                previous_cliques = new_cliques
                new_cliques = set()

        outer_loop()
Exemplo n.º 5
0
    def fes(self):
        """The basic workflow of FGES is to first consider add all edges with positive bump, as defined
        by the SEMBicScore, to a sorted list (sorted by bump).
        Edges are popped off this list and added to the graph, after which point the Meek rules are utilized to
        orient edges in the graph that can be oriented. Then, all relevant bumps are recomputed and
        the list is resorted. This process is repeated until there remain no edges to add with positive bump."""
        # print("Running FES.`.")
        # print("Length of sorted arrows", len(self.sorted_arrows))
        # print(self.arrow_dict)
        while len(self.sorted_arrows) > 0:
            if self.checkpoint_frequency > 0 and (
                    time.time() -
                    self.last_checkpoint) > self.checkpoint_frequency:
                self.create_checkpoint()
                self.last_checkpoint = time.time()
            max_bump_arrow = self.sorted_arrows.pop(
                0)  # Pops the highest bump edge off the sorted list
            x = max_bump_arrow.a
            y = max_bump_arrow.b
            # print("Popped arrow: " + str(x) + " -> " + str(y))

            if graph_util.adjacent(self.graph, x, y):
                continue

            na_y_x = graph_util.get_na_y_x(self.graph, x, y)

            # TODO: max degree checks
            # print(na_y_x)

            if max_bump_arrow.na_y_x != na_y_x:
                continue

            # print("Past crucial step")

            if not graph_util.get_t_neighbors(self.graph, x, y).issuperset(
                    max_bump_arrow.h_or_t):
                continue

            if not self.valid_insert(x, y, max_bump_arrow.h_or_t, na_y_x):
                # print("Not valid insert")
                continue

            T = max_bump_arrow.h_or_t
            bump = max_bump_arrow.bump

            # TODO: Insert should return a bool that we check here
            inserted = self.insert(
                x, y, T, bump)  # Insert highest bump edge into the graph
            if not inserted:
                continue

            self.total_score += bump
            # print("Edge set before reapplying orientation: " + str(self.graph.edges()))
            visited_nodes = self.reapply_orientation(
                x, y, None)  # Orient edges appropriately following insertion
            # print("Edge set after reapplying orientation: " + str(self.graph.edges()))
            to_process = set({})

            # check whether the (undirected) neighbors of each node in
            # visited_nodes changed compared to stored neighbors
            for node in visited_nodes:
                # gets undirected neighbors
                new_neighbors = graph_util.neighbors(self.graph, node)
                stored_neighbors = self.stored_neighbors.get(node)
                if stored_neighbors != new_neighbors:
                    to_process.add(node)  # Reevaluate neighbor nodes

            to_process.add(x)  # Reevaluate edges relating to node x
            to_process.add(y)  # Reevaluate edges relating to node y

            self.reevaluate_forward(to_process,
                                    max_bump_arrow)  # Do actual reevaluation