Skip to content

Commit

Permalink
Graph tests
Browse files Browse the repository at this point in the history
  • Loading branch information
marcharper committed Jan 2, 2017
1 parent 88989f7 commit 5332637
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 32 deletions.
66 changes: 34 additions & 32 deletions axelrod/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,33 +18,37 @@ class Graph(object):
[[node1, node2, weights], ...]
Weights can be omitted for an undirected graph.
For efficiency, neighbors are cached in dictionaries.
For efficiency, neighbors are cached in dictionaries. Undirected graphs
are implemented as directed graphs in which every edge (s, t) has the
opposite edge (t, s).
"""

def __init__(self, edges=None, directed=False):
self.directed = directed
self.original_edges = edges
self.out_mapping = defaultdict(lambda: defaultdict(float))
self.in_mapping = defaultdict(lambda: defaultdict(float))
self.directed = directed
self._edges = []
if edges:
self.add_edges(edges)

def add_vertex(self, label):
self._vertices.add(label)

def add_edge(self, source, target, weight=1.):
self.out_mapping[source][target] = weight
self.in_mapping[target][source] = weight
if not self.directed:
def add_edge(self, source, target, weight=None):
if (source, target) not in self._edges:
self._edges.append((source, target))
self.out_mapping[source][target] = weight
self.in_mapping[target][source] = weight
if not self.directed and (source != target) and \
(target, source) not in self._edges:
self._edges.append((target, source))
self.out_mapping[target][source] = weight
self.in_mapping[source][target] = weight

def add_edges(self, edges):
try:
for source, target, weight in edges:
self.add_edge(source, target, weight)
except ValueError:
for source, target in edges:
self.add_edge(source, target, 1.0)
for edge in edges:
self.add_edge(*edge)

def edges(self):
return self._edges

def vertices(self):
"""Returns the set of vertices of the graph."""
Expand All @@ -62,20 +66,13 @@ def in_dict(self, target):
"""Returns a dictionary of the incoming edges of source with weights."""
return self.in_mapping[target]

def normalize_weights(self):
"""Normalizes the weights coming out of each vertex to be probability
distributions."""
new_edges = []
for source in self.out_mapping.keys():
total = float(sum(out_mapping[source].values()))
for target, weight in self.out_mapping.items():
self.out_mapping[target] = weight / total
self._edges = new_edges
def in_vertices(self, source):
"""Returns a list of the outgoing vertices."""
return list(self.in_mapping[source].keys())

def __getitem__(self, k):
"""Returns the dictionary of outgoing edges. You can access the weight
of an edge with g[source][target]."""
return self.out_mapping[k]
def __repr__(self):
s = "<Graph: {}>".format(repr(self.original_edges))
return s


## Example Graphs
Expand Down Expand Up @@ -104,9 +101,11 @@ def cycle(length, directed=False):
return graph


def complete_graph(length, directed=False):
def complete_graph(length, loops=True):
"""
Produces a complete graph of size `length`.
Produces a complete graph of size `length`, with loops.
https://en.wikipedia.org/wiki/Complete_graph
Parameters
----------
length: int
Expand All @@ -117,10 +116,13 @@ def complete_graph(length, directed=False):
-------
a Graph object
"""
graph = Graph(directed=directed)
offset = 1
if loops:
offset = 0
graph = Graph(directed=False)
edges = []
for i in range(length):
for j in range(length):
for j in range(i + offset, length):
edges.append((i, j))
graph.add_edges(edges)
return graph
5 changes: 5 additions & 0 deletions axelrod/moran.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,11 @@ def __init__(self, players, interaction_graph, reproduction_graph=None,
population. This is not the only method yet emulates the common method
in the literature.
Note: the weighted graph case is not yet implemented.
See [Shakarian2013]_ for more detail on the process and different
updating modes.
Parameters
----------
players, iterable of axelrod.Player subclasses
Expand Down
96 changes: 96 additions & 0 deletions axelrod/tests/unit/test_graph.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import unittest

from axelrod import graph

class TestGraph(unittest.TestCase):

def test_cycle(self):
g = graph.cycle(1, directed=False)
self.assertEqual(g.vertices(), [0])
self.assertEqual(g.edges(), [(0, 0)])
self.assertEqual(g.directed, False)
g = graph.cycle(1, directed=True)
self.assertEqual(g.vertices(), [0])
self.assertEqual(g.edges(), [(0, 0)])
self.assertEqual(g.directed, True)
g = graph.cycle(2, directed=True)
self.assertEqual(g.vertices(), [0, 1])
self.assertEqual(g.edges(), [(0, 1), (1, 0)])
g = graph.cycle(2, directed=False)
self.assertEqual(g.vertices(), [0, 1])
self.assertEqual(g.edges(), [(0, 1), (1, 0)])
g = graph.cycle(3, directed=True)
self.assertEqual(g.vertices(), [0, 1, 2])
self.assertEqual(g.edges(), [(0, 1), (1, 2), (2, 0)])
g = graph.cycle(3, directed=False)
edges = [(0, 1), (1, 0), (1, 2), (2, 1), (2, 0), (0, 2)]
self.assertEqual(g.vertices(), [0, 1, 2])
self.assertEqual(g.edges(), edges)
g = graph.cycle(4, directed=True)
self.assertEqual(g.vertices(), [0, 1, 2, 3])
self.assertEqual(g.edges(), [(0, 1), (1, 2), (2, 3), (3, 0)])
self.assertEqual(g.out_vertices(0), [1])
self.assertEqual(g.out_vertices(1), [2])
self.assertEqual(g.out_vertices(2), [3])
self.assertEqual(g.out_vertices(3), [0])
self.assertEqual(g.in_vertices(0), [3])
self.assertEqual(g.in_vertices(1), [0])
self.assertEqual(g.in_vertices(2), [1])
self.assertEqual(g.in_vertices(3), [2])
g = graph.cycle(4, directed=False)
edges = [(0, 1), (1, 0), (1, 2), (2, 1),
(2, 3), (3, 2), (3, 0), (0, 3)]
self.assertEqual(g.vertices(), [0, 1, 2, 3])
self.assertEqual(g.edges(), edges)
for vertex, neighbors in [
(0, (1, 3)), (1, (0, 2)), (2, (1, 3)), (3, (0, 2))]:
self.assertEqual(set(g.out_vertices(0)), set(neighbors))
for vertex, neighbors in [
(0, (1, 3)), (1, (0, 2)), (2, (1, 3)), (3, (0, 2))]:
self.assertEqual(set(g.in_vertices(0)), set(neighbors))

def test_complete(self):
g = graph.complete_graph(2, loops=False)
self.assertEqual(g.vertices(), [0, 1])
self.assertEqual(g.edges(), [(0, 1), (1, 0)])
self.assertEqual(g.directed, False)
g = graph.complete_graph(3, loops=False)
self.assertEqual(g.vertices(), [0, 1, 2])
edges = [(0, 1), (1, 0), (0, 2), (2, 0), (1, 2), (2, 1)]
self.assertEqual(g.edges(), edges)
self.assertEqual(g.directed, False)
g = graph.complete_graph(4, loops=False )
self.assertEqual(g.vertices(), [0, 1, 2, 3])
edges = [(0, 1), (1, 0), (0, 2), (2, 0), (0, 3), (3, 0),
(1, 2), (2, 1), (1, 3), (3, 1), (2, 3), (3, 2)]
self.assertEqual(g.edges(), edges)
self.assertEqual(g.directed, False)
for vertex, neighbors in [
(0, (1, 2, 3)), (1, (0, 2, 3)), (2, (0, 1, 3)), (3, (0, 1, 2))]:
self.assertEqual(set(g.out_vertices(0)), set(neighbors))
for vertex, neighbors in [
(0, (1, 2, 3)), (1, (0, 2, 3)), (2, (0, 1, 3)), (3, (0, 1, 2))]:
self.assertEqual(set(g.in_vertices(0)), set(neighbors))

def test_complete_with_loops(self):
g = graph.complete_graph(2, loops=True)
self.assertEqual(g.vertices(), [0, 1])
self.assertEqual(g.edges(), [(0, 0), (0, 1), (1, 0), (1, 1)])
self.assertEqual(g.directed, False)
g = graph.complete_graph(3, loops=True)
self.assertEqual(g.vertices(), [0, 1, 2])
edges = [(0, 0), (0, 1), (1, 0), (0, 2), (2, 0), (1, 1),
(1, 2), (2, 1), (2, 2)]
self.assertEqual(g.edges(), edges)
self.assertEqual(g.directed, False)
g = graph.complete_graph(4, loops=True)
self.assertEqual(g.vertices(), [0, 1, 2, 3])
edges = [(0, 0), (0, 1), (1, 0), (0, 2), (2, 0), (0, 3), (3, 0),
(1, 1), (1, 2), (2, 1), (1, 3), (3, 1),
(2, 2), (2, 3), (3, 2), (3, 3)]
self.assertEqual(g.edges(), edges)
self.assertEqual(g.directed, False)
neighbors = range(4)
for vertex in range(4):
self.assertEqual(set(g.out_vertices(vertex)), set(neighbors))
self.assertEqual(set(g.in_vertices(vertex)), set(neighbors))
1 change: 1 addition & 0 deletions docs/reference/bibliography.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ documentation.
.. [PRISON1998] LIFL (1998) PRISON. Available at: http://www.lifl.fr/IPD/ipd.frame.html (Accessed: 19 September 2016).
.. [Robson1989] Robson, Arthur, (1989), EFFICIENCY IN EVOLUTIONARY GAMES: DARWIN, NASH AND SECRET HANDSHAKE, Working Papers, Michigan - Center for Research on Economic & Social Theory, http://EconPapers.repec.org/RePEc:fth:michet:89-22.
.. [Singer-Clark2014] Singer-Clark, T. (2014). Morality Metrics On Iterated Prisoner’s Dilemma Players.
.. [Shakarian2013] Shakarian, P., Roos, P. & Moores, G. A Novel Analytical Method for Evolutionary Graph Theory Problems.
.. [Slany2007] Slany W. and Kienreich W., On some winning strategies for the iterated prisoner’s dilemma, in Kendall G., Yao X. and Chong S. (eds.) The iterated prisoner’s dilemma: 20 years on. World Scientific, chapter 8, pp. 171-204, 2007.
.. [Stewart2012] Stewart, a. J., & Plotkin, J. B. (2012). Extortion and cooperation in the Prisoner’s Dilemma. Proceedings of the National Academy of Sciences, 109(26), 10134–10135. http://doi.org/10.1073/pnas.1208087109
.. [Szabó1992] Szabó, G., & Fáth, G. (2007). Evolutionary games on graphs. Physics Reports, 446(4-6), 97–216. http://doi.org/10.1016/j.physrep.2007.04.004
Expand Down

0 comments on commit 5332637

Please sign in to comment.