Skip to content

Commit

Permalink
reconsider the lobster generator (networkx#3822)
Browse files Browse the repository at this point in the history
Fixes networkx#3701

* reconsider the lobster generator to allow branches

* improved lobster docstring information
  • Loading branch information
austinorr authored Feb 21, 2020
1 parent a4d024c commit e9506c8
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 3 deletions.
21 changes: 18 additions & 3 deletions networkx/generators/random_graphs.py
Original file line number Diff line number Diff line change
Expand Up @@ -1000,6 +1000,11 @@ def random_lobster(n, p1, p2, seed=None):
leaf nodes. A caterpillar is a tree that reduces to a path graph
when pruning all leaf nodes; setting `p2` to zero produces a caterpillar.
This implementation iterates on the probabilities `p1` and `p2` to add
edges at levels 1 and 2, respectively. Graphs are therefore constructed
iteratively with uniform randomness at each level rather than being selected
uniformly at random from the set of all possible lobsters.
Parameters
----------
n : int
Expand All @@ -1011,19 +1016,29 @@ def random_lobster(n, p1, p2, seed=None):
seed : integer, random_state, or None (default)
Indicator of random number generation state.
See :ref:`Randomness<randomness>`.
Raises
------
NetworkXError
If `p1` or `p2` parameters are >= 1 because the while loops would never finish.
"""
p1, p2 = abs(p1), abs(p2)
if any([p >= 1 for p in [p1, p2]]):
raise nx.NetworkXError("Probability values for `p1` and `p2` must both be < 1.")

# a necessary ingredient in any self-respecting graph library
llen = int(2 * seed.random() * n + 0.5)
L = path_graph(llen)
# build caterpillar: add edges to path graph with probability p1
current_node = llen - 1
for n in range(llen):
if seed.random() < p1: # add fuzzy caterpillar parts
while seed.random() < p1: # add fuzzy caterpillar parts
current_node += 1
L.add_edge(n, current_node)
if seed.random() < p2: # add crunchy lobster bits
cat_node = current_node
while seed.random() < p2: # add crunchy lobster bits
current_node += 1
L.add_edge(current_node - 1, current_node)
L.add_edge(cat_node, current_node)
return L # voila, un lobster!


Expand Down
31 changes: 31 additions & 0 deletions networkx/generators/tests/test_random_graphs.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,38 @@ def test_random_graph(self):
constructor = [(10, 20, 0.8), (20, 40, 0.8)]
G = random_shell_graph(constructor, seed)

def is_caterpillar(g):
"""
A tree is a caterpillar iff all nodes of degree >=3 are surrounded
by at most two nodes of degree two or greater.
ref: http://mathworld.wolfram.com/CaterpillarGraph.html
"""
deg_over_3 = [n for n in g if g.degree(n) >= 3]
for n in deg_over_3:
nbh_deg_over_2 = [nbh for nbh in g.neighbors(n) if g.degree(nbh) >= 2]
if not len(nbh_deg_over_2) <= 2:
return False
return True

def is_lobster(g):
"""
A tree is a lobster if it has the property that the removal of leaf
nodes leaves a caterpillar graph (Gallian 2007)
ref: http://mathworld.wolfram.com/LobsterGraph.html
"""
non_leafs = [n for n in g if g.degree(n) > 1]
return is_caterpillar(g.subgraph(non_leafs))

G = random_lobster(10, 0.1, 0.5, seed)
assert max([G.degree(n) for n in G.nodes()]) > 3
assert is_lobster(G)
pytest.raises(NetworkXError, random_lobster, 10, 0.1, 1, seed)
pytest.raises(NetworkXError, random_lobster, 10, 1, 1, seed)
pytest.raises(NetworkXError, random_lobster, 10, 1, 0.5, seed)

# docstring says this should be a caterpillar
G = random_lobster(10, 0.1, 0.0, seed)
assert is_caterpillar(G)

# difficult to find seed that requires few tries
seq = random_powerlaw_tree_sequence(10, 3, seed=14, tries=1)
Expand Down

0 comments on commit e9506c8

Please sign in to comment.