Tweaks (#2)
* added scalefree/newman_watts_strogatz models, fixed a bug * Readme * Readme * added PR2 suggestions
This commit is contained in:
parent
ac4c245ef7
commit
d677a4c78d
|
@ -27,7 +27,7 @@ gen_jsons.sh can generate given number of Waku networs and outputs them to a dir
|
||||||
> usage: ./gen_jsons.sh <output_dir> <#json files needed> </br>
|
> usage: ./gen_jsons.sh <output_dir> <#json files needed> </br>
|
||||||
|
|
||||||
## generate_network.py
|
## generate_network.py
|
||||||
generate_network.py can generate networks with specified number of nodes and topics. the network types currently supported is "configuration_model" and more are on the way. Use with Python3.
|
generate_network.py can generate networks with specified number of nodes and topics. the network types currently supported is "configuration_model" and more are on the way. Use with Python3. Comment out the `#draw(fname, H)` line to visualise the generated graph.
|
||||||
|
|
||||||
> usage: generate_network [-h] [-o <file_name>] [-n <#nodes>] [-t <#topics>]
|
> usage: generate_network [-h] [-o <file_name>] [-n <#nodes>] [-t <#topics>]
|
||||||
[-T <type>] <br>
|
[-T <type>] <br>
|
||||||
|
|
|
@ -1,34 +1,40 @@
|
||||||
#! /usr/bin/env python3
|
#! /usr/bin/env python3
|
||||||
|
|
||||||
import matplotlib.pyplot as mp
|
import matplotlib.pyplot as plt
|
||||||
import networkx as nx
|
import networkx as nx
|
||||||
import networkx.readwrite.json_graph
|
|
||||||
import random, math
|
import random, math
|
||||||
import json
|
import json
|
||||||
import argparse,sys
|
import argparse, os, sys
|
||||||
|
|
||||||
|
# Dump to a json file
|
||||||
def write_json(filename, data_2_dump):
|
def write_json(filename, data_2_dump):
|
||||||
json.dump(data_2_dump, open(filename,'w'), indent=2)
|
json.dump(data_2_dump, open(filename,'w'), indent=2)
|
||||||
|
|
||||||
|
|
||||||
# has trouble with non-integer/non-hashable keys
|
# has trouble with non-integer/non-hashable keys
|
||||||
def read_json(filename):
|
def read_json(filename):
|
||||||
with open(filename) as f:
|
with open(filename) as f:
|
||||||
jdata = json.load(f)
|
jdata = json.load(f)
|
||||||
return nx.node_link_graph(jdata)
|
return nx.node_link_graph(jdata)
|
||||||
|
|
||||||
def draw(H):
|
|
||||||
nx.draw(H, pos=nx.kamada_kawai_layout(H), with_labels=True)
|
|
||||||
mp.show()
|
|
||||||
mp.savefig("topology.png", format="PNG")
|
|
||||||
|
|
||||||
def init_arg_parser() :
|
# Draw the network and output the image to a file
|
||||||
# Initialize parser, add arguments and set the defaults
|
def draw(fname, H):
|
||||||
|
nx.draw(H, pos=nx.kamada_kawai_layout(H), with_labels=True)
|
||||||
|
plt.savefig(os.path.splitext(fname)[0] + ".png", format="png")
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
|
||||||
|
# Initialize parser, set the defaults, and extract the options
|
||||||
|
def get_options():
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
prog = 'generate_network',
|
prog = 'generate_network',
|
||||||
description = '''Generates and outputs
|
description = '''Generates and outputs
|
||||||
the Waku network conforming to input parameters''',
|
the Waku network conforming to input parameters''',
|
||||||
epilog = '''The defaults are: -o "Topology.json";
|
epilog = '''Defaults: -o "Topology.json";
|
||||||
-n 1; -t 1; -p 1; -T "configuration_model"''')
|
-n 1; -t 1; -p 1; -T "configuration_model"
|
||||||
|
Supported nw types "configuration_model", "scalefree",
|
||||||
|
"newman_watts_strogatz"''')
|
||||||
parser.add_argument("-o", "--output",
|
parser.add_argument("-o", "--output",
|
||||||
default='Topology.json', dest='fname',
|
default='Topology.json', dest='fname',
|
||||||
help='output json filename for the Waku network',
|
help='output json filename for the Waku network',
|
||||||
|
@ -53,34 +59,28 @@ def init_arg_parser() :
|
||||||
# default=1, dest='num_edges',
|
# default=1, dest='num_edges',
|
||||||
# help='The number of edges in the Waku network',
|
# help='The number of edges in the Waku network',
|
||||||
# type=int, metavar='#edges>')
|
# type=int, metavar='#edges>')
|
||||||
return parser
|
return parser.parse_args()
|
||||||
|
|
||||||
# https://networkx.org/documentation/stable/reference/generated/networkx.generators.degree_seq.configuration_model.html
|
|
||||||
def generate_config_model(n):
|
|
||||||
#degrees = nx.random_powerlaw_tree_sequence(n, tries=10000)
|
|
||||||
degrees = [random.randint(1, n) for i in range(n)]
|
|
||||||
if (sum(degrees)) % 2 != 0: # adjust the degree sum to be even
|
|
||||||
degrees[-1] += 1
|
|
||||||
G = nx.configuration_model(degrees) # generate the graph
|
|
||||||
return G
|
|
||||||
|
|
||||||
|
# Generate a random string (UC chars) of len n
|
||||||
def generate_topic_string(n):
|
def generate_topic_string(n):
|
||||||
rs = ""
|
rs = ""
|
||||||
for _ in range(n):
|
for _ in range(n):
|
||||||
r = random.randint(65, 65 + 26 - 1) # only letters
|
r = random.randint(65, 65 + 26 - 1) # generate a random UC char
|
||||||
rs += (chr(r)) # append the char generated
|
rs += (chr(r)) # append the char generated
|
||||||
return rs
|
return rs
|
||||||
|
|
||||||
|
|
||||||
|
# Generate the topics - UC chars prefixed by "topic"
|
||||||
def generate_topics(num_topics):
|
def generate_topics(num_topics):
|
||||||
# generate the topics - uppercase chars prefixed by "topic"
|
|
||||||
topics = []
|
topics = []
|
||||||
base = 26
|
base = 26
|
||||||
topic_len = int(math.log(num_topics)/math.log(base)) + 1
|
topic_len = int(math.log(num_topics)/math.log(base)) + 1
|
||||||
topics = {}
|
topics = {i: f"topic_{generate_topic_string(topic_len)}" for i in range(num_topics)}
|
||||||
for i in range(num_topics):
|
|
||||||
topics[i] = "topic_" + generate_topic_string(topic_len)
|
|
||||||
return topics
|
return topics
|
||||||
|
|
||||||
|
|
||||||
|
# Get a random sub-list of topics
|
||||||
def get_random_sublist(topics):
|
def get_random_sublist(topics):
|
||||||
n = len(topics)
|
n = len(topics)
|
||||||
lo = random.randint(0, n - 1)
|
lo = random.randint(0, n - 1)
|
||||||
|
@ -90,28 +90,53 @@ def get_random_sublist(topics):
|
||||||
sublist.append(topics[i])
|
sublist.append(topics[i])
|
||||||
return sublist
|
return sublist
|
||||||
|
|
||||||
def generate_network(num_nodes, prefix):
|
|
||||||
|
# Network Types
|
||||||
|
# https://networkx.org/documentation/stable/reference/generated/networkx.generators.degree_seq.configuration_model.html
|
||||||
|
def generate_config_model(n):
|
||||||
|
#degrees = nx.random_powerlaw_tree_sequence(n, tries=10000)
|
||||||
|
degrees = [random.randint(1, n) for i in range(n)]
|
||||||
|
if (sum(degrees)) % 2 != 0: # adjust the degree to even
|
||||||
|
degrees[-1] += 1
|
||||||
|
return nx.configuration_model(degrees) # generate the graph
|
||||||
|
|
||||||
|
|
||||||
|
def generate_scalefree_graph(n):
|
||||||
|
return nx.scale_free_graph(n)
|
||||||
|
|
||||||
|
|
||||||
|
# n must be larger than k
|
||||||
|
def generate_newman_watts_strogatz_graph(n):
|
||||||
|
return nx.newman_watts_strogatz_graph(n, 12, 0.5)
|
||||||
|
|
||||||
|
|
||||||
|
# Generate the network from nw type
|
||||||
|
def generate_network(num_nodes, nw_type, prefix):
|
||||||
G = nx.empty_graph()
|
G = nx.empty_graph()
|
||||||
if nw_type == "configuration_model":
|
if nw_type == "configuration_model":
|
||||||
G = generate_config_model(num_nodes)
|
G = generate_config_model(num_nodes)
|
||||||
|
elif nw_type == "scalefree":
|
||||||
|
G = generate_scalefree_graph(num_nodes)
|
||||||
|
elif nw_type == "newman_watts_strogatz":
|
||||||
|
G = generate_newman_watts_strogatz_graph(num_nodes)
|
||||||
else:
|
else:
|
||||||
print(nw_type +": Unsupported network type")
|
print(nw_type +": Unsupported network type")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
H = postprocess_network(G, prefix)
|
H = postprocess_network(G, prefix)
|
||||||
return H
|
return H
|
||||||
|
|
||||||
# used by generate_dump_data - *ought* to be global for handling partitions
|
|
||||||
ports_shifted = 0
|
|
||||||
|
|
||||||
|
# used by generate_dump_data, *ought* to be global to handle partitions
|
||||||
|
ports_shifted = 0
|
||||||
def postprocess_network(G, prefix):
|
def postprocess_network(G, prefix):
|
||||||
G = nx.Graph(G) # prune out parallel/multi edges
|
G = nx.Graph(G) # prune out parallel/multi edges
|
||||||
G.remove_edges_from(nx.selfloop_edges(G)) # Removing self-loops
|
G.remove_edges_from(nx.selfloop_edges(G)) # Removing self-loops
|
||||||
# Labeling nodes to match waku containers
|
# Labeling nodes to match waku containers
|
||||||
mapping = {}
|
mapping = {i: f"{prefix}{i}" for i in range(len(G))}
|
||||||
for i in range(num_nodes):
|
|
||||||
mapping[i] = prefix + str(i)
|
|
||||||
return nx.relabel_nodes(G, mapping)
|
return nx.relabel_nodes(G, mapping)
|
||||||
|
|
||||||
|
|
||||||
|
# Generate dump data from the network and topics
|
||||||
def generate_dump_data(H, topics):
|
def generate_dump_data(H, topics):
|
||||||
data_to_dump = {}
|
data_to_dump = {}
|
||||||
global ports_shifted
|
global ports_shifted
|
||||||
|
@ -125,31 +150,34 @@ def generate_dump_data(H, topics):
|
||||||
data_to_dump[node]["static-nodes"].append(edge[1])
|
data_to_dump[node]["static-nodes"].append(edge[1])
|
||||||
return data_to_dump
|
return data_to_dump
|
||||||
|
|
||||||
#extract the CLI arguments
|
|
||||||
args = init_arg_parser().parse_args()
|
|
||||||
|
|
||||||
#parameters to generate the network
|
def main():
|
||||||
fname = args.fname
|
#extract the CLI arguments and assign params
|
||||||
num_nodes = args.num_nodes
|
options = get_options()
|
||||||
num_topics = args.num_topics
|
fname = options.fname
|
||||||
nw_type = args.nw_type
|
num_nodes = options.num_nodes
|
||||||
prefix = "waku_"
|
num_topics = options.num_topics
|
||||||
num_partitions = args.num_partitions
|
nw_type = options.nw_type
|
||||||
#num_edges = args.num_edges ## do we need to control #edges?
|
prefix = "waku_"
|
||||||
|
num_partitions = options.num_partitions
|
||||||
|
#num_edges = options.num_edges ## need to control num_edges?
|
||||||
|
|
||||||
if num_partitions > 1 :
|
if num_partitions > 1:
|
||||||
print("-p",num_partitions, ": Sorry, we do not yet support partitions")
|
print("-p",num_partitions,
|
||||||
sys.exit(1)
|
"Sorry, we do not yet support partitions")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
# Generate the network and postprocess it
|
# Generate the network and postprocess it
|
||||||
H = generate_network(num_nodes, prefix)
|
H = generate_network(num_nodes, nw_type, prefix)
|
||||||
|
# Generate the topics
|
||||||
|
topics = generate_topics(num_topics)
|
||||||
|
# Generate the dump data
|
||||||
|
dump_data = generate_dump_data(H, topics)
|
||||||
|
# Dump the network in a json file
|
||||||
|
write_json(fname, dump_data)
|
||||||
|
# Display the graph
|
||||||
|
draw(fname, H)
|
||||||
|
|
||||||
#generate the topics
|
|
||||||
topics = generate_topics(num_topics)
|
|
||||||
|
|
||||||
# Generate the dump data
|
if __name__ == "__main__":
|
||||||
dump_data = generate_dump_data(H, topics)
|
main()
|
||||||
|
|
||||||
# dump the network to the json file
|
|
||||||
write_json(fname, dump_data)
|
|
||||||
#draw(H)
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
BIN
topology.png
BIN
topology.png
Binary file not shown.
Before Width: | Height: | Size: 2.3 KiB |
Loading…
Reference in New Issue