Ejemplo n.º 1
0
    def __init__(self, F, arg):
        """
        If arg is a list:
        Generate the core graph corresponding to the group generated by group_gens.
        If arg is a graph:
        Check for validity and do all folds.
        """
        assert is_FreeGroup(F), "F must be a free group"
        self.F = F
        self.F_rank = F.rank()
        self.letter_types = range(1, self.F_rank + 1)
        self.letters = list(range(-self.F_rank, 0)) + list(range(1, self.F_rank + 1))
        # -r, ..., -1, 1, ..., r

        if isinstance(arg, list):
            group_gens = arg
            assert all([gen in F for gen in group_gens]), "The generators must be elements of F."
            self.group_gens = group_gens

            G = MultiDiGraph()
            G.add_node((0,))  # the marked vertex (id)
            for i, gen in enumerate(self.group_gens):
                word = gen.Tietze()
                word_len = len(word)
                # new_nodes = [(i, j) for j in range(1, word_len)]
                # G.add_nodes_from(new_nodes)
                get_node = lambda j: (0,) if (j % word_len == 0) else (i, j)
                for j in range(word_len):
                    G.add_edge(get_node(j), get_node(j + 1), label=word[j])
                    G.add_edge(get_node(j + 1), get_node(j), label=-word[j])

        elif isinstance(arg, MultiDiGraph):
            # We are going to copy the graph, add reverse edges when needed,
            # and sort the edges.
            # The reason we sort the edges is to get a "canonical" version
            # of the object, so subgroup_gens would be the same in different
            # objects with the same graph.
            G = MultiDiGraph()
            G.add_nodes_from(arg.nodes())
            edges = arg.edges(data='label')
            G_edges = [e for e in edges]
            assert len(edges) == len(arg.edges()), "Every edge has to be labelled."
            for src, dst, letter in edges:
                assert letter in self.letters, \
                    f"The edge betwen {src} and {dst} has an invalid label"
                if (dst, src, -letter) not in G.edges(data='label'):
                    G_edges.append((dst, src, -letter))
            G.add_weighted_edges_from(sorted(G_edges), weight='label')

        else:
            raise ValueError("arg must be a list of words or a MultiDiGraph.")

        self.G = do_all_folds(G)

        # The subgraph of positive edges
        G_pos = MultiDiGraph()
        G_pos.add_edges_from([e for e in self.G.edges(data=True) if e[2]['label'] > 0])
        self.G_pos = G_pos

        self.subgroup_gens = tuple(sorted(self.get_subgroup()))
Ejemplo n.º 2
0
def scale_free_graph(n, alpha=0.41, beta=0.54, delta_in=0.2, delta_out=0):
    def _choose_node(G, distribution, delta):
        cumsum = 0.0
        psum = float(sum(
            distribution.values())) + float(delta) * len(distribution)
        r = random.random()
        for i in range(0, len(distribution)):
            cumsum += (distribution[i] + delta) / psum
            if r < cumsum:
                break
        return i

    G = MultiDiGraph()
    G.add_edges_from([(0, 1), (1, 2), (2, 0)])
    gamma = 1 - alpha - beta

    while len(G) < n:
        r = random.random()
        if r < alpha:
            v = len(G)
            w = _choose_node(G, G.in_degree(), delta_in)
        elif r < alpha + beta:
            v = _choose_node(G, G.out_degree(), delta_out)
            w = _choose_node(G, G.in_degree(), delta_in)
        else:
            v = _choose_node(G, G.out_degree(), delta_out)
            w = len(G)
        G.add_edge(v, w)
    return G
Ejemplo n.º 3
0
def add_ref_edges(g: nx.MultiDiGraph, node):
    """Add edges with attr `data` for data references of the given node"""

    if isinstance(node, clang.graph.StmtInfo):
        for ref_rel in node.ref_relations:
            g.add_node(ref_rel, attr=(filter_type(ref_rel.type)))
            g.add_edge(node, ref_rel, attr="data")
Ejemplo n.º 4
0
    def replace_pattern(self, graph: nx.MultiDiGraph, match: dict):
        input = match['input']
        lstm = match['lstm']
        params = match['params'].value.copy()
        hidden_state = match['hidden_state']
        cell_state = match['cell_state']

        hidden_state_edge_attrs = deepcopy(graph.get_edge_data(hidden_state.id, lstm.id)[0])
        cell_state_edge_attrs = deepcopy(graph.get_edge_data(cell_state.id, lstm.id)[0])

        graph.remove_edge(match['params'].id, lstm.id)
        graph.remove_edge(match['hidden_state'].id, lstm.id)
        graph.remove_edge(match['cell_state'].id, lstm.id)

        self.repack_weights(graph, input, lstm, params)

        reshape = Reshape(graph, dict(dim=[lstm.in_node(0).shape[0], lstm.hidden_size]))

        if len(lstm.in_nodes()) > 2:
            hidden_state_edge_attrs['in'] = 3
            new_init_h = reshape.create_node_with_data([hidden_state], attrs=dict(name=lstm.name + '/HiddenStateResize'))
            graph.add_edge(new_init_h.id, lstm.id, **hidden_state_edge_attrs)

        if len(lstm.in_nodes()) > 3:
            cell_state_edge_attrs['in'] = 4
            new_init_c = reshape.create_node_with_data([cell_state], attrs=dict(name=lstm.name + '/CellStateResize'))
            graph.add_edge(new_init_c.id, lstm.id, **cell_state_edge_attrs)
Ejemplo n.º 5
0
def _add_cross_feed_edges(G: nx.MultiDiGraph, sid_lookup: Dict[str, str],
                          cross_feed_edges: pd.DataFrame,
                          walk_speed_kmph: float) -> nx.MultiDiGraph:
    # Add the cross feed edge connectors to the graph to
    # capture transfer points

    for i, row in cross_feed_edges.iterrows():
        # Extract the row column values as discrete variables
        sid = row.stop_id
        to = row.to_node
        d = row.distance

        # Use the lookup table to get converted stop id name
        full_sid = sid_lookup[sid]

        # Convert to km/hour
        kmph = (d / 1000) / walk_speed_kmph

        # Convert to seconds
        in_seconds = kmph * 60 * 60

        # And then add it to the graph
        G.add_edge(full_sid, to, length=in_seconds, mode='walk')
        # also add reverse edge
        G.add_edge(to, full_sid, length=in_seconds, mode='walk')
def _add_entry_point_for_graph(graph: nx.MultiDiGraph, tag=True):
    entry_points = []

    name = graph.graph["name"]
    for node_id in graph.nodes:
        if graph.in_degree(node_id) == 0:  # 入口节点
            entry_points.append(node_id)

    if tag is True:
        graph.add_node("{}@entry".format(name),
                       label="ENTRY",
                       type="ENTRY",
                       expression="ENTRY")
    else:
        graph.add_node("entry",
                       label="ENTRY",
                       type="ENTRY",
                       expression="ENTRY")

    for entry_point in entry_points:
        if tag is True:
            graph.add_edge("{}@entry".format(name), entry_point)
        else:
            graph.add_edge("entry", entry_point)

    return graph
def scale_free_graph(n, alpha=0.41,beta=0.54,delta_in=0.2,delta_out=0):
    def _choose_node(G,distribution,delta):
        cumsum = 0.0
        psum = float(sum(distribution.values()))+float(delta)*len(distribution)
        r = random.random()
        for i in range(0, len(distribution)):
            cumsum += (distribution[i]+delta)/psum
            if r < cumsum:
                break
        return i

    G = MultiDiGraph()
    G.add_edges_from([(0,1),(1,2),(2,0)])
    gamma = 1 - alpha - beta

    while len(G)<n:
        r = random.random()
        if r < alpha:
            v = len(G)
            w = _choose_node(G, G.in_degree(),delta_in)
        elif r < alpha+beta:
            v = _choose_node(G, G.out_degree(),delta_out)
            w = _choose_node(G, G.in_degree(),delta_in)
        else:
            v = _choose_node(G, G.out_degree(),delta_out)
            w = len(G)
        G.add_edge(v,w)
    return G
Ejemplo n.º 8
0
  def MaybeAddSingleExitBlock(self, g: nx.MultiDiGraph) -> None:
    """Add a magic exit block to unite the exit statements of a CFG.

    Args:
      g: The graph to add the exit block to.
    """
    exit_blocks = list(nx_utils.ExitBlockIterator(g))
    if not exit_blocks:
      raise ValueError("No exit blocks found in graph!")

    if (
      self.only_add_entry_and_exit_blocks_if_required and len(exit_blocks) == 1
    ):
      exit_block = exit_blocks[0][0]
    else:
      exit_block = f"{g.name}_exit"
      g.add_node(
        exit_block, name=exit_block, type="magic", x=self.dictionary["!MAGIC"]
      )
      # Connect exit blocks.
      for node, data in exit_blocks:
        g.add_edge(node, exit_block, flow="control")
      # Add a dataflow edge out, if there is one.
      for src, dst, data in nx_utils.DataFlowEdgeIterator(g):
        if dst == node:
          g.add_edge(node, exit_block, flow="data")
          break
    g.exit_block = exit_block
Ejemplo n.º 9
0
  def MaybeAddDataFlowElements(
    self, g: nx.MultiDiGraph, tag_hook: typing.Optional[llvm_util.TagHook]
  ) -> None:
    if self.dataflow == "none":
      return

    prefix = lambda s: f"{g.name}_{s}"
    unprefix = lambda s: s[len(f"{g.name}_") :]

    # Collect the edges to add so that we don't modify the graph while
    # iterating.
    edges_to_add: typing.List[typing.Tuple[str, str, str, int]] = []

    for statement, data in nx_utils.StatementNodeIterator(g):
      # TODO(github.com/ChrisCummins/ProGraML/issues/9): Separate !IDENTIFIER
      # and !IMMEDIATE uses.
      def_, uses = GetLlvmStatementDefAndUses(
        data["text"], store_destination_is_def=self.store_destination_is_def
      )
      if def_:  # Data flow out edge.
        def_name = f"{prefix(def_)}_operand"
        edges_to_add.append(
          (statement, def_name, def_name, 0, def_, data, "def")
        )
      for position, identifier in enumerate(uses):  # Data flow in edge.
        identifier_name = f"{prefix(identifier)}_operand"
        edges_to_add.append(
          (
            identifier_name,
            statement,
            identifier_name,
            position,
            identifier,
            data,
            "use",
          )
        )

    for (
      src,
      dst,
      identifier,
      position,
      name,
      original_node,
      dtype,
    ) in edges_to_add:
      g.add_edge(src, dst, flow="data", position=position)
      node = g.nodes[identifier]
      # TODO(github.com/ChrisCummins/ProGraML/issues/9): Separate !IDENTIFIER
      # and !IMMEDIATE nodes.
      node["type"] = "identifier"
      node["name"] = name
      node["text"] = name
      node["x"] = self.dictionary["!IDENTIFIER"]

      if tag_hook is not None:
        other_attrs = tag_hook.OnIdentifier(original_node, node, dtype) or {}
        for attrname, attrval in other_attrs.items():
          node[attrname] = attrval
Ejemplo n.º 10
0
def get_connection_multigraph(connections):
    G = MultiDiGraph()
    for c in connections:
        dp1 = c.dp1
        dp2 = c.dp2
        G.add_edge(dp1, dp2, s1=c.s1)
    return G
Ejemplo n.º 11
0
def get_connection_multigraph_weighted(name2dp, connections):
    G = MultiDiGraph()
    for c in connections:
        dp1 = c.dp1
        dp2 = c.dp2
        if not G.has_edge(dp1, dp2):
            already = []
            G.add_edge(dp1, dp2)
        else:
            already = G.edge[dp1][dp2]['spaces']
        R = name2dp[c.dp1].get_rtype(c.s1)
        already.append(R)
        G.edge[dp1][dp2]['spaces'] = already

#     cycles = list(simple_cycles(G))
#     for cycle in cycles:
#         cycle = list(cycle)
#         cycle = cycle + [cycle[0]]
#         
#         for i in range(len(cycle) - 1):
#             # XXX
#             _val = G.edge[cycle[i]][cycle[i + 1]]['spaces']
#             # print('%s -> %s -> %s' % (cycle[i], val, cycle[i + 1]))

    return G
    
Ejemplo n.º 12
0
def _add_nodes_and_edges(G: nx.MultiDiGraph,
                         name: str,
                         stops_df: pd.DataFrame,
                         summary_edge_costs: pd.DataFrame,
                         bidirectional: bool = False) -> Dict[str, str]:
    # As we convert stop ids to actual nodes, let's keep track of those names
    # here so that we can reference them when we add connector edges across
    # the various feeds loaded into the graph
    sid_lookup = {}

    for i, row in stops_df.iterrows():
        sid = str(row.stop_id)
        full_sid = nameify_stop_id(name, sid)

        # Add to the lookup crosswalk dictionary
        sid_lookup[sid] = full_sid
        G.add_node(full_sid,
                   boarding_cost=row.avg_cost,
                   y=row.stop_lat,
                   x=row.stop_lon)

    for i, row in summary_edge_costs.iterrows():
        sid_fr = nameify_stop_id(name, row.from_stop_id)
        sid_to = nameify_stop_id(name, row.to_stop_id)
        G.add_edge(sid_fr, sid_to, length=row.edge_cost, mode='transit')

        # If want to add both directions in this step, we can
        # by reversing the to/from node order on edge
        if bidirectional:
            G.add_edge(sid_to, sid_fr, length=row.edge_cost, mode='transit')

    return sid_lookup
Ejemplo n.º 13
0
def EdmondsKarp(layered_network: LayeredGraph,
                network: Graph,
                s: int,
                t: int,
                capacity_label: str = 'c') -> int:
    # unite_sinks(network=layered_network, t=t)
    # united_sink = layered_network.max_node * layered_network.layers
    # cut_dead_ends(layered_network, s=s, t=t)

    source_net = MultiDiGraph()
    for src, dst, key, data in network.edges(data=True, keys=True):
        new_key = data[ArcAttrs.arc_type]
        source_net.add_edge(src, dst, key=new_key, **data)
        source_net[src][dst][new_key][
            ArcAttrs.res_capacity] = data[capacity_label]

    flow_network = build_flow_network(
        layered_network)  # create auxiliary network with reversed arcs

    while True:
        path = search_path(flow_network=flow_network,
                           source_network=source_net,
                           s=s,
                           t=t)
        if path:
            augment(path, flow_network=flow_network, source_network=source_net)
        else:
            return gather_in_flows(node=t, flow_network=flow_network)
Ejemplo n.º 14
0
def get_connection_multigraph(connections):
    G = MultiDiGraph()
    for c in connections:
        dp1 = c.dp1
        dp2 = c.dp2
        G.add_edge(dp1, dp2, s1=c.s1)
    return G
Ejemplo n.º 15
0
def get_connection_multigraph_weighted(name2dp, connections):
    G = MultiDiGraph()
    for c in connections:
        dp1 = c.dp1
        dp2 = c.dp2
        if not G.has_edge(dp1, dp2):
            already = []
            G.add_edge(dp1, dp2)
        else:
            already = G.edge[dp1][dp2]['spaces']
        R = name2dp[c.dp1].get_rtype(c.s1)
        already.append(R)
        G.edge[dp1][dp2]['spaces'] = already

#     cycles = list(simple_cycles(G))
#     for cycle in cycles:
#         cycle = list(cycle)
#         cycle = cycle + [cycle[0]]
#
#         for i in range(len(cycle) - 1):
#             # XXX
#             _val = G.edge[cycle[i]][cycle[i + 1]]['spaces']
#             # print('%s -> %s -> %s' % (cycle[i], val, cycle[i + 1]))

    return G
Ejemplo n.º 16
0
def axiom(G: networkx.MultiDiGraph):
    axioms = ("(i)nitial", "(w)eakening", "(s)ubstitute")
    while command := get_required(f"{'|'.join(axioms)}").lower():
        print()
        if command == "i":
            wffs = get_formulas()
            G.add_node(Sequent(wffs, wffs))
            return
        elif command == "w":
            sequent = select(G)
            if sequent:
                sides = {"(l)eft", "(r)ight"}
                while side := get_required(f"{'|'.join(sides)}") in sides:
                    if side == "l":
                        print("<<weakening left side>>")
                        wffs = get_formulas()

                        weakened = sequent
                        for wff in wffs:
                            weakened = InferenceRules.l_weakening(
                                weakened, wff)
                        G.add_node(weakened)
                        G.add_edge(sequent, weakened, label="(l_weakening)")
                        return
                    elif side == "r":
                        print("<<weakening right side>>")
                        wffs = get_formulas()

                        weakened = sequent
                        for wff in wffs:
                            weakened = InferenceRules.r_weakening(
                                weakened, wff)
                        G.add_node(weakened)
                        G.add_edge(sequent, weakened, label="(r_weakening)")
                        return
Ejemplo n.º 17
0
def pad_op_transform(graph: nx.MultiDiGraph, match: dict):
    op = match['op']
    pad_op = match['pad_op']
    input_data = pad_op.in_node(0)
    pads = pad_op.in_node(1).value if len(
        pad_op.in_nodes()) == 2 else pad_op.pads

    if pad_op.mode != 'constant':
        log.info(
            'The pad node "{}" with pad mode "{}" cannot be fused.'.format(
                pad_op.soft_get('name'), pad_op.mode))
        return

    if pad_op.mode == 'constant' and pad_op.fill_value != 0.0:
        log.info('The pad node "{}" with non-zero fill value cannot be fused.'.
                 format(pad_op.soft_get('name')))
        return

    input_tensor_dims = len(match['pad_output'].shape)
    if np.any(pads[get_features_dim(op.graph.graph['layout'],input_tensor_dims)] != 0) or \
            np.any(pads[get_batch_dim(op.graph.graph['layout'], input_tensor_dims)] != 0):
        log.info(
            'The pad node "{}" with padding over feature/batch dimension cannot be fused.'
            .format(pad_op.soft_get('name')))
        return

    op.pad += pads
    op.pad_spatial_shape = op.pad[op.spatial_dims]
    op['auto_pad'] = None
    assert (graph[match['pad_output'].node][match['op'].node][0]['in'] == 0)
    edge_attrs = graph.get_edge_data(match['pad_output'].id, match['op'].id)[0]
    graph.remove_edge(match['pad_output'].id, match['op'].id)
    graph.add_edge(input_data.id, match['op'].id, **{'in': 0, **edge_attrs})
Ejemplo n.º 18
0
def add_all_edges(g1: nx.MultiDiGraph, g2: nx.MultiDiGraph, preserve: bool = True) -> int:
    """
    Add all edges from source graph (``g2``) to target graph (``g1``).

    Parameters
    ----------
    g1: networkx.MultiDiGraph
        Target graph
    g2: networkx.MultiDiGraph
        Source graph
    preserve: bool
        Whether or not to preserve conflicting properties

    Returns
    -------
    int
        Number of edges merged during this operation

    """
    logging.info(f"Adding {g2.number_of_edges()} edges from {g2} to {g1}")
    merge_count = 0
    for u, v, key, data in g2.edges(keys=True, data=True):
        if g1.has_edge(u, v, key):
            merge_edge(g1, u, v, key, data, preserve)
            merge_count += 1
        else:
            g1.add_edge(u, v, key, **data)
    return merge_count
Ejemplo n.º 19
0
def get_graphs():
    g1 = MultiDiGraph()
    g1.name = 'Graph 1'
    g1.add_node('A', id='A', name='Node A', category=['biolink:NamedThing'])
    g1.add_node('B', id='B', name='Node B', category=['biolink:NamedThing'])
    g1.add_node('C', id='C', name='Node C', category=['biolink:NamedThing'])
    g1.add_edge('C', 'B', key='C-biolink:subclass_of-B', edge_label='biolink:sub_class_of', relation='rdfs:subClassOf')
    g1.add_edge('B', 'A', key='B-biolink:subclass_of-A', edge_label='biolink:sub_class_of', relation='rdfs:subClassOf', provided_by='Graph 1')

    g2 = MultiDiGraph()
    g2.name = 'Graph 2'
    g2.add_node('A', id='A', name='Node A', description='Node A in Graph 2', category=['biolink:NamedThing'])
    g2.add_node('B', id='B', name='Node B', description='Node B in Graph 2', category=['biolink:NamedThing'])
    g2.add_node('C', id='C', name='Node C', description='Node C in Graph 2', category=['biolink:NamedThing'])
    g2.add_node('D', id='D', name='Node D', description='Node D in Graph 2', category=['biolink:NamedThing'])
    g2.add_node('E', id='E', name='Node E', description='Node E in Graph 2', category=['biolink:NamedThing'])
    g2.add_edge('B', 'A', key='B-biolink:related_to-A', edge_label='biolink:related_to', relation='biolink:related_to')
    g2.add_edge('D', 'A', key='D-biolink:related_to-A', edge_label='biolink:related_to', relation='biolink:related_to')
    g2.add_edge('E', 'A', key='E-biolink:related_to-A', edge_label='biolink:related_to', relation='biolink:related_to')


    g3 = MultiDiGraph()
    g3.name = 'Graph 3'
    g3.add_edge('F', 'E', key='F-biolink:same_as-E', edge_label='biolink:same_as', relation='OWL:same_as')

    return [g1, g2, g3]
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)
Ejemplo n.º 21
0
def relationship_edges(schema_graph_nx: nx.MultiDiGraph, class_add_mod: dict,
                       **kwargs) -> nx.MultiDiGraph:
    """
    Notes:
    =====
    # pass the below dictionary as the third argument (kwargs) to relationship_edges().
    # "in" indicates that the relationship has an in-edges behaviour.
    # "out" indicates that the relationship has an out-edges behaviour.

    rel_dict = {
        "rdfs:subClassOf": {
            "parentOf": "in"
        },
        "schema:domainIncludes": {
            "domainValue": "in"
        },
        "sms:requiresDependency": {
            "requiresDependency": "out"
        },
        "sms:requiresComponent": {
            "requiresComponent": "out"
        },
        "schema:rangeIncludes": {
            "rangeValue": "out"
        }
    }
    """
    for rel, rel_lab_node_type in kwargs.items():
        for rel_label, node_type in rel_lab_node_type.items():
            if rel in class_add_mod:
                parents = class_add_mod[rel]
                if type(parents) == list:
                    for _parent in parents:

                        if node_type == "in":
                            n1 = extract_name_from_uri_or_curie(_parent["@id"])
                            n2 = class_add_mod["rdfs:label"]

                        if node_type == "out":
                            n1 = class_add_mod["rdfs:label"]
                            n2 = extract_name_from_uri_or_curie(_parent["@id"])

                        # do not allow self-loops
                        if n1 != n2:
                            schema_graph_nx.add_edge(n1, n2, key=rel_label)
                elif type(parents) == dict:
                    if node_type == "in":
                        n1 = extract_name_from_uri_or_curie(parents["@id"])
                        n2 = class_add_mod["rdfs:label"]

                    if node_type == "out":
                        n1 = class_add_mod["rdfs:label"]
                        n2 = extract_name_from_uri_or_curie(parents["@id"])

                    # do not allow self-loops
                    if n1 != n2:
                        schema_graph_nx.add_edge(n1, n2, key=rel_label)

    return schema_graph_nx
Ejemplo n.º 22
0
def convert_grandalf_graph_to_networkx_graph(G):
    from networkx import MultiDiGraph
    nxg = MultiDiGraph()
    for v in G.V():
        nxg.add_node(v.data)
    for e in G.E():
        nxg.add_edge(e.v[0].data, e.v[1].data)
    return nxg
Ejemplo n.º 23
0
def add_edge(mygraph: MultiDiGraph, edge: str, origincolor: str,
             colormap: dict) -> MultiDiGraph:
    edge = reduce(str.removesuffix, [" bags", " bag"], edge)
    origincolornum = colormap.get(origincolor)
    num, tcolor = edge.split(" ", 1)
    for _ in range(int(num)):
        mygraph.add_edge(origincolornum, colormap.get(tcolor))
    return mygraph
Ejemplo n.º 24
0
def convert_grandalf_graph_to_networkx_graph(G):
    from networkx import MultiDiGraph
    nxg = MultiDiGraph()
    for v in G.V():
        nxg.add_node(v.data)
    for e in G.E():
        nxg.add_edge(e.v[0].data, e.v[1].data)
    return nxg
Ejemplo n.º 25
0
def get_meausurements_graph(po: PlacedObject) -> MultiDiGraph:
    G = MultiDiGraph()
    for name, sr in iterate_measurements_relations((), po):
        a = sr.a
        b = sr.b
        attr_dict = dict(sr=sr)
        G.add_edge(a, b, attr_dict=attr_dict)
    return G
Ejemplo n.º 26
0
def simplify_twoways(n, g: nx.MultiDiGraph, check_lanes: bool):
    edge_u1 = list(g.out_edges(n, data=True))[0][:2]
    edge_u2 = list(g.out_edges(n, data=True))[1][:2]
    temp1 = reversed(edge_u1)
    edge_u1 = tuple(temp1)
    temp2 = reversed(edge_u2)
    edge_u2 = tuple(temp2)
    new_id_out = list(g.out_edges(n, data=True))[0][2]['id']
    new_id_in = list(g.in_edges(n, data=True))[0][2]['id']
    coords_out = list(
        filter(
            None,
            list(g.in_edges(n, data=True))[1][2]['others'] + [[n[1], n[0]]] +
            list(g.out_edges(n, data=True))[0][2]['others']))
    coords_in = list(reversed(coords_out))
    edge_v1 = list(g.in_edges(n, data=True))[0][:2]
    edge_v2 = list(g.in_edges(n, data=True))[1][:2]
    edges_u = (edge_u1, edge_u2)
    edges_v = (edge_v1, edge_v2)
    lanes_u1 = list(g.out_edges(n, data=True))[0][2]['lanes']
    lanes_u2 = list(g.out_edges(n, data=True))[1][2]['lanes']
    lanes_v1 = list(g.in_edges(n, data=True))[0][2]['lanes']
    lanes_v2 = list(g.in_edges(n, data=True))[1][2]['lanes']
    if edges_u == edges_v:
        # remove edges and node
        is_deleted = [False, False]
        is_loop = False  # finding oneway loop (from_id == to_id)
        for i in edges_u:
            if check_oneway_loop(i):
                is_loop = True
        for i in edges_v:
            if check_oneway_loop(i):
                is_loop = True
        if is_loop:
            return
        if lanes_u1 == lanes_v2 or lanes_u1 is None or lanes_v2 is None or check_lanes:  # merge only edges with same number of lanes
            g.remove_edge(edge_u1[1], edge_u1[0])
            g.remove_edge(edge_u2[0], edge_u2[1])
            g.add_edge(edge_v2[0],
                       edge_v1[0],
                       id=new_id_out,
                       others=coords_out,
                       lanes=lanes_u1)
            is_deleted[0] = True
        if lanes_u2 == lanes_v1 or lanes_u2 is None or lanes_v1 is None or check_lanes:  # merge only edges with same number of lanes
            if edge_u1[1] != edge_u1[0] or edge_u2[0] != edge_u2[
                    1]:  # check  loops
                g.remove_edge(edge_u1[0], edge_u1[1])
                g.remove_edge(edge_u2[1], edge_u2[0])
                g.add_edge(edge_v1[0],
                           edge_v2[0],
                           id=new_id_in,
                           others=coords_in,
                           lanes=lanes_v1)
                is_deleted[1] = True

        if is_deleted[0] and is_deleted[1] or check_lanes:
            g.remove_node(n)
Ejemplo n.º 27
0
def read_cref_table(
        inmap,
        strip_paths=["/usr", "lib/STM32Cube_FW_F4_V1.16.0"],
        simplify_paths=["libgcc.a", "libc_nano.a", "libnosys.a"],
        hide_aeabi_symbols=True
        ):

    # Helper function to simplify filenames in order to increase graph clarity
    def process_filename(last_module):
        # Strip certain file paths
        for key in strip_paths:
            if last_module.find(key) > -1:
                s = last_module.split("/")
                print(last_module + " - " + s[len(s)-1])
                last_module = s[len(s)-1]
        # Remove symbol names (in round brackets) from certain libraries
        for key in simplify_paths:
            if last_module.find(key) > -1:
                s = last_module.split("(")
                print(last_module + " - " + s[0])
                last_module = s[0]
        return last_module

    # Create new graph
    modules = MultiDiGraph()

    # Parse all table lines
    redundant_associations = []
    while True:
        l = inmap.readline()
        words = l.split()

        if len(words) == 2:
            # New symbol A, which is defined in file B
            last_symbol = words[0]
            last_module = process_filename(words[1])

        elif len(words) == 1:
            # One more file uses the previous symbol
            filename = process_filename(words[0])

            # References of a file to itself are not particularly interesting
            if filename != last_module:
                if last_symbol.find("__aeabi") > -1 and hide_aeabi_symbols:
                    t = (filename, last_module)
                    if not (t in redundant_associations):
                        redundant_associations.append(t)
                        modules.add_edge(filename, last_module);
                else:
                    modules.add_edge(filename, last_module, label=last_symbol);

        elif len(l) == 0:
            # End of table
            break

    # Return the generated graph
    return modules
Ejemplo n.º 28
0
    def find_and_replace_pattern(self, graph: nx.MultiDiGraph):
        data_nodes = [
            Node(graph, node) for node in graph.node
            if Node(graph, node).kind == 'data'
        ]
        for node in data_nodes:
            # Get all requested shapes for current node
            # This mapping will contain pairs like {shape:[list of consumers nodes]}
            mapping = {}
            for consumer in node.out_nodes():
                edge_attrs = graph.get_edge_data(node.id, consumer.id)[0]
                if 'new_shape' in edge_attrs:
                    if np.array_equal(edge_attrs['new_shape'], node.shape):
                        continue
                    new_shape = tuple([x for x in edge_attrs['new_shape']])
                    if not new_shape in mapping:
                        mapping.update({new_shape: [consumer]})
                    else:
                        mapping[new_shape].append(consumer)

            if node.has_valid('value'):
                # Check that requested shape are the same
                # In case if they are different, we duplicate them
                for shape_key in mapping.keys():
                    shape = list(shape_key)
                    new_value = np.reshape(node.value, shape)
                    node_copy = Op.create_input_data_node(
                        graph, node.id + '/copy', value=np.array(new_value))
                    for consumer in mapping[shape_key]:
                        edge_attrs = graph.get_edge_data(node.id,
                                                         consumer.id)[0]
                        del edge_attrs['new_shape']

                        # Remove edge from previous data node and connect new data node with its consumer
                        graph.remove_edge(node.id, consumer.id)
                        graph.add_edge(node_copy.id, consumer.id, **edge_attrs)
            else:
                # Insert Reshape layer between data node and consumer
                for shape_key in mapping.keys():
                    shape = list(shape_key)
                    reshape = Reshape(graph,
                                      attrs={
                                          'dim': shape,
                                          'name': 'EltwiseReshapeNormalization'
                                      })
                    reshape_data = reshape.create_node_with_data(inputs=[node])

                    # Iterate over consumers and reconnect them to Reshape layer output
                    for consumer in mapping[shape_key]:
                        edge_attrs = graph.get_edge_data(node.id,
                                                         consumer.id)[0]
                        del edge_attrs['new_shape']

                        # Reconnect edge from original data node to Reshape output datanode
                        graph.remove_edge(node.id, consumer.id)
                        graph.add_edge(reshape_data.id, consumer.id,
                                       **edge_attrs)
Ejemplo n.º 29
0
def get_graphs():
    g1 = MultiDiGraph()
    g1.name = 'Graph 1'
    g1.add_node('HGNC:12345', id='HGNC:12345', name='Test Gene', category=['biolink:Gene'])
    g1.add_node('B', id='B', name='Node B', category=['biolink:NamedThing'])
    g1.add_node('C', id='C', name='Node C', category=['biolink:NamedThing'])
    g1.add_edge('C', 'B', key='C-biolink:subclass_of-B', edge_label='biolink:sub_class_of', relation='rdfs:subClassOf')
    g1.add_edge('B', 'A', key='B-biolink:subclass_of-A', edge_label='biolink:sub_class_of', relation='rdfs:subClassOf', provided_by='Graph 1')
    return [g1]
Ejemplo n.º 30
0
def FullConnection(group1: Group, group2: Group, graph: nx.MultiDiGraph):
    for node in group1.get_nodes():
        graph.add_node(node)
    for node in group2.get_nodes():
        graph.add_node(node)

    for i in group1.get_nodes():
        for j in group2.get_nodes():
            graph.add_edge(i, j)
Ejemplo n.º 31
0
def construct_multi_di_graph(edges: List[Tuple[str, str, Any]]) -> MultiDiGraph:
    """Constructs a multi directed graph from a list of edges.

    Arguments:
        edges: A list of tuples with entries: start, end and identifier.
    """
    g = MultiDiGraph()
    for start, end, key in edges:
        g.add_edge(start, end, key=key)
    return g
def inserir_nodo_grafo(grafo: MultiDiGraph, chave: int, nodo: dict,
                       nodo_novo: dict):
    grafo.add_node(chave,\
                   posicao_labirinto=nodo_novo["posicao_labirinto"],\
                   chave=chave,\
                   acao=nodo_novo["acao"],\
                   custo_caminho=nodo_novo["custo_caminho"])

    if nodo is not None:
        grafo.add_edge(nodo["chave"], chave)
Ejemplo n.º 33
0
def add_cfg_edges(g: nx.MultiDiGraph, node):
    """Add edges with attr `cfg` or `in` for control flow for the given node"""

    if isinstance(node, clang.graph.FunctionInfo):
        for cfg_b in node.cfgBlocks:
            g.add_node(cfg_b, attr="cfg")
            for succ in cfg_b.successors:
                g.add_edge(cfg_b, succ, attr="cfg")
                g.add_node(succ, attr="cfg")
            for stmt in cfg_b.statements:
                g.add_edge(stmt, cfg_b, attr="in")
                g.add_node(stmt, attr=(stmt.name))
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))
Ejemplo n.º 35
0
def AddInterproceduralCallEdges(
    graph: nx.MultiDiGraph,
    call_multigraph: nx.MultiDiGraph,
    function_entry_exit_nodes: typing.Dict[str, typing.Tuple[int, int]],
) -> None:
    """Add call edges between procedures to match the call graph.

  Args:
    graph: The disconnected per-function graphs.
    call_multigraph: A call graph with parallel edges to indicate multiple calls
      from the same function.
    function_entry_exit_nodes: A mapping from function name to a tuple of entry
      and exit statements.
  """
    # Drop the parallel edges by converting the call graph back to a regular
    # directed graph. Iterating over the edges in this graph then provides the
    # functions that need connecting, while the multigraph tells us how many
    # connections to expect.
    call_graph = nx.DiGraph(call_multigraph)

    for src, dst in call_graph.edges:
        # Ignore connections to the root node, we have already processed them.
        if src == "external node":
            continue

        call_sites = llvm_statements.FindCallSites(graph, src, dst)

        if not call_sites:
            continue

        # Check that the number of call sounds we found matches the expected number
        # from the call graph.
        # multigraph_call_count = call_multigraph.number_of_edges(src, dst)
        # if len(call_sites) != multigraph_call_count:
        #   raise ValueError("Call graph contains "
        #                    f"{humanize.Plural(multigraph_call_count, 'call')} from "
        #                    f"function `{src}` to `{dst}`, but found "
        #                    f"{len(call_sites)} call sites in the graph")

        for call_site in call_sites:
            if dst not in function_entry_exit_nodes:
                continue
            # Lookup the nodes to connect.
            call_entry, call_exit = function_entry_exit_nodes[dst]
            # Connect the nodes.
            graph.add_edge(call_site,
                           call_entry,
                           flow=programl_pb2.Edge.CALL,
                           position=0)
            graph.add_edge(call_exit,
                           call_site,
                           flow=programl_pb2.Edge.CALL,
                           position=0)
Ejemplo n.º 36
0
def read_cref(inmap):
    modules = MultiDiGraph()
    while True:
        l = inmap.readline()
        words = l.split()
        if len(words) == 2:
            last_symbol = words[0]
            last_module = words[1]
        elif len(words) == 1:
            modules.add_edge(words[0], last_module, label=last_symbol);
        elif len(l) == 0:
            break
    return modules
Ejemplo n.º 37
0
def InjectConnection(group1: Group, group2: Group, graph: nx.MultiDiGraph):
    neuron_group_1 = group1.get_nodes()
    neuron_group_2 = group2.get_nodes()

    if len(neuron_group_1) != len(neuron_group_2):
        raise Exception('Size of group do not match for inject connection')

    for node in neuron_group_1:
        graph.add_node(node)
    for node in neuron_group_2:
        graph.add_node(node)

    for i in range(len(neuron_group_1)):
        graph.add_edge(neuron_group_1[i], neuron_group_2[i])
Ejemplo n.º 38
0
def selector(request):
    request.session['viewer'] = request.path_info
    if request.method == 'POST':
        form = Neo4jQueryForm(request.POST)
        if form.is_valid():
            host = request.session.get('host', None)
            if host:
                gdb = GraphDatabase(host)
                node = gdb.node[form.cleaned_data['node']]
                depth = form.cleaned_data['depth']
                new_nodes = []
                graph = MultiDiGraph()
                node_id = str(node.id)
                graph.add_node(node_id, **node.properties)
                new_nodes.append(node_id)
                for i in range(depth):
                    added_nodes = []
                    for node_id in new_nodes:
                        node = gdb.node[node_id]
                        for relation in node.relationships.all():
                            start = relation.start
                            end = relation.end
                            if start not in new_nodes:
                                start_id = str(start.id)
                                graph.add_node(start_id, **start.properties)
                                added_nodes.append(start_id)
                            if end not in new_nodes:
                                end_id = str(end.id)
                                graph.add_node(end_id, **end.properties)
                                added_nodes.append(end_id)
                            graph.add_edge(start_id, end_id, **relation.properties)
                    new_nodes += added_nodes
                interactor = NetworkxInteractor(graph)
                request.session['interactor'] = interactor
                request.session['layout'] = None
                response_dictionary = set_response_dictionary(request)
            else:
                form = Neo4jConnectionForm()
                return render_to_response('neo4j/index.html', {
                                            'form': form,
                                            })
            return render_to_response('neo4j/explorer.html',
                                        response_dictionary)
    else:
        interactor = request.session.get('interactor')
        return render_to_response('neo4j/explorer.html',
                                set_response_dictionary(request))
Ejemplo n.º 39
0
def scale_free_graph(n, G=None,
                     alpha=0.41,
                     beta=0.54,
                     gamma=0.05,
                     delta_in=0.2,
                     delta_out=0,
                     seed=None):
    """Return a scale free directed graph

    Parameters
    ----------
    n : integer
       Number of nodes in graph

    G : NetworkX graph (optional)
       Use as starting graph in algorithm

    alpha : float 
       Probability for adding a new node conecgted to an existing node
       chosen randomly according to the in-degree distribution.

    beta : float
       Probability for adding an edge between two existing nodes.
       One existing node is chosen randomly according the in-degree 
       distribution and the other chosen randomly according to the out-degree 
       distribution.
       
    gamma : float
       Probability for adding a new node conecgted to an existing node
       chosen randomly according to the out-degree distribution.
        
    delta_in : float
       Bias for choosing ndoes from in-degree distribution.

    delta_out : float
       Bias for choosing ndoes from out-degree distribution.

    delta_out : float
       Bias for choosing ndoes from out-degree distribution.

    seed : integer (optional)
       Seed for random number generator

    Examples
    --------
    >>> G=nx.scale_free_graph(100)

    
    Notes
    -----
    The sum of alpha, beta, and gamma must be 1.

    Algorithm from
    
    @article{bollobas2003dsf,
    title={{Directed scale-free graphs}},
    author={Bollob{\'a}s, B. and Borgs, C. and Chayes, J. and Riordan, O.},
    journal={Proceedings of the fourteenth annual ACM-SIAM symposium on Discrete algorithms},
    pages={132--139},
    year={2003},
    publisher={Society for Industrial and Applied Mathematics Philadelphia, PA, USA}
    }

"""

    def _choose_node(G,distribution,delta):
        cumsum=0.0
        # normalization 
        psum=float(sum(distribution.values()))+float(delta)*len(distribution)
        r=random.random()
        for i in range(0,len(distribution)):
            cumsum+=(distribution[i]+delta)/psum
            if r < cumsum:  
                break
        return i

    if G is None:
        # start with 3-cycle
        G=MultiDiGraph()
        G.add_edges_from([(0,1),(1,2),(2,0)])

    if alpha <= 0:
        raise ValueError('alpha must be >= 0.')
    if beta <= 0:
        raise ValueError('beta must be >= 0.')
    if gamma <= 0:
        raise ValueError('beta must be >= 0.')

    if alpha+beta+gamma !=1.0:
        raise ValueError('alpha+beta+gamma must equal 1.')
        
    G.name="directed_scale_free_graph(%s,alpha=%s,beta=%s,gamma=%s,delta_in=%s,delta_out=%s)"%(n,alpha,beta,gamma,delta_in,delta_out)

    # seed random number generated (uses None as default)
    random.seed(seed)

    while len(G)<n:
        r = random.random()
        # random choice in alpha,beta,gamma ranges
        if r<alpha:
            # alpha
            # add new node v
            v = len(G) 
            # choose w according to in-degree and delta_in
            w = _choose_node(G, G.in_degree(with_labels=True),delta_in)
        elif r < alpha+beta:
            # beta
            # choose v according to out-degree and delta_out
            v = _choose_node(G, G.out_degree(with_labels=True),delta_out)
            # choose w according to in-degree and delta_in
            w = _choose_node(G, G.in_degree(with_labels=True),delta_in)
        else:
            # gamma
            # choose v according to out-degree and delta_out
            v = _choose_node(G, G.out_degree(with_labels=True),delta_out)
            # add new node w
            w = len(G) 
        G.add_edge(v,w)
        
    return G
Ejemplo n.º 40
0
 def add_edge(self,node1,node2,**kwargs):# inefficient but nobody cares and it's convenient
     e=Edge(node1,node2,self.keycounter,kwargs)
     MultiDiGraph.add_edge(self,node1,node2,object = e,key = self.keycounter)
     self.InverseLookUp[kwargs["morphism"]] = e
     self.keycounter+=1
     return e
Ejemplo n.º 41
0
def parseArnetminerDataset():
    """
      Parse the four area dataset, and use only barebones structures to keep everything efficient.

        Skips papers that:
            (1)

        The final parsed network
    """

    inputFile = open(os.path.join(projectRoot, 'data','DBLP-citation-Feb21.txt'))
    graph = MultiDiGraph()

    # Sets for authors, papers, conferences, and terms found so far
    indexToPaperIdMap = {}
    citationCountMap = {}
    indexSet = set()

    beginning = inputFile.tell()

    print "Parsing nodes for graph..."

    # Counts for statistics
    VALID_PAPERS = 1566322 # 99.62% of total papers in DBLP dataset
    papersProcessed = 0
    skippedPaperIndices = set()
    invalidPaperIndices = set()

    # Add each paper to graph (adding missing associated terms, authors, and conferences)
    for title, authors, conference, terms, citationCount, index in __papersFromFile(inputFile, skippedPaperIndices, invalidPaperIndices):

        # Check that index is unique, and record it
        assert index not in indexSet
        indexSet.add(index)

        # Create unique identifier with paper index & title
        paperId = '%d----%s' % (index, title)
        citationCountMap[paperId] = citationCount
        indexToPaperIdMap[index] = paperId

        # Add symmetric edges & nodes (if they don't already exist in the network)
        for author in authors:
            graph.add_edges_from([(author, paperId), (paperId, author)])
        graph.add_edges_from([(conference, paperId), (paperId, conference)])
        for term in terms:
            graph.add_edges_from([(term, paperId), (paperId, term)])

        # Output progress
        papersProcessed += 1
        sys.stdout.write("\r Processed %d / %d papers..." % (papersProcessed, VALID_PAPERS))

    # Rewind file
    inputFile.seek(beginning)

    print "Parsing citations for graph..."

    # Counts for statistics
    papersProcessed = 0
    successfulCitations = 0
    omittedPaperCitations = 0
    invalidPaperCitations = 0
    invalidCitations = 0

    # Add citations to the graph
    for title, index, citations in __citationsFromFile(inputFile):
        citingId = '%d----%s' % (index, title)
        for citationIndex in citations:

            # Add citation edge if it was found
            if citationIndex in indexToPaperIdMap:
                successfulCitations += 1
                graph.add_edge(citingId, indexToPaperIdMap[citationIndex])

            # Tally missing citation appropriately
            elif citationIndex in skippedPaperIndices:
                omittedPaperCitations += 1
            elif citationIndex in invalidPaperIndices:
                invalidPaperCitations += 1
            else:
                print "\nCitation '%d' not found for '%s'" % (citationIndex, title)
                invalidCitations += 1

        # Output progress
        papersProcessed += 1
        sys.stdout.write("\r Processed Citations for %d / %d papers..." % (papersProcessed, VALID_PAPERS))

    # Basic statistics about cleanliness of citations
    totalCitations = invalidCitations + successfulCitations
    successfulCitationsPercent = 100 * float(successfulCitations) / totalCitations
    omittedPaperCitationsPercent = 100 * float(omittedPaperCitations) / totalCitations
    invalidPaperCitationsPercent = 100 * float(invalidPaperCitations) / totalCitations
    invalidCitationsPercent = 100 * float(invalidCitations) / totalCitations
    print "\n\nTotal Citations: %d" % totalCitations
    print "  Citations Added (Successful): %d (%2.2f%%)" % (successfulCitations, successfulCitationsPercent)
    print "  Citations Skipped (Skipped Paper): %d (%2.2f%%)" % (omittedPaperCitations, omittedPaperCitationsPercent)
    print "  Citations Skipped (Invalid Paper): %d (%2.2f%%)" % (invalidPaperCitations, invalidPaperCitationsPercent)
    print "  Citations Invalid (Unknown): %d (%2.2f%%)" % (invalidCitations, invalidCitationsPercent)

    return graph
Ejemplo n.º 42
0
class Grafatality(object):
    def __init__(self, filename='graph.json'):
        self.filename=filename
        self.graph = MultiDiGraph()
        self.typed_nodes = {}
        self.typed_nodes[None] = []
        try:
            self.load_file(filename)
        except IOError as e:
            print 'file "%s" does not exist, creating' % filename
        
    def handle(self, line):
        obj = ujson.loads(line)
        action = obj['action']
        if action == 'add_node':
            self.load_node(obj)
        elif action == 'add_edge':
            self.load_edge(obj)
        elif action == 'remove_edge':
            self.load_remove_edge(obj)

    def load_file(self, filename):
        f = open(filename)
        for line in f:
            self.handle(line)
        f.close()

    def listify_typed_node(self, node):
        if type(node) == list:
            node = tuple(node)
            if len(node) == 2:
                node = tuple(node)
        return node

    def load_edge(self, obj):
        data = obj.get('data', None)
        key =  obj.get('key', None)
        src_node = obj['src_node']
        dst_node = obj['dst_node']
        src_node = self.listify_typed_node(src_node)
        dst_node = self.listify_typed_node(dst_node)
        if data:
            self.graph.add_edge(src_node, dst_node, key=key, **data)
        else:
            self.graph.add_edge(src_node, dst_node, key=key)
    
    def load_remove_edge(self, obj):
        key = obj.get('key', None)
        self.graph.remove_edge(obj['src_node'], obj['dst_node'], key=key)
        
    def load_node(self, obj):
        data = obj.get('data', None)
        node = obj['node']
        node_type = None
        node = self.listify_typed_node(node)
        if data:
            self.graph.add_node(node, node_type=node_type, **data)
        else:
            self.graph.add_node(node, node_type=node_type)
    
    def add_node(self, node, node_type=None, **attr):
        if node_type:
            if not node_type in self.typed_nodes:
                self.typed_nodes[node_type] = []
            node = (node, node_type)
            self.typed_nodes[node_type].append(node)
        else:
            self.typed_nodes[None].append(node)

        data = {"action": "add_node",
                "node": node}

        if len(attr):
            data['data'] = attr
        self.append_log(data)

        return self.graph.add_node(node, **attr)

    def nodes_of_type(self, node_type=None, full=True):
        if full:
            return self.typed_nodes[node_type]
        else:
            return self.typed_nodes[node_type][0]

    def add_edge(self, src_node, dst_node, key=None, **attr):
        
        data = {"action": "add_edge",
                "src_node": src_node,
                "dst_node": dst_node}
        if len(attr):
            data['data'] = attr
        if key:
            data['key'] = key
        self.append_log(data)
        
        return self.graph.add_edge(src_node, dst_node, key=key, **attr)

    def remove_edge(self, src_node, dst_node, key):
        data = {"action": "remove_edge",
                "src_node": src_node,
                "dst_node": dst_node,
                "key": key}
        self.append_log(data)
        
        return self.graph.remove_edge(src_node, dst_node, key)

    def append_log(self,data):
        """
        This persists the data and while it may be true that 
        the logfile could be kept open indefinitely, for some reason
        this causes dataloss when running multiprocess files.
        """
        self.log = open(self.filename, 'a')
        self.log.write(ujson.encode(data))
        self.log.write("\n")
        self.log.close()


    def shutdown(self):
        self.log.close()
class AbstractGame:
    def __init__(self, player1, player2, initial_gamestate):
        self.game_graph = MultiDiGraph()
        self.player1 = player1
        self.player2 = player2
        self.initial_gamestate = self.canonicalize(initial_gamestate)

    def canonicalize(self, gamestate):
        raise NotImplementedError

    def possible_moves(self, gamestate, player):
        raise NotImplementedError

    def other_player(self, player):
        if player == self.player1:
            return self.player2
        elif player == self.player2:
            return self.player1
        else:
            raise ValueError("%s is neither %s nor %s" % (player, self.player1, self.player2))

    def is_winning_position(self, gamestate, player):
        raise NotImplementedError

    def is_losing_position(self, gamestate, player):
        raise NotImplementedError

    def good_moves(self, gamestate, player, lookahead_depth):

        if lookahead_depth == 0:
            yield from self.possible_moves(gamestate, player)
        else:
            lookahead_depth -= 1
            sign = {self.player1: 1, self.player2: -1}

            def is_game_over(gamestate):
                return evaluate_gamestate(gamestate, 0) != 0

            def evaluate_gamestate(gamestate, depth):
                """
                Returns 1 if this gamestate favors the first player, -1 if it favors the second player,
                and 0 if it is neutral.
                """
                if self.is_winning_position(gamestate, self.player1):
                    return 1000 - depth
                elif self.is_winning_position(gamestate, self.player2):
                    return -1000 + depth
                else:
                    return 0

            def negamax(gamestate, current_depth, current_player):
                if is_game_over(gamestate) or current_depth > lookahead_depth:
                    return sign[current_player] * evaluate_gamestate(gamestate, current_depth)
                max_score = -1000
                for possible_gamestate in self.possible_moves(gamestate, current_player):
                    max_score = max(max_score,
                                    -negamax(possible_gamestate, current_depth + 1, self.other_player(current_player)))
                return max_score

            moves_and_lookaheads = {move: -negamax(move, 1, self.other_player(player)) for move in self.possible_moves(gamestate, player)}

            if len(moves_and_lookaheads) == 0:
                return
            best_lookahead = max(moves_and_lookaheads.values())
            good_moves = {self.canonicalize(move) for move, lookahead in moves_and_lookaheads.items() if lookahead == best_lookahead}
            for move in good_moves:
                yield move

    def play(self, lookahead=0):
        new_moves = set()  # A set of node, player tuples containing all new moves that can be played in this graph.

        def play_new_moves():
            new_new_moves = set()  # The moves that will be new after current new moves have been played.
            for gamestate, player in new_moves:
                next_player = self.other_player(player)
                for move_result in self.good_moves(gamestate, player, lookahead):
                    move_result = self.canonicalize(move_result)
                    if move_result not in self.game_graph:  # Add the result to the graph if it's not there.
                        self.game_graph.add_node(move_result, players=set())
                    self.game_graph.add_edge(gamestate, move_result, key=player)  # Add this move to the graph
                    if next_player not in self.game_graph.node[move_result]['players']:
                        self.game_graph.node[move_result]['players'].add(next_player)
                        new_new_moves.add((move_result, next_player))
            new_moves.clear()
            new_moves.update(new_new_moves)

        new_moves.add((self.initial_gamestate, self.player1))
        self.game_graph.add_node(self.initial_gamestate, players=set())

        while len(new_moves) != 0:
            play_new_moves()
        self.game_graph