|
|
"""Routines to calculate the broadcast time of certain graphs. |
|
|
|
|
|
Broadcasting is an information dissemination problem in which a node in a graph, |
|
|
called the originator, must distribute a message to all other nodes by placing |
|
|
a series of calls along the edges of the graph. Once informed, other nodes aid |
|
|
the originator in distributing the message. |
|
|
|
|
|
The broadcasting must be completed as quickly as possible subject to the |
|
|
following constraints: |
|
|
- Each call requires one unit of time. |
|
|
- A node can only participate in one call per unit of time. |
|
|
- Each call only involves two adjacent nodes: a sender and a receiver. |
|
|
""" |
|
|
|
|
|
import networkx as nx |
|
|
from networkx.utils import not_implemented_for |
|
|
|
|
|
__all__ = [ |
|
|
"tree_broadcast_center", |
|
|
"tree_broadcast_time", |
|
|
] |
|
|
|
|
|
|
|
|
def _get_max_broadcast_value(G, U, v, values): |
|
|
adj = sorted(set(G.neighbors(v)) & U, key=values.get, reverse=True) |
|
|
return max(values[u] + i for i, u in enumerate(adj, start=1)) |
|
|
|
|
|
|
|
|
def _get_broadcast_centers(G, v, values, target): |
|
|
adj = sorted(G.neighbors(v), key=values.get, reverse=True) |
|
|
j = next(i for i, u in enumerate(adj, start=1) if values[u] + i == target) |
|
|
return set([v] + adj[:j]) |
|
|
|
|
|
|
|
|
@not_implemented_for("directed") |
|
|
@not_implemented_for("multigraph") |
|
|
@nx._dispatchable |
|
|
def tree_broadcast_center(G): |
|
|
"""Return the broadcast center of a tree. |
|
|
|
|
|
The broadcast center of a graph `G` denotes the set of nodes having |
|
|
minimum broadcast time [1]_. This function implements a linear algorithm |
|
|
for determining the broadcast center of a tree with ``n`` nodes. As a |
|
|
by-product, it also determines the broadcast time from the broadcast center. |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
G : Graph |
|
|
The graph should be an undirected tree. |
|
|
|
|
|
Returns |
|
|
------- |
|
|
b_T, b_C : (int, set) tuple |
|
|
Minimum broadcast time of the broadcast center in `G`, set of nodes |
|
|
in the broadcast center. |
|
|
|
|
|
Raises |
|
|
------ |
|
|
NetworkXNotImplemented |
|
|
If `G` is directed or is a multigraph. |
|
|
|
|
|
NotATree |
|
|
If `G` is not a tree. |
|
|
|
|
|
References |
|
|
---------- |
|
|
.. [1] Slater, P.J., Cockayne, E.J., Hedetniemi, S.T, |
|
|
Information dissemination in trees. SIAM J.Comput. 10(4), 692–701 (1981) |
|
|
""" |
|
|
|
|
|
if not nx.is_tree(G): |
|
|
raise nx.NotATree("G is not a tree") |
|
|
|
|
|
if (n := len(G)) < 3: |
|
|
return n - 1, set(G) |
|
|
|
|
|
|
|
|
U = {node for node, deg in G.degree if deg == 1} |
|
|
values = {n: 0 for n in U} |
|
|
T = G.copy() |
|
|
T.remove_nodes_from(U) |
|
|
|
|
|
|
|
|
W = {node for node, deg in T.degree if deg == 1} |
|
|
values.update((w, G.degree[w] - 1) for w in W) |
|
|
|
|
|
|
|
|
while len(T) >= 2: |
|
|
|
|
|
w = min(W, key=values.get) |
|
|
v = next(T.neighbors(w)) |
|
|
|
|
|
|
|
|
U.add(w) |
|
|
W.remove(w) |
|
|
T.remove_node(w) |
|
|
|
|
|
|
|
|
if T.degree(v) == 1: |
|
|
|
|
|
values.update({v: _get_max_broadcast_value(G, U, v, values)}) |
|
|
W.add(v) |
|
|
|
|
|
|
|
|
v = nx.utils.arbitrary_element(T) |
|
|
b_T = _get_max_broadcast_value(G, U, v, values) |
|
|
return b_T, _get_broadcast_centers(G, v, values, b_T) |
|
|
|
|
|
|
|
|
@not_implemented_for("directed") |
|
|
@not_implemented_for("multigraph") |
|
|
@nx._dispatchable |
|
|
def tree_broadcast_time(G, node=None): |
|
|
"""Return the minimum broadcast time of a (node in a) tree. |
|
|
|
|
|
The minimum broadcast time of a node is defined as the minimum amount |
|
|
of time required to complete broadcasting starting from that node. |
|
|
The broadcast time of a graph is the maximum over |
|
|
all nodes of the minimum broadcast time from that node [1]_. |
|
|
This function returns the minimum broadcast time of `node`. |
|
|
If `node` is `None`, the broadcast time for the graph is returned. |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
G : Graph |
|
|
The graph should be an undirected tree. |
|
|
|
|
|
node : node, optional (default=None) |
|
|
Starting node for the broadcasting. If `None`, the algorithm |
|
|
returns the broadcast time of the graph instead. |
|
|
|
|
|
Returns |
|
|
------- |
|
|
int |
|
|
Minimum broadcast time of `node` in `G`, or broadcast time of `G` |
|
|
if no node is provided. |
|
|
|
|
|
Raises |
|
|
------ |
|
|
NetworkXNotImplemented |
|
|
If `G` is directed or is a multigraph. |
|
|
|
|
|
NodeNotFound |
|
|
If `node` is not a node in `G`. |
|
|
|
|
|
NotATree |
|
|
If `G` is not a tree. |
|
|
|
|
|
References |
|
|
---------- |
|
|
.. [1] Harutyunyan, H. A. and Li, Z. |
|
|
"A Simple Construction of Broadcast Graphs." |
|
|
In Computing and Combinatorics. COCOON 2019 |
|
|
(Ed. D. Z. Du and C. Tian.) Springer, pp. 240-253, 2019. |
|
|
""" |
|
|
if node is not None and node not in G: |
|
|
err = f"node {node} not in G" |
|
|
raise nx.NodeNotFound(err) |
|
|
b_T, b_C = tree_broadcast_center(G) |
|
|
if node is None: |
|
|
return b_T + sum(1 for _ in nx.bfs_layers(G, b_C)) - 1 |
|
|
return b_T + next( |
|
|
d for d, layer in enumerate(nx.bfs_layers(G, b_C)) if node in layer |
|
|
) |
|
|
|