graph.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. import numpy as np
  2. from verticle import verticle
  3. import matplotlib.pyplot as plt
  4. from matplotlib.patches import FancyArrowPatch
  5. from functools import singledispatch
  6. class Graph:
  7. '''
  8. Graph by matrix
  9. '''
  10. __verticles = []
  11. __edges = []
  12. matrix = []
  13. def __init__(self, verticles, matrix):
  14. self.verticles = verticles
  15. self.matrix = matrix
  16. @singledispatch
  17. def build(structs) -> "Graph":
  18. raise Exception("Конструктор не работает")
  19. @build.register
  20. @staticmethod
  21. def build_verticles(verticles: dict):
  22. matrix = []
  23. for vert in verticles:
  24. to = verticles[vert]
  25. matrix.append( [1 if x in to else 0 for x in verticles] )
  26. matrix = np.array(matrix)
  27. return Graph(verticles, matrix)
  28. @build.register
  29. @staticmethod
  30. def build_matrix(matrix: np.ndarray):
  31. return Graph([str(i) for i in range(matrix.shape[0])], matrix)
  32. @build.register
  33. @staticmethod
  34. def build_matrix(matrix: list):
  35. return Graph([str(i) for i in range(len(matrix))], np.array(matrix))
  36. def __eq__(self, __o: object) -> bool:
  37. return np.array_equal(self.matrix, __o.matrix)
  38. def __and__(self, __o: "Graph"):
  39. result = self.copy()
  40. result.matrix = (self.matrix+__o.matrix==2).astype(int)
  41. return result
  42. def __or__(self, __o: "Graph"):
  43. result = self.copy()
  44. result.matrix = (self.matrix+__o.matrix>0).astype(int)
  45. return result
  46. def __sub__(self, __o: "Graph"):
  47. result = self.copy()
  48. result.matrix = (self.matrix-__o.matrix>0).astype(int)
  49. return result
  50. def __xor__(self, __o: "Graph"):
  51. result = self.copy()
  52. result.matrix = (self.matrix!=__o.matrix).astype(int)
  53. return result
  54. def __str__(self):
  55. result = " "+"".join([str(node) for node in self.verticles])+"\n"
  56. for i,row in enumerate(self.matrix):
  57. result += str(self.verticles[i]) + " " + "".join(map(str,row))+"\n"
  58. return result
  59. def __invert__(self):
  60. result = self.copy()
  61. result.matrix = (self.matrix==0).astype(int)
  62. return result
  63. def set(self, indexes: list[int]):
  64. '''
  65. Меняет вершины по заданым индексам.
  66. '''
  67. if len(indexes) != len(self.verticles):
  68. raise IndexError(f"indexes must be len of {len(self.verticles)}")
  69. new_matrix = self.matrix[indexes]
  70. self.matrix = new_matrix[:, indexes]
  71. self.__verticles = [self.verticles[x] for x in indexes]
  72. def replace(self, fro:str|verticle, to:str|verticle):
  73. '''
  74. Меняет 2 переданные вершины графа местами.
  75. '''
  76. index_fro = self.verticles.index(str(fro))
  77. index_to = self.verticles.index(str(to))
  78. indexes = [index_to if i==index_fro else index_fro if i==index_to else i for i in range(len(self.verticles))]
  79. self.set(indexes)
  80. def copy(self):
  81. return Graph(self.dict)
  82. def plot(self, x=0, y=0):
  83. '''
  84. x,y - смещение построения графа, если координаты вершин не заданы
  85. '''
  86. fig, ax = plt.subplots()
  87. if not isinstance(self.verticles[0], verticle):
  88. print("Перепись " + "="*32)
  89. space = np.linspace(0, 2*np.pi, len(self.verticles)+1)[:-1]
  90. self.__verticles = [verticle(name, np.cos(sp)+x, np.sin(sp)+y) for name, sp in zip(self.verticles, space)]
  91. print([vert.name for vert in self.verticles])
  92. print([vert.x for vert in self.verticles])
  93. print([vert.y for vert in self.verticles])
  94. for i,row in enumerate(self.matrix):
  95. for j,elem in enumerate(row):
  96. if elem == 0: continue
  97. arrow = FancyArrowPatch((self.verticles[i].x, self.verticles[i].y),
  98. (self.verticles[j].x, self.verticles[j].y),
  99. connectionstyle="arc3,rad=.05", arrowstyle='->', mutation_scale=30)
  100. ax.add_patch(arrow)
  101. va = 'bottom' if j >= i else 'top'
  102. aligment = 'left' if j >= i else 'right'
  103. ax.annotate(str(elem), (.5, .5), xycoords=arrow, ha='center',
  104. va=va, horizontalalignment=aligment, size=20)
  105. x = []
  106. y = []
  107. for v in self.verticles:
  108. x.append(v.x)
  109. y.append(v.y)
  110. v.plot()
  111. plt.scatter(x,y, c='red', zorder=1)
  112. @property
  113. def dict(self):
  114. review = {}
  115. if isinstance(self.__verticles[0],verticle): n_vert = [verticle(vert.name, vert.x, vert.y) for vert in self.verticles]
  116. else: n_vert = self.__verticles
  117. for vert, row in zip(n_vert, self.matrix):
  118. if isinstance(vert,verticle):
  119. review[vert] = [n_vert[i] for i,sym in enumerate(row) if sym]
  120. else:
  121. review[vert] = ''.join([self.__verticles[i] for i,sym in enumerate(row) if sym])
  122. return review
  123. @property
  124. def verticles(self):
  125. return self.__verticles
  126. @verticles.setter
  127. def verticles(self, verts):
  128. self.__verticles = verts
  129. if __name__ == "__main__":
  130. # g = Graph({'a':'ad','b':'ad','c':'bc','d':'bc', 'e':'ac', 'f':'bd'})
  131. # h = Graph({'a':'bd','b':'bc','c':'bc','d':'ac'})
  132. # print("graph G1:",g,sep="\n")
  133. # print("graph H1:",h,sep="\n")
  134. # print("graph G1 and H1:",g&h,sep="\n")
  135. # print("graph G1 or H1:",g|h,sep="\n")
  136. # print("graph G1 / H1:",g-h,sep="\n")
  137. # print("graph G1 xor H1:",g^h,sep="\n")
  138. # print("Invert G1:",~g)
  139. # g.plot(1, 1)
  140. # h.plot(3.2, 1)
  141. g = Graph.build([[0, 1, 0, 1, 1],
  142. [1, 0, 0, 0, 1],
  143. [1, 0, 0, 0, 0],
  144. [1, 0, 1, 0, 0],
  145. [1, 0, 1, 0, 0]])
  146. g.plot()
  147. print(g)
  148. plt.show()