test_structuralholes.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. """Unit tests for the :mod:`networkx.algorithms.structuralholes` module."""
  2. import math
  3. import pytest
  4. import networkx as nx
  5. from networkx.classes.tests import dispatch_interface
  6. class TestStructuralHoles:
  7. """Unit tests for computing measures of structural holes.
  8. The expected values for these functions were originally computed using the
  9. proprietary software `UCINET`_ and the free software `IGraph`_ , and then
  10. computed by hand to make sure that the results are correct.
  11. .. _UCINET: https://sites.google.com/site/ucinetsoftware/home
  12. .. _IGraph: http://igraph.org/
  13. """
  14. def setup_method(self):
  15. self.D = nx.DiGraph()
  16. self.D.add_edges_from([(0, 1), (0, 2), (1, 0), (2, 1)])
  17. self.D_weights = {(0, 1): 2, (0, 2): 2, (1, 0): 1, (2, 1): 1}
  18. # Example from http://www.analytictech.com/connections/v20(1)/holes.htm
  19. self.G = nx.Graph()
  20. self.G.add_edges_from(
  21. [
  22. ("A", "B"),
  23. ("A", "F"),
  24. ("A", "G"),
  25. ("A", "E"),
  26. ("E", "G"),
  27. ("F", "G"),
  28. ("B", "G"),
  29. ("B", "D"),
  30. ("D", "G"),
  31. ("G", "C"),
  32. ]
  33. )
  34. self.G_weights = {
  35. ("A", "B"): 2,
  36. ("A", "F"): 3,
  37. ("A", "G"): 5,
  38. ("A", "E"): 2,
  39. ("E", "G"): 8,
  40. ("F", "G"): 3,
  41. ("B", "G"): 4,
  42. ("B", "D"): 1,
  43. ("D", "G"): 3,
  44. ("G", "C"): 10,
  45. }
  46. # This additionally tests the @nx._dispatch mechanism, treating
  47. # nx.mutual_weight as if it were a re-implementation from another package
  48. @pytest.mark.parametrize("wrapper", [lambda x: x, dispatch_interface.convert])
  49. def test_constraint_directed(self, wrapper):
  50. constraint = nx.constraint(wrapper(self.D))
  51. assert constraint[0] == pytest.approx(1.003, abs=1e-3)
  52. assert constraint[1] == pytest.approx(1.003, abs=1e-3)
  53. assert constraint[2] == pytest.approx(1.389, abs=1e-3)
  54. def test_effective_size_directed(self):
  55. effective_size = nx.effective_size(self.D)
  56. assert effective_size[0] == pytest.approx(1.167, abs=1e-3)
  57. assert effective_size[1] == pytest.approx(1.167, abs=1e-3)
  58. assert effective_size[2] == pytest.approx(1, abs=1e-3)
  59. def test_constraint_weighted_directed(self):
  60. D = self.D.copy()
  61. nx.set_edge_attributes(D, self.D_weights, "weight")
  62. constraint = nx.constraint(D, weight="weight")
  63. assert constraint[0] == pytest.approx(0.840, abs=1e-3)
  64. assert constraint[1] == pytest.approx(1.143, abs=1e-3)
  65. assert constraint[2] == pytest.approx(1.378, abs=1e-3)
  66. def test_effective_size_weighted_directed(self):
  67. D = self.D.copy()
  68. nx.set_edge_attributes(D, self.D_weights, "weight")
  69. effective_size = nx.effective_size(D, weight="weight")
  70. assert effective_size[0] == pytest.approx(1.567, abs=1e-3)
  71. assert effective_size[1] == pytest.approx(1.083, abs=1e-3)
  72. assert effective_size[2] == pytest.approx(1, abs=1e-3)
  73. def test_constraint_undirected(self):
  74. constraint = nx.constraint(self.G)
  75. assert constraint["G"] == pytest.approx(0.400, abs=1e-3)
  76. assert constraint["A"] == pytest.approx(0.595, abs=1e-3)
  77. assert constraint["C"] == pytest.approx(1, abs=1e-3)
  78. def test_effective_size_undirected_borgatti(self):
  79. effective_size = nx.effective_size(self.G)
  80. assert effective_size["G"] == pytest.approx(4.67, abs=1e-2)
  81. assert effective_size["A"] == pytest.approx(2.50, abs=1e-2)
  82. assert effective_size["C"] == pytest.approx(1, abs=1e-2)
  83. def test_effective_size_undirected(self):
  84. G = self.G.copy()
  85. nx.set_edge_attributes(G, 1, "weight")
  86. effective_size = nx.effective_size(G, weight="weight")
  87. assert effective_size["G"] == pytest.approx(4.67, abs=1e-2)
  88. assert effective_size["A"] == pytest.approx(2.50, abs=1e-2)
  89. assert effective_size["C"] == pytest.approx(1, abs=1e-2)
  90. def test_constraint_weighted_undirected(self):
  91. G = self.G.copy()
  92. nx.set_edge_attributes(G, self.G_weights, "weight")
  93. constraint = nx.constraint(G, weight="weight")
  94. assert constraint["G"] == pytest.approx(0.299, abs=1e-3)
  95. assert constraint["A"] == pytest.approx(0.795, abs=1e-3)
  96. assert constraint["C"] == pytest.approx(1, abs=1e-3)
  97. def test_effective_size_weighted_undirected(self):
  98. G = self.G.copy()
  99. nx.set_edge_attributes(G, self.G_weights, "weight")
  100. effective_size = nx.effective_size(G, weight="weight")
  101. assert effective_size["G"] == pytest.approx(5.47, abs=1e-2)
  102. assert effective_size["A"] == pytest.approx(2.47, abs=1e-2)
  103. assert effective_size["C"] == pytest.approx(1, abs=1e-2)
  104. def test_constraint_isolated(self):
  105. G = self.G.copy()
  106. G.add_node(1)
  107. constraint = nx.constraint(G)
  108. assert math.isnan(constraint[1])
  109. def test_effective_size_isolated(self):
  110. G = self.G.copy()
  111. G.add_node(1)
  112. nx.set_edge_attributes(G, self.G_weights, "weight")
  113. effective_size = nx.effective_size(G, weight="weight")
  114. assert math.isnan(effective_size[1])
  115. def test_effective_size_borgatti_isolated(self):
  116. G = self.G.copy()
  117. G.add_node(1)
  118. effective_size = nx.effective_size(G)
  119. assert math.isnan(effective_size[1])