Skip to content

Add random_tournament_graph #378

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 2, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion quantecon/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from .ecdf import ECDF
from .estspec import smooth, periodogram, ar_periodogram
# from .game_theory import <objects-here> #Place Holder if we wish to promote any general objects to the qe namespace.
from .graph_tools import DiGraph
from .graph_tools import DiGraph, random_tournament_graph
from .gridtools import cartesian, mlinspace, simplex_grid, simplex_index
from .kalman import Kalman
from .lae import LAE
Expand Down
70 changes: 70 additions & 0 deletions quantecon/graph_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
from scipy import sparse
from scipy.sparse import csgraph
from fractions import gcd
from numba import jit

from .util import check_random_state


# Decorator for *_components properties
Expand Down Expand Up @@ -371,3 +374,70 @@ def _csr_matrix_indices(S):
for j in range(S.indptr[i], S.indptr[i+1]):
row_index, col_index = i, S.indices[j]
yield row_index, col_index


def random_tournament_graph(n, random_state=None):
"""
Return a random tournament graph [1]_ with n nodes.

Parameters
----------
n : scalar(int)
Number of nodes.

random_state : int or np.random.RandomState, optional
Random seed (integer) or np.random.RandomState instance to set
the initial state of the random number generator for
reproducibility. If None, a randomly initialized RandomState is
used.

Returns
-------
DiGraph
A DiGraph representing the tournament graph.

References
----------
.. [1] `Tournament (graph theory)
<https://en.wikipedia.org/wiki/Tournament_(graph_theory)>`_,
Wikipedia.

"""
random_state = check_random_state(random_state)
num_edges = n * (n-1) // 2
r = random_state.random_sample(num_edges)
row = np.empty(num_edges, dtype=int)
col = np.empty(num_edges, dtype=int)
_populate_random_tournament_row_col(n, r, row, col)
data = np.ones(num_edges, dtype=bool)
adj_matrix = sparse.coo_matrix((data, (row, col)), shape=(n, n))
return DiGraph(adj_matrix)


@jit(nopython=True, cache=True)
def _populate_random_tournament_row_col(n, r, row, col):
"""
Populate ndarrays `row` and `col` with directed edge indices
determined by random numbers in `r` for a tournament graph with n
nodes, which has num_edges = n * (n-1) // 2 edges.

Parameters
----------
n : scalar(int)
Number of nodes.

r : ndarray(float, ndim=1)
ndarray of length num_edges containing random numbers in [0, 1).

row, col : ndarray(int, ndim=1)
ndarrays of length num_edges to be modified in place.

"""
k = 0
for i in range(n):
for j in range(i+1, n):
if r[k] < 0.5:
row[k], col[k] = i, j
else:
row[k], col[k] = j, i
k += 1
25 changes: 24 additions & 1 deletion quantecon/tests/test_graph_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import nose
from nose.tools import eq_, ok_, raises

from quantecon.graph_tools import DiGraph
from quantecon.graph_tools import DiGraph, random_tournament_graph


def list_of_array_equal(s, t):
Expand Down Expand Up @@ -300,6 +300,29 @@ def test_raises_non_homogeneous_node_labels():
assert_raises(ValueError, DiGraph, adj_matrix, node_labels=node_labels)


class TestRandomTournamentGraph:
def setUp(self):
n = 5
g = random_tournament_graph(n)
self.adj_matrix = g.csgraph.toarray()
self.eye_bool = np.eye(n, dtype=bool)

def test_diagonal(self):
# Test no self loop
ok_(not self.adj_matrix[self.eye_bool].any())

def test_off_diagonal(self):
# Test for each pair of distinct nodes to have exactly one edge
ok_((self.adj_matrix ^ self.adj_matrix.T)[~self.eye_bool].all())


def test_random_tournament_graph_seed():
n = 7
seed = 1234
graphs = [random_tournament_graph(n, random_state=seed) for i in range(2)]
assert_array_equal(*[g.csgraph.toarray() for g in graphs])


if __name__ == '__main__':
argv = sys.argv[:]
argv.append('--verbose')
Expand Down