Skip to content

Commit

Permalink
Erdos renyi gilbert (#159)
Browse files Browse the repository at this point in the history
* edit : minor edit in `pyrgg_gen_using`.

* add : `edge_gen_erg` and `erdos_renyi_gilbert_gen_using` functions added.

* add : `probability` added to `input_dict`.

* update : functions and params updated for ERG model.

* update : tests updated.

* log : changes logged. (#146)

* rename : `handle_str_to_float_0_1` -> `handle_str_prob`.

* fix : `erdos_renyi_gilbert_gen_using` docstring.

* fix : `erdos_renyi_gilbert_gen_using` return added.

* test : a test added to `graph_gen_test.py`.

* fix : typo in tests fixed.

* test : tests added for `handle_str_prob`.

* fix : typo fixed in `functions_test.py`.
  • Loading branch information
sadrasabouri authored Feb 9, 2024
1 parent 4d73c0d commit 901d2f7
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 36 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [Unreleased]
### Added
- `Erdős-Rényi-Gilbert` generation model
- `pyrgg_gen_using` function
- Generation engine menu
- `handle_string` function
Expand Down
5 changes: 4 additions & 1 deletion pyrgg/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

ENGINE_GENERATOR = {
1: pyrgg_gen_using,
2: erdos_renyi_gilbert_gen_using,
}


Expand All @@ -54,6 +55,7 @@ def gen_graph(input_dict, file_name):
self_loop = input_dict["self_loop"]
multigraph = input_dict["multigraph"]
output_format = input_dict["output_format"]
probability = input_dict["probability"]
engine = input_dict["engine"]
edge_number = ENGINE_GENERATOR[engine](
GENERATOR_MENU[output_format],
Expand All @@ -66,7 +68,8 @@ def gen_graph(input_dict, file_name):
sign=sign,
direct=direct,
self_loop=self_loop,
multigraph=multigraph)
multigraph=multigraph,
probability=probability)
if output_format == 4:
json_to_yaml(file_name)
if output_format == 7:
Expand Down
92 changes: 84 additions & 8 deletions pyrgg/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from json import dump as json_dump
import os
from pickle import dump as pickle_dump
from random import randint, uniform, choice
from random import randint, uniform, choice, random
from yaml import safe_dump as yaml_dump
import pyrgg.params

Expand Down Expand Up @@ -98,6 +98,22 @@ def handle_str_to_number(string):
return float(string) if is_float(string) else int(string)


def handle_str_prob(string):
"""
Convert string to float and raise ValueError if string is invalid.
:param string: input string
:type string: str
:return: result as float
"""
val = handle_str_to_number(string)
if val < 0:
raise ValueError
if val > 1:
raise ValueError
return val


def handle_str_to_bool(string):
"""
Convert 0/1 string to bool and raise ValueError if string is invalid.
Expand Down Expand Up @@ -156,6 +172,7 @@ def handle_engine(string):
"self_loop": handle_str_to_bool,
"multigraph": handle_str_to_bool,
"config": handle_str_to_bool,
"probability": handle_str_prob,
}


Expand Down Expand Up @@ -365,6 +382,7 @@ def get_input(input_func=input):
"self_loop": True,
"multigraph": False,
"config": False,
"probability": 0.5,
}

result_dict = _update_using_menu(result_dict, input_func)
Expand Down Expand Up @@ -429,7 +447,7 @@ def _update_with_engine_params(result_dict, input_func, engine_params):

def _threshold_calc(min_edge, max_edge, vertex_degree):
"""
Calculate threshold for branch_gen function.
Calculate threshold for branch_gen_pyrgg function.
:param min_edge: minimum number of edges (connected to each vertex)
:type min_edge: int
Expand All @@ -449,7 +467,7 @@ def _threshold_calc(min_edge, max_edge, vertex_degree):
return threshold


def branch_gen(
def branch_gen_pyrgg(
vertex_index,
max_edge,
min_edge,
Expand Down Expand Up @@ -562,7 +580,7 @@ def branch_gen(
return [branch_list, weight_list]


def edge_gen(
def edge_gen_pyrgg(
vertices_number,
min_weight,
max_weight,
Expand Down Expand Up @@ -622,7 +640,7 @@ def edge_gen(
"degree_sort_dict": degree_sort_dict,
"precision": precision}
for i in vertices_id:
temp_list = branch_gen(vertex_index=i, **branch_gen_params)
temp_list = branch_gen_pyrgg(vertex_index=i, **branch_gen_params)
vertices_edge.append(temp_list[0])
weight_list.append(temp_list[1])
temp = temp + len(temp_list[0])
Expand Down Expand Up @@ -748,15 +766,15 @@ def pyrgg_gen_using(
gen_function,
**kwargs):
"""
Generate graph using given function.
Generate graph using given function based on PyRGG model.
:param gen_function: generation function
:type gen_function: function object
:param kwargs: input data as keyword arguments
:type kwargs: dict
:return: number of edges as int
"""
edge_dic, weight_dic, edge_number = edge_gen(
edge_dic, weight_dic, edge_number = edge_gen_pyrgg(
kwargs['vertices_number'],
kwargs['min_weight'],
kwargs['max_weight'],
Expand All @@ -780,6 +798,64 @@ def pyrgg_gen_using(
"direct": kwargs['direct'],
"self_loop": kwargs['self_loop'],
"multigraph": kwargs['multigraph'],
"edge_number": edge_number
"edge_number": edge_number,
})
return edge_number


def edge_gen_erg(n, p):
"""
Generate each vertex connection number.
:param n: number of vertices
:type n: int
:param p: probability
:type p: float
:return: list of dicts
"""
edge_dic = {}
edge_number = 0
for i in range(1, n + 1):
edge_dic[i] = []
for j in range(i + 1, n + 1):
if random() < p:
edge_dic[i].append(j)
edge_number += 1
return edge_dic, edge_number


def erdos_renyi_gilbert_gen_using(
gen_function,
**kwargs):
"""
Generate graph using given function based on Erdos Renyi Gilbert model.
Refer to (https://en.wikipedia.org/wiki/Erd%C5%91s%E2%80%93R%C3%A9nyi_model)
:param gen_function: generation function
:type gen_function: function object
:param kwargs: input data as keyword arguments
:type kwargs: dict
:return: number of edges as int
"""
edge_dic, edge_number = edge_gen_erg(
kwargs['vertices_number'],
kwargs['probability'])
weight_dic = {key: [1] * edge_number for key in range(1, kwargs['vertices_number'] + 1)}
gen_function(
edge_dic,
weight_dic,
{
"file_name": kwargs['file_name'],
"vertices_number": kwargs['vertices_number'],
"max_weight": 1,
"min_weight": 1,
"min_edge": edge_number,
"max_edge": edge_number,
"sign": False,
"direct": False,
"self_loop": False,
"multigraph": False,
"edge_number": edge_number,
})
return edge_number
24 changes: 16 additions & 8 deletions pyrgg/params.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"""\
- Select generation engine :
1- Pyrgg engine
2- Erdős-Rényi-Gilbert - G(n, p)
"""
)],
2: ["file_name", "- File Name (Not Empty) : "],
Expand All @@ -33,7 +34,7 @@
16- DOT(.gv)
"""
)],
5: ["weight", "- Unweighted[0] or Weighted[1]"],
5: ["config", "- Save Config[1] or Not[0]"],
}

SUFFIX_MENU = {
Expand All @@ -59,23 +60,30 @@
1: ["vertices", "- Vertices Number (>=0) : "],
2: ["min_edge", "- Min Edge Number - Connected to Each Vertex (>=0) : "],
3: ["max_edge", "- Max Edge Number - Connected to Each Vertex (>=0) : "],
4: ["min_weight", "- Min Weight : "],
5: ["max_weight", "- Max Weight : "],
6: ["sign", "- Unsigned[0] or Signed[1]"],
7: ["direct", "- Undirected[0] or Directed[1]"],
8: ["self_loop", "- No Self Loop[0] or Self Loop[1]"],
9: ["multigraph", "- Simple[0] or Multigraph[1]"],
10: ["config", "- Save Config[1] or Not[0]"],
4: ["weight", "- Unweighted[0] or Weighted[1]"],
5: ["min_weight", "- Min Weight : "],
6: ["max_weight", "- Max Weight : "],
7: ["sign", "- Unsigned[0] or Signed[1]"],
8: ["direct", "- Undirected[0] or Directed[1]"],
9: ["self_loop", "- No Self Loop[0] or Self Loop[1]"],
10: ["multigraph", "- Simple[0] or Multigraph[1]"],
}

ERG_ENGINE_PARAMS = {
1: ["vertices", "- Vertices Number (>=0) : "],
2: ["probability", "- Probability (0 <= p <= 1) : "],
}

ENGINE_MENU = {
1: "pyrgg",
2: "erg",
}

ENGINE_MENU_INV = {v: k for k, v in ENGINE_MENU.items()}

ENGINE_PARAM_MAP = {
1: PYRGG_ENGINE_PARAMS,
2: ERG_ENGINE_PARAMS,
}

OUTPUT_FORMAT = {i: output_format[1:].upper()
Expand Down
18 changes: 9 additions & 9 deletions pyrgg/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,31 +86,31 @@
>>> degree_dict_sort[0] = {i:i for i in range(1,41)}
>>> all_vertices = list(range(1, 41))
>>> random.seed(2)
>>> branch_gen(1,10,10,1,20,0,True,True,True,False,used_vertices,degree_dict,degree_dict_sort)
>>> branch_gen_pyrgg(1,10,10,1,20,0,True,True,True,False,used_vertices,degree_dict,degree_dict_sort)
[[4, 25, 18, 3, 30, 34, 2, 26, 14, 11], [3, 10, 20, 14, -18, -2, -15, -14, 8, 6]]
>>> random.seed(20)
>>> branch_gen(1,10,4,1,20,0,False,True,True,False,used_vertices,degree_dict,degree_dict_sort)
>>> branch_gen_pyrgg(1,10,4,1,20,0,False,True,True,False,used_vertices,degree_dict,degree_dict_sort)
[[], []]
>>> used_vertices = {k:[] for k in range(1,41)}
>>> degree_dict = {k:0 for k in range(1,41)}
>>> degree_dict_sort = {k:{} for k in range(41)}
>>> degree_dict_sort[0] = {i:i for i in range(1,41)}
>>> branch_gen(1,10,4,1,20,0,False,True,True,False,used_vertices,degree_dict,degree_dict_sort)
>>> branch_gen_pyrgg(1,10,4,1,20,0,False,True,True,False,used_vertices,degree_dict,degree_dict_sort)
[[10, 7, 39, 2, 30, 9, 25, 35, 18], [9, 11, 6, 14, 3, 5, 16, 14, 7]]
>>> branch_gen(40,1,20,1)
>>> branch_gen_pyrgg(40,1,20,1)
Traceback (most recent call last):
...
TypeError: branch_gen() missing 9 required positional arguments: 'max_weight', 'precision', 'sign', 'direct', 'self_loop', 'multigraph', 'used_vertices', 'degree_dict', and 'degree_sort_dict'
TypeError: branch_gen_pyrgg() missing 9 required positional arguments: 'max_weight', 'precision', 'sign', 'direct', 'self_loop', 'multigraph', 'used_vertices', 'degree_dict', and 'degree_sort_dict'
>>> random.seed(2)
>>> edge_gen(20,0,400,2,10,True,True,True,False)
>>> edge_gen_pyrgg(20,0,400,2,10,True,True,True,False)
[{1: [3, 7], 2: [4, 17, 20, 9, 11], 3: [14, 8, 5, 12, 16, 19, 15], 4: [15, 17, 12, 8, 14, 13], 5: [16, 9, 7, 20, 19, 18, 13, 5], 6: [6, 10], 7: [18, 10, 11], 8: [], 9: [], 10: [12, 18, 8, 1, 14], 11: [9, 11], 12: [], 13: [], 14: [19, 16, 17, 20, 15], 15: [6, 1, 19], 16: [12, 13, 8, 9, 17], 17: [], 18: [9, 12, 17, 6, 20, 19, 1], 19: [13], 20: []}, {1: [184, -128], 2: [220, -278, -257, 14, -163], 3: [286, 118, 166, 261, -263, 228, -303], 4: [-82, -335, 250, -256, -338, -179], 5: [-337, -358, -395, -155, -159, 250, -350, -371], 6: [30, -302], 7: [386, -125, 216], 8: [], 9: [], 10: [127, 42, 12, 191, 80], 11: [-301, 77], 12: [], 13: [], 14: [146, -15, -282, 135, 242], 15: [-52, -65, -249], 16: [-132, -334, 343, -17, 87], 17: [], 18: [126, -37, 302, -131, -142, 77, -209], 19: [123], 20: []}, 61]
>>> random.seed(11)
>>> edge_gen(20,0,100,2,10,False,True,True,False)
>>> edge_gen_pyrgg(20,0,100,2,10,False,True,True,False)
[{1: [18, 15, 19, 7, 20, 11, 2, 6, 3], 2: [17], 3: [8, 4, 5, 9, 12, 10, 14, 16], 4: [20, 13, 4, 6], 5: [12, 7, 11, 10, 14], 6: [9], 7: [19], 8: [8, 18, 11, 2, 16, 17, 10], 9: [15, 12, 18], 10: [20, 14, 13, 15, 17, 16], 11: [19, 7, 20], 12: [13], 13: [2, 16, 13], 14: [18, 19, 6, 14, 17, 15], 15: [6, 7, 16], 16: [17, 20, 12, 18], 17: [19], 18: [7, 6, 9, 12, 20], 19: [19, 11, 4], 20: []}, {1: [99, 57, 75, 23, 80, 23, 57, 18, 68], 2: [50], 3: [79, 67, 7, 24, 76, 99, 41, 75], 4: [29, 63, 84, 58], 5: [70, 90, 40, 65, 3], 6: [51], 7: [37], 8: [2, 0, 26, 60, 90, 53, 72], 9: [43, 39, 1], 10: [15, 31, 1, 59, 22, 57], 11: [98, 53, 49], 12: [53], 13: [34, 2, 23], 14: [82, 12, 18, 56, 1, 37], 15: [9, 26, 1], 16: [47, 58, 75, 73], 17: [23], 18: [39, 78, 92, 20, 49], 19: [10, 6, 13], 20: []}, 74]
>>> edge_gen(0,400,2,10,1)
>>> edge_gen_pyrgg(0,400,2,10,1)
Traceback (most recent call last):
...
TypeError: edge_gen() missing 4 required positional arguments: 'sign', 'direct', 'self_loop', and 'multigraph'
TypeError: edge_gen_pyrgg() missing 4 required positional arguments: 'sign', 'direct', 'self_loop', and 'multigraph'
>>> random.seed(2)
>>> pyrgg_gen_using(dimacs_maker, file_name='testfile', min_weight=0, max_weight=200, vertices_number=10, min_edge=0, max_edge=2, sign=True, direct=True, self_loop=True, multigraph=False)
7
Expand Down
30 changes: 20 additions & 10 deletions test/functions_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,16 @@
ValueError
>>> handle_str_to_bool("0")
False
>>> handle_str_prob("0.2")
0.2
>>> handle_str_prob("-0.2")
Traceback (most recent call last):
...
ValueError
>>> handle_str_prob("1.2")
Traceback (most recent call last):
...
ValueError
>>> is_float(10)
False
>>> is_float(10.2)
Expand All @@ -131,39 +141,39 @@
>>> degree_dict = {1:2,2:3,3:3,4:3,5:3}
>>> degree_dict_sort = {0:{},1:{},2:{1:1},3:{2:2,3:3,4:4,5:5},4:{},5:{}}
>>> all_vertices = list(range(1, 6))
>>> branch_gen(1,3,3,300,3000,0,True,False,False,False,used_vertices,degree_dict,degree_dict_sort)
>>> branch_gen_pyrgg(1,3,3,300,3000,0,True,False,False,False,used_vertices,degree_dict,degree_dict_sort)
[[], []]
>>> used_vertices = {k:[] for k in range(1,41)}
>>> degree_dict = {k:0 for k in range(1,41)}
>>> degree_dict_sort = {k:{} for k in range(41)}
>>> degree_dict_sort[0] = {i:i for i in range(1,41)}
>>> all_vertices = list(range(1, 41))
>>> random.seed(2)
>>> branch_gen(1,10,10,1,20,0,True,True,True,False,used_vertices,degree_dict,degree_dict_sort)
>>> branch_gen_pyrgg(1,10,10,1,20,0,True,True,True,False,used_vertices,degree_dict,degree_dict_sort)
[[4, 25, 18, 3, 30, 34, 2, 26, 14, 11], [3, 10, 20, 14, -18, -2, -15, -14, 8, 6]]
>>> random.seed(20)
>>> branch_gen(1,10,4,1,20,0,False,True,True,False,used_vertices,degree_dict,degree_dict_sort)
>>> branch_gen_pyrgg(1,10,4,1,20,0,False,True,True,False,used_vertices,degree_dict,degree_dict_sort)
[[], []]
>>> used_vertices = {k:[] for k in range(1,41)}
>>> degree_dict = {k:0 for k in range(1,41)}
>>> degree_dict_sort = {k:{} for k in range(41)}
>>> degree_dict_sort[0] = {i:i for i in range(1,41)}
>>> branch_gen(1,10,4,1,20,0,False,True,True,False,used_vertices,degree_dict,degree_dict_sort)
>>> branch_gen_pyrgg(1,10,4,1,20,0,False,True,True,False,used_vertices,degree_dict,degree_dict_sort)
[[10, 7, 39, 2, 30, 9, 25, 35, 18], [9, 11, 6, 14, 3, 5, 16, 14, 7]]
>>> branch_gen(40,1,20,1)
>>> branch_gen_pyrgg(40,1,20,1)
Traceback (most recent call last):
...
TypeError: branch_gen() missing 9 required positional arguments: 'max_weight', 'precision', 'sign', 'direct', 'self_loop', 'multigraph', 'used_vertices', 'degree_dict', and 'degree_sort_dict'
TypeError: branch_gen_pyrgg() missing 9 required positional arguments: 'max_weight', 'precision', 'sign', 'direct', 'self_loop', 'multigraph', 'used_vertices', 'degree_dict', and 'degree_sort_dict'
>>> random.seed(2)
>>> edge_gen(20,0,400,2,10,True,True,True,False)
>>> edge_gen_pyrgg(20,0,400,2,10,True,True,True,False)
[{1: [3, 7], 2: [4, 17, 20, 9, 11], 3: [14, 8, 5, 12, 16, 19, 15], 4: [15, 17, 12, 8, 14, 13], 5: [16, 9, 7, 20, 19, 18, 13, 5], 6: [6, 10], 7: [18, 10, 11], 8: [], 9: [], 10: [12, 18, 8, 1, 14], 11: [9, 11], 12: [], 13: [], 14: [19, 16, 17, 20, 15], 15: [6, 1, 19], 16: [12, 13, 8, 9, 17], 17: [], 18: [9, 12, 17, 6, 20, 19, 1], 19: [13], 20: []}, {1: [184, -128], 2: [220, -278, -257, 14, -163], 3: [286, 118, 166, 261, -263, 228, -303], 4: [-82, -335, 250, -256, -338, -179], 5: [-337, -358, -395, -155, -159, 250, -350, -371], 6: [30, -302], 7: [386, -125, 216], 8: [], 9: [], 10: [127, 42, 12, 191, 80], 11: [-301, 77], 12: [], 13: [], 14: [146, -15, -282, 135, 242], 15: [-52, -65, -249], 16: [-132, -334, 343, -17, 87], 17: [], 18: [126, -37, 302, -131, -142, 77, -209], 19: [123], 20: []}, 61]
>>> random.seed(11)
>>> edge_gen(20,0,100,2,10,False,True,True,False)
>>> edge_gen_pyrgg(20,0,100,2,10,False,True,True,False)
[{1: [18, 15, 19, 7, 20, 11, 2, 6, 3], 2: [17], 3: [8, 4, 5, 9, 12, 10, 14, 16], 4: [20, 13, 4, 6], 5: [12, 7, 11, 10, 14], 6: [9], 7: [19], 8: [8, 18, 11, 2, 16, 17, 10], 9: [15, 12, 18], 10: [20, 14, 13, 15, 17, 16], 11: [19, 7, 20], 12: [13], 13: [2, 16, 13], 14: [18, 19, 6, 14, 17, 15], 15: [6, 7, 16], 16: [17, 20, 12, 18], 17: [19], 18: [7, 6, 9, 12, 20], 19: [19, 11, 4], 20: []}, {1: [99, 57, 75, 23, 80, 23, 57, 18, 68], 2: [50], 3: [79, 67, 7, 24, 76, 99, 41, 75], 4: [29, 63, 84, 58], 5: [70, 90, 40, 65, 3], 6: [51], 7: [37], 8: [2, 0, 26, 60, 90, 53, 72], 9: [43, 39, 1], 10: [15, 31, 1, 59, 22, 57], 11: [98, 53, 49], 12: [53], 13: [34, 2, 23], 14: [82, 12, 18, 56, 1, 37], 15: [9, 26, 1], 16: [47, 58, 75, 73], 17: [23], 18: [39, 78, 92, 20, 49], 19: [10, 6, 13], 20: []}, 74]
>>> edge_gen(0,400,2,10,1)
>>> edge_gen_pyrgg(0,400,2,10,1)
Traceback (most recent call last):
...
TypeError: edge_gen() missing 4 required positional arguments: 'sign', 'direct', 'self_loop', and 'multigraph'
TypeError: edge_gen_pyrgg() missing 4 required positional arguments: 'sign', 'direct', 'self_loop', and 'multigraph'
>>> prev_item = ""
>>> input_func_dict = {"vertices":"120","max_weight":"110","min_weight":"0","min_edge":"1","max_edge":"1000","sign":"1","direct":"1","self_loop":"1","multigraph":"0","file_name":"File 1","output_format":"2","weight":"1","error":"120","number_of_files":"3","config":"0","engine":"1"}
>>> def input_func_test(input_data):
Expand Down
19 changes: 19 additions & 0 deletions test/graph_gen_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,25 @@
a 8 2 -97
a 9 1 60
<BLANKLINE>
>>> random.seed(2)
>>> erdos_renyi_gilbert_gen_using(dimacs_maker, file_name='testfile', vertices_number=10, probability=0.1)
5
>>> file=open('testfile.gr','r')
>>> print(file.read())
c FILE :testfile.gr
c No. of vertices :10
c No. of edges :5
c Max. weight :1
c Min. weight :1
c Min. edge :5
c Max. edge :5
p sp 10 5
a 1 4 1
a 1 5 1
a 3 7 1
a 3 8 1
a 4 10 1
<BLANKLINE>
>>> random.seed(4)
>>> pyrgg_gen_using(dimacs_maker, file_name='testfile2', min_weight=0, max_weight=50, vertices_number=30, min_edge=0, max_edge=4, sign=True, direct=True, self_loop=True, multigraph=False)
35
Expand Down

0 comments on commit 901d2f7

Please sign in to comment.