Source code for gcmpy.gcm_algorithm
# GCM algorithm for gcmpy
#
# Copyright (C) 2021 Peter Mann
#
# This file is part of gcmpy, generalised configuration model networks in Python.
#
# gcmpy is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# gcmpy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gcmpy. If not, see <http://www.gnu.org/licenses/gpl.html>.
import random
from typing import List, Callable
from .types import _EDGES, _NODES, _JDS
from .utils import edge_list, output_data, results
[docs]class GCM_algorithm(object):
"""Generalised configuration model algorithm.
:param num_networks: the number of networks to create
:param motif_sizes: list of ints that indicate the number of nodes in each motif
:param build_functions: callbacks that accept list of nodes and return edges"""
_num_networks : int # number of networks to create
_motif_sizes : List[int] # list of number of nodes in each motif
_build_functions : List[Callable[[_NODES],_EDGES]] # list of callbacks for motif construction
def __init__(self, motif_sizes : List[int],
build_functions : List[Callable[[_NODES],_EDGES]]):
self._motif_sizes = motif_sizes
self._build_functions = build_functions
[docs] def random_clustered_graph(self, jds : _JDS)->edge_list:
'''Generate a random graph from a given joint degree sequence of motifs. If motif constructors
are not specified, ValueError is raised.
:param jds: joint degree sequence
:returns: a list of edges in the graph as an edge_list object'''
# as default construct cliques
if self._build_functions is None:
raise ValueError('GCM_algorithm._build_functions is None')
# create an empty graph
N = len(jds)
# initialise a list of lists for distinct motif topology
stubs = list([] for _ in range(len(jds[0])))
# for each node n
for n in range(N):
joint_degree = jds[n]
# for each topology ...
for k, k_list in enumerate(stubs):
# append node n to the list once per unique motif of a given topology
for _ in range(joint_degree[k]):
k_list.append(n)
# shuffle each stub list
for k_list in stubs:
random.shuffle(k_list)
# create edge list object
es = edge_list()
# for each topology list ...
for k, k_list in enumerate(stubs):
motif_size = self._motif_sizes[k]
# while there are still nodes ...
while k_list:
nodes = []
# grab required number of nodes to build the motif
for _ in range(motif_size):
nodes.append(k_list.pop())
# add the edges to the network using the builder callback
es.add_edges_from(self._build_functions[k](nodes))
# return the graph
return es
[docs]class ResampleJDS(GCM_algorithm):
'''Resamples a joint degree sequence to create multiple networks.
:param num_networks: the number of networks to create
:param motif_sizes: list of ints that indicate the number of nodes in each motif
:param network_name: string identifier/classifier for network
:param build_functions: callbacks that accept list of nodes and return edges'''
def __init__(self, num_networks : int,
motif_sizes : List[int],
build_functions : List[Callable[[_NODES],_EDGES]],
network_name : str = None,):
self._num_networks = num_networks
self._allow_rewires = True # indicate that the networks are rewired from a single JDS sample
self._network_name = network_name
super().__init__(motif_sizes, build_functions)
[docs] def random_clustered_graph_from_resampled_jds(self, jds : _JDS)->results:
'''Routine to create multiple configuration model networks from a single joint degree sequence.
This essentially rewires a given sequence of joint degrees using the configuration model.
:param jds: joint degree sequence
:returns results: data of constructed networks'''
res : results = []
for i in range(self._num_networks):
res_ = output_data(i)
res_._name = self._network_name
res_._network = self.random_clustered_graph(jds)
res.append(res_)
return res