Beispiel #1
0
def graph_equals(
        g1: nx.DiGraph,
        g2: nx.DiGraph,
        weight_column_name: Text = 'weight') -> bool:
    """Checks if two graphs are equal.

    If weight_column_name is None, then it does not check weight values.

    Args:
        g1: First graph to be compared.

        g2: Second graph to be compared.

        weight_column_name: The name of weight column.

    Returns:
        Boolean whether g1 equals g2 or not.

    Raises:
        None.
    """
    if g1.nodes() != g2.nodes():
        return False
    if g1.edges() != g2.edges():
        return False
    if weight_column_name:
        for edge in g1.edges():
            w1 = g1.get_edge_data(edge[0], edge[1])[weight_column_name]
            w2 = g2.get_edge_data(edge[0], edge[1])[weight_column_name]
            if w1 != w2:
                return False
    return True
Beispiel #2
0
def equivalent_singlegraphs(g1_single: nx.DiGraph,
                            g2_single: nx.DiGraph) -> bool:
    return all([
        g1_single.get_edge_data(*e) == g2_single.get_edge_data(*e)
        for e in g1_single.edges()
    ] + [
        g1_single.get_edge_data(*e) == g2_single.get_edge_data(*e)
        for e in g2_single.edges()
    ]) & (g1_single.nodes() == g2_single.nodes())
Beispiel #3
0
def is_gather_scatter(alert_sub_g: nx.DiGraph, is_ordered: bool = True):
    alert_id = alert_sub_g.graph["alert_id"]
    num_accts = alert_sub_g.number_of_nodes()
    out_degrees = alert_sub_g.out_degree()
    in_degrees = alert_sub_g.in_degree()

    orig_accts = [
        n for n, d in out_degrees.items() if d == 1 and in_degrees[n] == 0
    ]
    bene_accts = [
        n for n, d in in_degrees.items() if d == 1 and out_degrees[n] == 0
    ]
    num_orig = len(orig_accts)
    num_bene = len(bene_accts)
    hub_accts = [
        n for n, d in out_degrees.items()
        if d == num_bene and in_degrees[n] == num_orig
    ]
    if len(hub_accts) != 1 or (num_orig + num_bene + 1) != num_accts:
        logging.info("Alert %s is not a gather-scatter pattern" % alert_id)
        return False  # Mismatched the number of accounts

    hub = hub_accts[0]
    last_gather_date = datetime.strptime("1970-01-01", "%Y-%m-%d")
    total_gather_amount = 0.0
    for orig in orig_accts:
        attr = alert_sub_g.get_edge_data(orig, hub)
        if attr is None:
            logging.info(
                "Alert %s is not a gather-scatter pattern: gather edge %s -> %s not found"
                % (alert_id, orig, hub))
            return False  # No gather edges found
        date = attr["date"]
        amount = attr["amount"]
        last_gather_date = max(last_gather_date, date)
        total_gather_amount += amount

    if is_ordered:
        max_scatter_amount = total_gather_amount / num_bene
        for bene in bene_accts:
            attr = alert_sub_g.get_edge_data(hub, bene)
            if attr is None:
                return False
            date = attr["date"]
            amount = attr["amount"]
            if date < last_gather_date:
                logging.info(
                    "Alert %s gather-scatter transactions are chronologically unordered "
                    % alert_id)
                return False
            elif max_scatter_amount <= amount:
                logging.info(
                    "Alert %s gather-scatter transaction amounts are unordered"
                    % alert_id)
                return False

    return True
Beispiel #4
0
 def __increase_flow_in_path(self, path, graph: nx.DiGraph, n):
     for i in reversed(range(1, len(path))):
         if graph.get_edge_data(path[i], path[i - 1]) is not None:
             current_weight = float(
                 graph.get_edge_data(path[i],
                                     path[i - 1])["weight"]) + float(n)
             graph.add_edge(path[i],
                            path[i - 1],
                            weight=str(current_weight))
def add_data_from_single_image_graph_to_cluster_graph(line_graph: nx.DiGraph,
                                                      graph: nx.MultiDiGraph):
    for node in line_graph.adj:
        for neighbour in nx.neighbors(line_graph, node):
            edge = line_graph.get_edge_data(node, neighbour)
            for neighbour2 in nx.neighbors(line_graph, neighbour):
                edge2 = line_graph.get_edge_data(neighbour, neighbour2)
                graph.add_edge(edge['cluster'],
                               edge2['cluster'],
                               key=None,
                               nodes_from=str(node) + "_" + str(neighbour),
                               nodes_to=str(neighbour) + "_" + str(neighbour2))
Beispiel #6
0
def get_bags(g: nx.DiGraph, node: str):
    count = 1
    for n in g.neighbors(node):
        # print(node, n, g.get_edge_data(node, n)['amount'])
        count += g.get_edge_data(node, n)['amount'] * get_bags(g, n)

    return count
Beispiel #7
0
class GraphConversionManager(ConversionManager):
    def __init__(self):
        super().__init__()
        self.conversion_graph = DiGraph()

    def get_conversion_path(self, start_space_name, target_space_name):
        if start_space_name not in self.conversion_graph:
            raise UndefinedColorSpaceError(start_space_name)
        if target_space_name not in self.conversion_graph:
            raise UndefinedColorSpaceError(target_space_name)
        try:
            # Retrieve node sequence that leads from start_space_name to
            # target_space_name.
            path = shortest_path(self.conversion_graph, start_space_name,
                                 target_space_name)
        except NetworkXNoPath:
            raise UndefinedConversionError(
                start_space_name,
                target_space_name,
            )
        # Look up edges between nodes and retrieve the conversion function
        # for each edge.
        cf = 'conversion_function'
        return [self.conversion_graph.get_edge_data(node_a, node_b)[cf]
                for node_a, node_b in zip(path[:-1], path[1:])]

    def add_type_conversion(self, start_space_name, target_space_name,
                            conversion_function):
        super().add_type_conversion(start_space_name,
                                    target_space_name,
                                    conversion_function)
        self.conversion_graph.add_edge(
            start_space_name,
            target_space_name,
            {'conversion_function': conversion_function})
Beispiel #8
0
def count_descends(graph: networkx.DiGraph, node: str) -> int:
    total = 0
    for child, _ in graph.in_edges(node):
        count = 1 + count_descends(graph, child)
        multiplier = graph.get_edge_data(child, node)['count']
        total += multiplier * count
    return total
Beispiel #9
0
        def _transitive_closure(def_: Definition,
                                graph: networkx.DiGraph,
                                result: networkx.DiGraph,
                                visited: Optional[Set[Definition]] = None):
            if def_ in self._transitive_closures.keys():
                return self._transitive_closures[def_]

            predecessors = list(graph.predecessors(def_))

            result.add_node(def_)
            result.add_edges_from(
                list(
                    map(lambda e: (*e, graph.get_edge_data(*e)),
                        map(lambda p: (p, def_), predecessors))))

            visited = visited or set()
            visited.add(def_)
            predecessors_to_visit = set(predecessors) - set(visited)

            closure = reduce(
                lambda acc, definition: _transitive_closure(
                    definition, graph, acc, visited), predecessors_to_visit,
                result)

            self._transitive_closures[def_] = closure
            return closure
Beispiel #10
0
        def _transitive_closure(def_: Definition,
                                graph: networkx.DiGraph,
                                result: networkx.DiGraph,
                                visited: Optional[Set[Definition]] = None):
            """
            Returns a joint graph that comprises the transitive closure of all defs that `def_` depends on and the
            current graph `result`. `result` is updated.
            """
            if def_ in self._transitive_closures.keys():
                closure = self._transitive_closures[def_]
                # merge closure into result
                result.add_edges_from(closure.edges())
                return result

            predecessors = list(graph.predecessors(def_))

            result.add_node(def_)
            result.add_edges_from(
                list(
                    map(lambda e: (*e, graph.get_edge_data(*e)),
                        map(lambda p: (p, def_), predecessors))))

            visited = visited or set()
            visited.add(def_)
            predecessors_to_visit = set(predecessors) - set(visited)

            closure = reduce(
                lambda acc, def0: _transitive_closure(
                    def0, graph, acc, visited), predecessors_to_visit, result)

            self._transitive_closures[def_] = closure
            return closure
Beispiel #11
0
def get_call_track(cfg: DiGraph, node):
    """
    Returns a list containing all the nodes that are placed between the given one and the first CALL edge found
    going upward
    :param cfg:
    :param node:
    :return:
    """
    track = []
    unvisited_edges = set()
    visited_edges = []
    for edge in cfg.in_edges(node[0]):
        track.append(edge[0])
        for edge_t in cfg.in_edges(edge[0]):
            unvisited_edges.add(edge_t)

    while len(unvisited_edges) > 0:
        curr_edge = unvisited_edges.pop()
        if curr_edge[0] not in track:
            track.append(curr_edge[0])
        if cfg.get_edge_data(curr_edge[0],
                             curr_edge[1])['kind'] == Transition.CALL:
            return track

        for edge in cfg.in_edges(curr_edge[0]):
            if edge not in visited_edges:
                unvisited_edges.add(edge)

        visited_edges.append(curr_edge)

    if node in track:
        track.remove(node)

    return track
def add_path_step(line_code: int, current_node, from_node_label: str,
                  step_count: int, line_graph: nx.DiGraph,
                  graph: nx.MultiDiGraph, taboo_list: Set[int]):
    if step_count == 5:
        return

    for neighbour in nx.neighbors(line_graph, current_node):
        if neighbour in taboo_list:
            continue

        edge = line_graph.get_edge_data(current_node, neighbour)
        cluster_number = edge['cluster']
        to_node_label = str(step_count + 1) + '_' + str(cluster_number)

        existing_edges = graph.get_edge_data(from_node_label,
                                             to_node_label,
                                             default=None)
        if not edge_exists(existing_edges, line_code, current_node, neighbour):
            graph.add_edge(from_node_label,
                           to_node_label,
                           line_code=line_code,
                           from_node=current_node,
                           to_node=neighbour,
                           cluster=cluster_number)
            taboo_list.add(neighbour)
            add_path_step(line_code, neighbour, to_node_label, step_count + 1,
                          line_graph, graph, taboo_list)
Beispiel #13
0
def _add_field(graph: nx.DiGraph, parent_node: str, field: FieldDescriptor) -> None:
    field_node: str = _get_node_name(field)
    field_type: str = _get_type_ascription(field)
    if graph.nodes.get(field_node) is None:
        graph.add_node(field_node, node_type=field_type)
    if graph.get_edge_data(parent_node, field_node) is None:
        graph.add_edge(parent_node, field_node, fields=[])
    graph[parent_node][field_node]["fields"].append(field)
Beispiel #14
0
def calculate_happiness(seating_arrangement: Tuple[str],
                        preference_DiGraph: nx.DiGraph) -> int:
    """Calculate the happiness of a given seating arrangement based on the preference DiGraph."""
    happiness_score = 0
    for left, right in zip(seating_arrangement, seating_arrangement[1:]):
        happiness_score += preference_DiGraph.get_edge_data(left,
                                                            right)["happiness"]
        happiness_score += preference_DiGraph.get_edge_data(right,
                                                            left)["happiness"]

    # Wrap tails
    left, right = seating_arrangement[0], seating_arrangement[-1]
    happiness_score += preference_DiGraph.get_edge_data(left,
                                                        right)["happiness"]
    happiness_score += preference_DiGraph.get_edge_data(right,
                                                        left)["happiness"]

    return happiness_score
Beispiel #15
0
def walk_succ(graph: nx.DiGraph, node):
    successors = list(graph.successors(node))
    total = 0

    for succ in successors:
        succ_weight = graph.get_edge_data(node, succ)["weight"]
        total += succ_weight * (1 + walk_succ(graph, succ))

    return total
def add_or_update_edge(
    graph: nx.DiGraph,
    edge: tuple,
    method_name: str,
    method_specified: str,
    score: float
):
    edge_exists = graph.has_edge(*edge)
    edge_rev_exists = graph.has_edge(edge[1], edge[0])
    if not edge_exists and not edge_rev_exists:
        id0 = graph.nodes[edge[0]]['identifiers']['node_network_id']
        id1 = graph.nodes[edge[1]]['identifiers']['node_network_id']
        graph.add_edge(
            *edge,
            edges=[{
                'source': edge[0],
                'target': edge[1],
                'predicted': True,
                'prediction_score':score,
                'applied_methods': {method_name: [method_specified]}
            }],
            edge_color=COLOR_PREDICTED_ONLY,
            identifiers={
                id0: graph.nodes[edge[0]]['identifiers']['node_id'],
                id1: graph.nodes[edge[1]]['identifiers']['node_id'],
                'original_network_id': graph.nodes[edge[0]]['identifiers']['original_network_id'],
                'predicted_network_id': graph.nodes[edge[0]]['identifiers']['predicted_network_id']
            }
        )
    else:
        actual_edge = edge if edge_exists else (edge[1], edge[0])
        single_edges = graph.get_edge_data(*actual_edge)
        add_edge = True
        reverse_edge_predicted = False
        for single_edge in single_edges['edges']:
            if single_edge['source'] == edge[0] and single_edge['target'] == edge[1]:
                if single_edge['predicted'] is True:
                    single_edge['prediction_score'] += score
                    if method_name in single_edge['applied_methods']:
                        single_edge['applied_methods'][method_name].append(method_specified)
                    else:
                        single_edge['applied_methods'][method_name] = [method_specified]
                add_edge = False
            if single_edge['source'] == edge[1] and single_edge['target'] == edge[0] \
                    and single_edge['predicted'] is True:
                reverse_edge_predicted = True
        if add_edge is True:
            single_edges['edges'].append({
                'source': edge[0],
                'target': edge[1],
                'predicted': True,
                'prediction_score': score,
                'applied_methods': {method_name: [method_specified]}
            })
            if reverse_edge_predicted is False:
                single_edges['edge_color'] = COLOR_MIXED
Beispiel #17
0
 def __decrease_flow_in_path(self, path, graph: nx.DiGraph, n):
     for i in range(len(path) - 1):
         current_weight = float(
             graph.get_edge_data(path[i], path[i + 1])["weight"]) - float(n)
         if current_weight == 0:
             graph.remove_edge(path[i], path[i + 1])
         else:
             graph.add_edge(path[i],
                            path[i + 1],
                            weight=str(current_weight))
Beispiel #18
0
    def assemble_record_from_cycle(
        cls,
        cycle: List[str],
        graph: nx.DiGraph,
        annotate_sources: bool = False,
        annotate_junctions: bool = False,
    ) -> SeqRecord:

        source_indices = []
        i = 0

        records = [graph.nodes[n]["record"] for n in cycle]
        stored_features = cls._collect_features(records)

        record = SeqRecord(Seq(""))

        c1 = cycle[-1:] + cycle[:-1]
        c2 = cycle
        c3 = cycle[1:] + cycle[:1]

        for n1, n2, n3 in zip(c1, c2, c3):
            r1, r2, r3 = [graph.nodes[n]["record"] for n in [n1, n2, n3]]

            edata12 = graph.get_edge_data(n1, n2)
            edata23 = graph.get_edge_data(n2, n3)

            s12 = edata12["match"]["top_strand_slice"]
            s23 = edata23["match"]["top_strand_slice"]

            o12 = r2[s12[0] : s12[1]]
            o23 = r3[s23[0] : s23[1]]

            trunc_r2 = slice_with_features(r2, slice(len(o12), -len(o23)))

            if annotate_junctions:
                annotate(o12, "junction: tm {}".format(format_float(edata12["tm"])))
            record += o12
            record += trunc_r2

            source_indices.append((i, i + len(r2)))
            i = i + len(r2) - len(o23)

        for i, (a, b) in enumerate(source_indices):
            if annotate_sources:
                annotate(
                    record,
                    "source: fragment {} ({})".format(i, r2.name),
                    a,
                    b,
                    cyclic=True,
                )

        cls._restore_features(stored_features, record, cyclic=True)
        clean_features(record)
        return record
Beispiel #19
0
def generate_all_cs_dataset(filtered_graph: DiGraph):
    relations = []
    for node in filtered_graph.nodes():
        for neighbor in filtered_graph.neighbors(node):
            relation = filtered_graph.get_edge_data(node, neighbor)['label']
            rs = ''.join(c if c.islower() else ' ' + c
                         for c in relation).lower()[1:]
            relations.append(node + ' ' + rs + ' ' + neighbor)
    with open('datasets/all_cs_217k.txt', 'w') as outfile:
        for relation in relations:
            print(relation, file=outfile)
def solveMaxFlow(G: nx.DiGraph, source: int, sink: int) -> dict:
    """
    Solves the maximum flow problem.
    :param G: directed graph
    :param source: Source node in G
    :param sink: Sink node in G
    :return: Dict of edges and flow values
    """
    maxFlow = Model('MaxFlow')

    # Variable
    X = dict()

    for a in G.edges():
        X[a] = maxFlow.addVar(vtype=GRB.CONTINUOUS,
                              lb=0,
                              ub=G.get_edge_data(*a)['capacity'],
                              name=f'X_{a}')

    B = maxFlow.addVar(vtype=GRB.CONTINUOUS, lb=0, name='B')

    # Objective function
    maxFlow.setObjective(B, sense=GRB.MAXIMIZE)

    # Constraints
    for v in G.nodes():
        if v == source:
            maxFlow.addConstr(
                quicksum(X[a] for a in G.out_edges(v)) -
                quicksum(X[a] for a in G.in_edges(v)), GRB.EQUAL, B)
        elif v == sink:
            maxFlow.addConstr(
                quicksum(X[a] for a in G.out_edges(v)) -
                quicksum(X[a] for a in G.in_edges(v)), GRB.EQUAL, -B)
        else:
            maxFlow.addConstr(
                quicksum(X[a] for a in G.out_edges(v)) -
                quicksum(X[a] for a in G.in_edges(v)), GRB.EQUAL, 0)

    # Solve model
    maxFlow.update()
    maxFlow.optimize()

    if maxFlow.status == GRB.OPTIMAL:
        flows = dict()
        for a in G.edges():
            if maxFlow.getVarByName(f'X_{a}').x > 0:
                flows[a] = maxFlow.getVarByName(f'X_{a}').x
        return flows

    else:
        return dict()
Beispiel #21
0
def assert_graph_equals(
        g1: nx.DiGraph,
        g2: nx.DiGraph,
        weight_column_name: Text = 'weight') -> None:
    """Checks if two graphs are equal.

    If weight_column_name is None, then it does not check weight values.

    Args:
        g1: First graph to be compared.

        g2: Second graph to be compared.

        weight_column_name: The name of weight column.

    Returns:
        None.

    Raises:
        AssertionError: If the two graphs are not equal. It also prints a
        message why they do not match for easier debugging purposes.
    """
    if g1.nodes() != g2.nodes():
        raise AssertionError(
            'Two graphs do not have the same nodes: {} != {}'.format(
                g1.nodes(), g2.nodes()))
    if g1.edges() != g2.edges():
        raise AssertionError(
            'Two graphs do not have the same edges: {} != {}'.format(
                g1.edges(), g2.edges()))
    if weight_column_name:
        for edge in g1.edges():
            w1 = g1.get_edge_data(edge[0], edge[1])[weight_column_name]
            w2 = g2.get_edge_data(edge[0], edge[1])[weight_column_name]
            if w1 != w2:
                raise AssertionError(
                    'Two graphs do not have the same weight at {}: {} != {}'
                    .format(edge, w1, w2))
Beispiel #22
0
def filter_non_commonsense(graph: DiGraph,
                           commonsense_relations: List[str]) -> DiGraph:
    print('Filtering commonsense relations...')
    # Remove edges that are not listed as commonsense
    edges_to_remove = []
    for x, y in graph.edges:
        data = graph.get_edge_data(x, y)
        if data['label'] not in commonsense_relations or data['weight'] == -1:
            edges_to_remove.append((x, y))
    new_graph = copy(graph)
    new_graph.remove_edges_from(edges_to_remove)
    # Remove possible leftover island nodes
    islands = list(isolates(new_graph))
    new_graph.remove_nodes_from(islands)
    return new_graph
Beispiel #23
0
def draw_ComputerSetDiGraph_matplotlib(spsg: nx.DiGraph,
                                       ax,
                                       pos=None,
                                       **kwargs):
    if pos is None:
        pos = nx.spring_layout(spsg)
        # pos = nx.circular_layout(spsg)

    nx.draw(spsg,
            labels={n: node_2_string(n)
                    for n in spsg.nodes()},
            ax=ax,
            node_size=2000,
            node_shape="s",
            pos=pos,
            **kwargs)
    for e in spsg.edges():
        print(spsg.get_edge_data(*e))

    edge_labels = {
        e: compset_2_string(spsg.get_edge_data(*e)["computers"])
        for e in spsg.edges()
    }
    nx.draw_networkx_edge_labels(spsg, ax=ax, edge_labels=edge_labels, pos=pos)
Beispiel #24
0
def solveShortestPath(G: nx.DiGraph, source,  sink) -> list:
    """
    Solves the shortest path problem.
    :param G: directed graph
    :param source: Source node in G
    :param sink: Sink node in G
    :return: List of edges
    """
    shortestPath = Model('ShortestPath')

    # Variable
    X = dict()

    for a in G.edges():
        X[a] = shortestPath.addVar(vtype=GRB.BINARY, name=f'X_{a}')

    # Objective function
    shortestPath.setObjective(quicksum(X[a] * G.get_edge_data(*a)['weight'] for a in G.edges()), GRB.MINIMIZE)

    # Constraints
    for v in G.nodes():
        if v == source:
            shortestPath.addConstr(quicksum(X[a] for a in G.out_edges(v)) - quicksum(X[a] for a in G.in_edges(v)),
                                  GRB.EQUAL, 1)
        elif v == sink:
            shortestPath.addConstr(quicksum(X[a] for a in G.out_edges(v)) - quicksum(X[a] for a in G.in_edges(v)),
                                  GRB.EQUAL, -1)
        else:
            shortestPath.addConstr(quicksum(X[a] for a in G.out_edges(v)) - quicksum(X[a] for a in G.in_edges(v)),
                                  GRB.EQUAL, 0)

    # Solve model
    shortestPath.update()
    shortestPath.optimize()

    if shortestPath.status == GRB.OPTIMAL:
        SPedges = list()
        for a in G.edges():
            if round(shortestPath.getVarByName(f'X_{a}').x, 0) == 1.0:
                SPedges.append(a)
        return SPedges

    else:
        return list()
Beispiel #25
0
def generate_multiple_choice_dataset(choice_matrix_path: str,
                                     filtered_graph: DiGraph,
                                     n_choices=7,
                                     name=''):
    incorrect_choices_dict = {}
    with open(choice_matrix_path, 'r') as matrix_file:
        for line in matrix_file.readlines()[1:]:
            tokens = line.split(',')
            incorrect_choices_dict[tokens[0]] = [
                token for token in tokens[1:] if token != '' and token != '\n'
                and token in commonsense_relations
            ]
    question_tuples = []
    id_number = 0
    counter = collections.Counter()
    for e1 in filtered_graph.nodes():
        for e2 in filtered_graph.neighbors(e1):
            counter.update([(e1, e2)])
            correct_relation = filtered_graph.get_edge_data(e1, e2)['label']
            incorrect_relations = incorrect_choices_dict[correct_relation]
            question_tuples.append(
                (id_number, e1, e2, correct_relation, incorrect_relations))
            id_number += 1
    if max(counter.values()) > 1:
        raise RuntimeError('Some pair of entities appear more than once.')
    r.shuffle(question_tuples)

    with open(name + '-data.jsonl', 'w') as data_file:
        with open(name + '-labels.lst', 'w') as labels_file:
            for id_number, e1, e2, correct_relation, incorrect_relations in question_tuples:
                final_choices: List = r.sample(
                    incorrect_relations, n_choices - 1) + [correct_relation]
                r.shuffle(final_choices)
                correct_index = final_choices.index(correct_relation)
                choice_strings = [
                    '\"sol' + str(i + 1) + '\": \"' +
                    relation_to_string[final_choices[i]] + '\"'
                    for i in range(n_choices)
                ]
                line_to_print = '{\"id\": \"' + str(id_number) + '\", \"e1\": \"' + e1.replace('_',' ') + \
                                '\", \"e2\": \"' + e2.replace('_',' ') + '\", ' + ', '.join(choice_strings) +'}'
                print(line_to_print, file=data_file)
                print(str(correct_index), file=labels_file)
Beispiel #26
0
        def _transitive_closure(def_: Definition, graph: networkx.DiGraph,
                                result: networkx.DiGraph):
            if def_ in self._transitive_closures.keys():
                return self._transitive_closures[def_]

            predecessors = list(graph.predecessors(def_))

            result.add_node(def_)
            result.add_edges_from(
                list(
                    map(lambda e: (*e, graph.get_edge_data(*e)),
                        map(lambda p: (p, def_), predecessors))))

            closure = reduce(
                lambda acc, definition: _transitive_closure(
                    definition, graph, acc), predecessors, result)

            self._transitive_closures[def_] = closure
            return closure
    def _validate_edge_constraints(self, node_isomorphism_map: dict,
                                   graph: nx.DiGraph, constraints: dict):
        """
        Validate all edge constraints on a subgraph.

        Arguments:
            node_isomorphism_map (dict[nodename:str, nodeID:str]): A mapping of
                node names to node IDs (where name comes from the motif and the
                ID comes from the haystack graph).
            graph (nx.DiGraph): The haystack graph
            constraints (dict[(motif_u, motif_v), dict[operator, value]]): Map
                of constraints on the MOTIF node names.

        Returns:
            bool: If the isomorphism satisfies the edge constraints

        For example, if constraints =
        {
            ("A", "B"): {"weight": { "==": 10 }}
        }

        And node_isomorphism_map =
        {
            "A": "x",
            "B": "y",
        }

        And haystack contains the edge (x, y) with attribute weight=10, then
        this function will return True.

        """
        for (motif_U, motif_V), constraint_list in constraints.items():
            # Get graph nodes (from this isomorphism)
            graph_u = node_isomorphism_map[motif_U]
            graph_v = node_isomorphism_map[motif_V]

            # Check edge in graph for constraints
            edge_attrs = graph.get_edge_data(graph_u, graph_v)

            if not _edge_satisfies_constraints(edge_attrs, constraint_list):
                # Fail fast
                return False
        return True
Beispiel #28
0
def _build_paths(g: nx.DiGraph, node: Any, data_lookup: Callable[[Dict[K, V]],
                                                                 V],
                 accumulator: Callable[[V, V], V]):
    r"""Utility for accumulating the set of metadata along every path from a node.

  Example:

      -{f:'a'}-> 2 -{f:'b'}-> 3
     /
    1
     \
      -{f:'d'}-> 4 -{f:'e'}-> 5

  $ _build_paths(g, node=1, data_lookup=lambda d: d['f'], accumulator a, b: a+b)
  > { 2: 'a', 3: 'ba', 4: 'd', 5: 'de' }

  Args:
    g: The DiGraph in question.
    node: The source node
    data_lookup: The lambda which can accept a networkx-style metadata
      dictionary and extract a particular key (and, optionally, transform it).
    accumulator: The lambda which can accept two bits of metadata and join them
      in some arbitrary way.

  Returns:
    A map where the keys are nodes which appear below |node| in the graph, and
    where the values are accumulated results of the list of metadata items
    extracted by |data_lookup| from each edge along the path from |node| down to
    that node.
  """
    result = {}
    for succ in g.successors(node):
        if succ == node:
            continue
        result[succ] = data_lookup(g.get_edge_data(node, succ))
        for subnode, d in _build_paths(g, succ, data_lookup,
                                       accumulator).items():
            result[subnode] = accumulator(d, result[succ])
    return result
Beispiel #29
0
def find_adapter_chain(adapter_graph: nx.DiGraph) -> tuple[int, int]:
    """Find a valid chain of adapters & determine how many 1 & 3 joltage deltas are present."""
    # NOTE: This approach is super overkill for this problem, but I wanted to stick with the graph
    #
    # Since our edges represent a valid adapter connection, we're looking for a Hamiltonian path
    # here: a path that visits every node exactly once. For this problem, since adapter joltage has
    # to be ascending, we can check a topological sort and see if they're all connected.
    n_one_delta = 0
    n_three_delta = 0
    for check_path in nx.all_topological_sorts(adapter_graph):
        # First check that the topological sort has yielded a valid path.
        if not nx.is_path(adapter_graph, check_path):
            continue

        # Let's assume there's only one valid path
        for source, dest in zip(check_path, check_path[1:]):
            delta = adapter_graph.get_edge_data(source, dest)["delta"]
            if delta == 1:
                n_one_delta += 1
            elif delta == 3:
                n_three_delta += 1

    return n_one_delta, n_three_delta
Beispiel #30
0
def getWeight(graph: nx.DiGraph, orig: str, dest: str,
              multiplier: float) -> float:
    """Get the weight of the edge from orig to dest in the graph. This weight is expected to be
    proportional to the movement between nodes. If the edge doesn't have a weight, 1.0 is assumed
    and the returned weight is adjusted by the multiplier and any delta_adjustment on the edge.

    :param graph: A graph with each region as a node and the weights corresponding to the commutes
                  between regions. Edges must contain weight and delta_adjustment attributes (assumed 1.0)
    :param orig: The vertex people are coming from.
    :param dest: The vertex people are going to.
    :param multiplier: Value that will dampen or heighten movements between nodes.
    :return: The final weight value
    """
    edge = graph.get_edge_data(orig, dest)

    # The data loaders force weight and delta_adjustment to always be present
    weight = edge["weight"]
    delta_adjustment = edge["delta_adjustment"]

    delta = weight - (weight * multiplier)
    # The delta_adjustment is applied on the delta. It can either completely cancel any changes (factor = 0.0) or
    # enable it fully (factor = 1.0). If the movement multiplier doesn't make any changes to the node's movements (ie.
    # multiplier = 1.0), then the delta_adjustment will have no effect.
    return weight - (delta * delta_adjustment)
Beispiel #31
0
class TransformTree:
    '''
    A feature complete if not particularly optimized implementation of a transform graph.
    
    Few allowances are made for thread safety, caching, or enforcing graph structure.
    '''

    def __init__(self, base_frame='world'):
        self._transforms = DiGraph()
        self._parents    = {}
        self._paths      = {}
        self._is_changed = False
        self.base_frame  = base_frame

    def update(self, 
               frame_to,
               frame_from = None,
               **kwargs):
        '''
        Update a transform in the tree.

        Arguments
        ---------
        frame_from: hashable object, usually a string (eg 'world').
                    If left as None it will be set to self.base_frame
        frame_to:   hashable object, usually a string (eg 'mesh_0')
        
        Additional kwargs (can be used in combinations)
        --------- 
        matrix:      (4,4) array 
        quaternion:  (4) quatenion
        axis:        (3) array
        angle:       float, radians
        translation: (3) array
        '''
        if frame_from is None:
            frame_from = self.base_frame

        matrix = np.eye(4)
        if 'matrix' in kwargs:
            # a matrix takes precedence over other options
            matrix = kwargs['matrix']
        elif 'quaternion' in kwargs:
            matrix = quaternion_matrix(kwargs['quaternion'])
        elif ('axis' in kwargs) and ('angle' in kwargs):
            matrix = rotation_matrix(kwargs['angle'],
                                     kwargs['axis'])
        else: 
            raise ValueError('Couldn\'t update transform!')

        if 'translation' in kwargs:
            # translation can be used in conjunction with any of the methods of 
            # specifying transforms. In the case a matrix and translation are passed,
            # we add the translations together rather than picking one. 
            matrix[0:3,3] += kwargs['translation']

        if self._transforms.has_edge(frame_from, frame_to):
            self._transforms.edge[frame_from][frame_to]['matrix'] = matrix
            self._transforms.edge[frame_from][frame_to]['time']   = time.time()
        else:
            # since the connectivity has changed, throw out previously computed
            # paths through the transform graph so queries compute new shortest paths
            # we could only throw out transforms that are connected to the new edge,
            # but this is less bookeeping at the expensive of being slower. 
            self._paths = {}
            self._transforms.add_edge(frame_from, 
                                      frame_to, 
                                      matrix = matrix, 
                                      time   = time.time())
        self._is_changed = True

    def get(self,
            frame_to,
            frame_from = None):
        '''
        Get the transform from one frame to another, assuming they are connected
        in the transform tree. 

        If the frames are not connected a NetworkXNoPath error will be raised.

        Arguments
        ---------
        frame_from: hashable object, usually a string (eg 'world').
                    If left as None it will be set to self.base_frame
        frame_to:   hashable object, usually a string (eg 'mesh_0')

        Returns
        ---------
        transform:  (4,4) homogenous transformation matrix
        '''
        if frame_from is None:
            frame_from = self.base_frame

        transform = np.eye(4)
        path, inverted = self._get_path(frame_from, frame_to)
        for i in range(len(path) - 1):
            matrix = self._transforms.get_edge_data(path[i], 
                                                    path[i+1])['matrix']
            transform = np.dot(transform, matrix)
        if inverted:
            transform = np.linalg.inv(transform)
        return transform

    def clear(self):
        self._transforms = DiGraph()
        self._paths      = {}
        
    def _get_path(self, 
                  frame_from,
                  frame_to):
        '''
        Find a path between two frames, either from cached paths or
        from the transform graph. 
        
        Arguments
        ---------
        frame_from: a frame key, usually a string 
                    example: 'world'
        frame_to:   a frame key, usually a string 
                    example: 'mesh_0'

        Returns
        ----------
        path: (n) list of frame keys
              example: ['mesh_finger', 'mesh_hand', 'world']
        inverted: boolean flag, whether the path is traversing stored
                  matrices forwards or backwards. 
        '''
        try: 
            return self._paths[(frame_from, frame_to)]
        except KeyError:
            return self._generate_path(frame_from, frame_to)

    def _generate_path(self, 
                       frame_from, 
                       frame_to):
        '''
        Generate a path between two frames.
        
        Arguments
        ---------
        frame_from: a frame key, usually a string 
                    example: 'world'
        frame_to:   a frame key, usually a string 
                    example: 'mesh_0'

        Returns
        ----------
        path: (n) list of frame keys
              example: ['mesh_finger', 'mesh_hand', 'world']
        inverted: boolean flag, whether the path is traversing stored
                  matrices forwards or backwards. 
        '''
        try: 
            path = shortest_path(self._transforms, frame_from, frame_to)
            inverted = False
        except NetworkXNoPath:
            path = shortest_path(self._transforms, frame_to, frame_from)
            inverted = True
        self._paths[(frame_from, frame_to)] = (path, inverted)
        return path, inverted