def from_networkx(graph: Any) -> Network: n = Network(directed=graph.is_directed(), multiedges=graph.is_multigraph()) for v in graph.nodes: n.add_node(v, **graph.nodes[v]) for e in graph.edges: n.add_edge(e[0], e[1], **graph.edges[e]) return n
def lattice_network(start: Optional[int]=0, stop: Optional[int]=10, dims: Optional[int]=2) -> Network: """ Generates a n-dimensional lattice network with coordinates in each dimension ranging from start (inclusive) to stop (exclusive) """ network = Network(directed=False) for pos in _multi_dim_range(start, stop, dims): network.add_node(Node("".join(str(i)+'-' for i in pos).strip('-'), pos=np.array(pos))) for v in network.nodes: for w in network.nodes: if np.sum(np.abs(v['pos']-w['pos']))==1 and (v.uid, w.uid) not in network.edges: network.add_edge(v, w) return network
def train_test_split(network: Network, test_size: Optional[float] = 0.25, train_size: Optional[float] = None, split: Optional[str] = 'node') -> tuple(Network, Network): """Returns a train/test split of a network object. This method is implemented for instances of Network and TemporalNetwork. The train/test split is non-destructive and based on object references, i.e. the function returns new Network instances that contain references to the same node/edge objects. The original network is not affected. Parameters ---------- network: Union[Network, TemporalNetwork] The network or temporal network for which the train/test split shall be performed. test_size: Optional[float] = 0.25 Fraction of the network to include in the test network train_size: Optional[float] = 0.25 Fraction of the network to include in the training network split: Optional['str'] = 'node' Specifies how the train/test split shall be performed. For 'node' a random subset of nodes is selected, while for 'edge' a random subset of edges is selected. Returns ------- Tuple (n1, n2) where n1 is the training network and n2 is the test network Examples -------- >>> n = pp.Network() >>> n.add_edge('a', 'b') >>> n.add_edge('b', 'c') >>> n.add_edge('c', 'd') >>> n.add_edge('d', 'a') >>> train, test = train_test_split(n, test_size=0.25) >>> print(train) >>> print(test) Network with one node Network with three nodes """ test_network = Network(directed=network.directed, multiedges=network.multiedges, uid=network.uid + '_test') train_network = Network(directed=network.directed, multiedges=network.multiedges, uid=network.uid + '_train') if train_size == None: ts = test_size else: ts = 1.0 - train_size if split == 'node': test_nodes = choice([v.uid for v in network.nodes], size=int(ts * network.number_of_nodes()), replace=False) for v in test_nodes: test_network.add_node(network.nodes[v]) train_nodes = [ v.uid for v in network.nodes if v.uid not in test_network.nodes.uids ] for v in train_nodes: train_network.add_node(network.nodes[v]) for e in network.edges: if e.v.uid in test_network.nodes.uids and e.w.uid in test_network.nodes.uids: test_network.add_edge(e) if e.v.uid in train_network.nodes.uids and e.w.uid in train_network.nodes.uids: train_network.add_edge(e) elif split == 'edge': for v in network.nodes: test_network.add_node(v) train_network.add_node(v) test_edges = choice([e.uid for e in network.edges], size=int(ts * network.number_of_edges()), replace=False) for e in test_edges: test_network.add_edge(network.edges[e]) train_edges = [ e.uid for e in network.edges if e.uid not in test_network.edges.uids ] for e in train_edges: train_network.add_edge(network.edges[e]) else: raise NotImplementedError( 'Unsupported split method "{0}" for instance of type Network'. format(split)) return train_network, test_network
def ER_nm(n: int, m: int, directed: bool = False, loops: bool = False, multiedges: bool = False, node_uids: Optional[list] = None) -> Union[Network, None]: """(n, m) Erdös-Renyi model. Generates a random graph with a fixed number of n nodes and m edges based on the Erdös-Renyi model. Parameters ---------- n : int The number of nodes in the generated network m : int The number of randomly generated edges in the network directed : bool Whether a directed network should be generated loops : bool Whether or not the generated network may contain loops. multi_edge : bool Whether or not the same edge can be added multiple times node_uids : list Optional list of node uids that will be used. Examples -------- Generate random undirected network with 10 nodes and 25 edges >>> import pathpy as pp >>> random_graph = pp.algorithms.random_graphs.ER_nm(n=10, m=25) >>> print(random_graph.summary()) ... """ # Check parameter sanity M = max_edges(n, directed=directed, loops=loops, multiedges=multiedges) if m > M: LOG.error( 'Given network type with n nodes can have at most {} edges.'.format(M)) return None network = Network(directed=directed) if node_uids is None or len(node_uids) != n: LOG.info('No valid node uids given, generating numeric node uids') node_uids = [] for i in range(n): node_uids.append(str(i)) for i in range(n): network.add_node(node_uids[i]) edges = 0 while edges < m: v, w = np.random.choice(node_uids, size=2, replace=loops) if multiedges or network.nodes[w] not in network.successors[v]: network.add_edge(v, w) edges += 1 return network
def Molloy_Reed(degrees: Union[np.array, Dict[int, float]], multiedge: bool = False, relax: bool=False, node_uids: Optional[list] = None) -> Optional[Network]: """Generate Molloy-Reed graph. Generates a random undirected network with given degree sequence based on the Molloy-Reed algorithm. .. note:: The condition proposed by Erdös and Gallai (1967) is used to test whether the degree sequence is graphic, i.e. whether a network with the given degree sequence exists. Parameters ---------- degrees : list List of integer node degrees. The number of nodes of the generated network corresponds to len(degrees). relax : bool If True, we conceptually allow self-loops and multi-edges, but do not add them to the network This implies that the generated network may not have exactly sum(degrees)/2 links, but it ensures that the algorithm always finishes. node_uids : list Optional list of node uids that will be used. Examples -------- Generate random undirected network with given degree sequence >>> import pathpy as pp >>> random_network = pp.algorithms.random_graphs.Molloy_Reed([1,0]) >>> print(random_network.summary()) ... Network generation fails for non-graphic sequences >>> import pathpy as pp >>> random_network = pp.algorithms.random_graphs.Molloy_Reed([1,0]) >>> print(random_network) None """ # assume that we are given a graphical degree sequence if not is_graphic_Erdos_Gallai(degrees): return None # create empty network with n nodes n = len(degrees) network = Network(directed=False, multiedges=multiedge) if node_uids is None or len(node_uids) != n: LOG.info('No valid node uids given, generating numeric node uids') node_uids = [] for i in range(n): node_uids.append(str(i)) for i in range(n): network.add_node(node_uids[i]) # generate link stubs based on degree sequence stubs = [] for i in range(n): for _ in range(int(degrees[i])): stubs.append(str(node_uids[i])) # connect randomly chosen pairs of link stubs while(len(stubs) > 0): v, w = np.random.choice(stubs, 2, replace=False) if v == w or (multiedge == False and relax == False and network.nodes[w] in network.successors[v]): # remove random edge and add stubs if network.number_of_edges()>0: edge = random.choice(list(network.edges)) stubs.append(edge.v.uid) stubs.append(edge.w.uid) network.remove_edge(edge) else: if not network.nodes[w] in network.successors[v]: network.add_edge(v, w) stubs.remove(v) stubs.remove(w) return network
def Watts_Strogatz(n: int, s: int, p: float = 0.0, loops: bool = False, node_uids: Optional[list] = None) -> Network: """Undirected Watts-Strogatz lattice network Generates an undirected Watts-Strogatz lattice network with lattice dimensionality one. Parameters ---------- n : int The number of nodes in the generated network s : float The number of nearest neighbors that will be connected in the ring lattice p : float The rewiring probability node_uids : list Optional list of node uids that will be used. Examples -------- Generate a Watts-Strogatz network with 100 nodes >>> import pathpy as pp >>> small_world = pp.algorithms.random_graphs.Watts_Strogatz(n=100, s=2, p=0.1) >>> print(small_world.summary()) ... """ network = Network(directed=False) if node_uids is None or len(node_uids) != n: LOG.info('No valid node uids given, generating numeric node uids') node_uids = [] for i in range(n): network.add_node(Node(str(i))) node_uids.append(str(i)) else: for i in range(n): network.add_node(node_uids[i]) # construct a ring lattice (dimension 1) for i in range(n): if loops: x = 0 y = s else: x = 1 y = s+1 for j in range(x, y): v = network.nodes[node_uids[i]] w = network.nodes[node_uids[(i+j) % n]] if (v.uid, w.uid) not in network.edges: network.add_edge(v, w) if p == 0: # nothing to do here return network # Rewire each link with probability p for edge in tqdm(list(network.edges.values()), 'generating WS network'): if np.random.rand() < p: # Delete original link and remember source node v = edge.v.uid network.remove_edge(edge) # Find new random tgt, which is not yet connected to src new_target = None # This loop repeatedly chooses a random target until we find # a target not yet connected to src. Note that this could potentially # result in an infinite loop depending on parameters. while new_target is None: x = node_uids[np.random.randint(n)] if (x != v or loops) and (v, x) not in network.edges: new_target = x network.add_edge(v, new_target) return network
def ER_np(n: int, p: float, directed: bool = False, loops: bool = False, node_uids: Optional[list] = None) -> Network: """(n, p) Erdös-Renyi model Generates a random graph with a fixed number of n nodes and edge probability p based on the Erdös-Renyi model. Parameters ---------- n : int The number of nodes in the generated network p : float The probability with which an edge will be created between each pair of nodes directed : bool Whether a directed network should be generated loops : bool Whether or not the generated network may contain loops. node_uids : list Optional list of node uids that will be used. Examples -------- Generate random undirected network with 10 nodes >>> import pathpy as pp >>> random_graph = pp.algorithms.random_graphs.ER_np(n=10, p=0.03) >>> print(random_graph.summary()) ... """ network = Network(directed=directed) if node_uids is None or len(node_uids) != n: LOG.info('No valid node uids given, generating numeric node uids') node_uids = [] for i in range(n): node_uids.append(str(i)) for i in range(n): network.add_node(node_uids[i]) for s in tqdm(range(n), 'generating G(n,p) network'): if directed: x = n else: x = s+1 for t in range(x): if t == s and not loops: continue if np.random.random_sample() < p: network.add_edge(node_uids[s], node_uids[t]) return network