pystate.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. from typing import Any, List, Tuple, Optional
  2. from state.base import State, Node, Edge, Element
  3. class PyState(State):
  4. """
  5. State interface implemented using Python data structures.
  6. This code is based on:
  7. https://msdl.uantwerpen.be/git/yentl/modelverse/src/master/state/modelverse_state/main.py
  8. """
  9. def __init__(self):
  10. self.edges = {}
  11. self.outgoing = {}
  12. self.incoming = {}
  13. self.values = {}
  14. self.nodes = set()
  15. # Set used for garbage collection
  16. self.GC = True
  17. self.to_delete = set()
  18. self.cache = {}
  19. self.cache_node = {}
  20. self.cache_all = {}
  21. self.root = self.create_node()
  22. def create_node(self) -> Node:
  23. new_id = self.new_id()
  24. self.nodes.add(new_id)
  25. return new_id
  26. def create_edge(self, source: Element, target: Element) -> Optional[Edge]:
  27. # TODO: why does this call SILENTLY fail if source/target does not exist ???????????
  28. if source not in self.edges and source not in self.nodes:
  29. return None
  30. elif target not in self.edges and target not in self.nodes:
  31. return None
  32. else:
  33. new_id = self.new_id()
  34. self.outgoing.setdefault(source, set()).add(new_id)
  35. self.incoming.setdefault(target, set()).add(new_id)
  36. self.edges[new_id] = (source, target)
  37. if source in self.edges:
  38. # We are creating something dict_readable
  39. # Fill in the cache already!
  40. dict_source, dict_target = self.edges[source]
  41. if target in self.values:
  42. self.cache.setdefault(dict_source, {})[self.values[target]] = source
  43. self.cache_all.setdefault(dict_source, {}).setdefault(self.values[target], set()).add(source)
  44. self.cache_node.setdefault(dict_source, {})[target] = source
  45. return new_id
  46. def create_nodevalue(self, value: Any) -> Optional[Node]:
  47. if not self.is_valid_datavalue(value):
  48. return None
  49. new_id = self.new_id()
  50. self.values[new_id] = value
  51. self.nodes.add(new_id)
  52. return new_id
  53. def create_dict(self, source: Element, value: Any, target: Element) -> None:
  54. if source not in self.nodes and source not in self.edges:
  55. return None
  56. elif target not in self.nodes and target not in self.edges:
  57. return None
  58. elif not self.is_valid_datavalue(value):
  59. return None
  60. else:
  61. n = self.create_nodevalue(value)
  62. e = self.create_edge(source, target)
  63. assert n != None and e != None
  64. e2 = self.create_edge(e, n)
  65. self.cache.setdefault(source, {})[value] = e
  66. self.cache_all.setdefault(source, {}).setdefault(value, set()).add(e)
  67. self.cache_node.setdefault(source, {})[n] = e
  68. def read_root(self) -> Node:
  69. return self.root
  70. def read_value(self, node: Node) -> Any:
  71. if node in self.values:
  72. return self.values[node]
  73. else:
  74. return None
  75. def read_outgoing(self, elem: Element) -> Optional[List[Edge]]:
  76. if elem in self.edges or elem in self.nodes:
  77. if elem in self.outgoing:
  78. return list(self.outgoing[elem])
  79. else:
  80. return []
  81. else:
  82. return None
  83. def read_incoming(self, elem: Element) -> Optional[List[Edge]]:
  84. if elem in self.edges or elem in self.nodes:
  85. if elem in self.incoming:
  86. return list(self.incoming[elem])
  87. else:
  88. return []
  89. else:
  90. return None
  91. def read_edge(self, edge: Edge) -> Tuple[Optional[Element], Optional[Element]]:
  92. if edge in self.edges:
  93. return self.edges[edge][0], self.edges[edge][1]
  94. else:
  95. return None, None
  96. def is_edge(self, elem: Element) -> bool:
  97. return elem in self.edges
  98. def read_dict(self, elem: Element, value: Any) -> Optional[Element]:
  99. e = self.read_dict_edge(elem, value)
  100. if e == None:
  101. return None
  102. else:
  103. return self.edges[e][1]
  104. def read_dict_keys(self, elem: Element) -> Optional[List[Element]]:
  105. if elem not in self.nodes and elem not in self.edges:
  106. return None
  107. result = []
  108. # NOTE: cannot just use the cache here, as some keys in the cache might not actually exist;
  109. # we would have to check all of them anyway
  110. if elem in self.outgoing:
  111. for e1 in self.outgoing[elem]:
  112. if e1 in self.outgoing:
  113. for e2 in self.outgoing[e1]:
  114. result.append(self.edges[e2][1])
  115. return result
  116. def read_dict_edge(self, elem: Element, value: Any) -> Optional[Edge]:
  117. try:
  118. first = self.cache[elem][value]
  119. # Got hit, so validate
  120. if (self.edges[first][0] == elem) and (value in [self.values[self.edges[i][1]]
  121. for i in self.outgoing[first]
  122. if self.edges[i][1] in self.values]):
  123. return first
  124. # Hit but invalid now
  125. del self.cache[elem][value]
  126. self.cache_all[elem][value].remove(first)
  127. return None
  128. except KeyError:
  129. return None
  130. def read_dict_edge_all(self, elem: Element, value: Any) -> List[Edge]:
  131. result = []
  132. try:
  133. all_ = self.cache_all[elem][value]
  134. for a in all_:
  135. try:
  136. if ((self.edges[a][0] == elem) and (value in [self.values[self.edges[i][1]]
  137. for i in self.outgoing[a]
  138. if self.edges[i][1] in self.values])):
  139. result.append(a)
  140. continue
  141. except KeyError:
  142. pass
  143. if len(result) != len(all_):
  144. self.cache_all[elem][value] = set(result)
  145. except KeyError:
  146. pass
  147. return result
  148. def read_dict_node(self, elem: Element, value_node: Node) -> Optional[Element]:
  149. e = self.read_dict_node_edge(elem, value_node)
  150. if e == None:
  151. return None
  152. else:
  153. self.cache_node.setdefault(elem, {})[value_node] = e
  154. return self.edges[e][1]
  155. def read_dict_node_edge(self, elem: Element, value_node: Node) -> Optional[Edge]:
  156. try:
  157. first = self.cache_node[elem][value_node]
  158. # Got hit, so validate
  159. if (self.edges[first][0] == elem) and \
  160. (value_node in [self.edges[i][1] for i in self.outgoing[first]]):
  161. return first
  162. # Hit but invalid now
  163. del self.cache_node[elem][value_node]
  164. return None
  165. except KeyError:
  166. return None
  167. def read_reverse_dict(self, elem: Element, value: Any) -> Optional[List[Element]]:
  168. if elem not in self.nodes and elem not in self.edges:
  169. return None
  170. # Get all outgoing links
  171. matches = []
  172. if elem in self.incoming:
  173. for e1 in self.incoming[elem]:
  174. # For each link, we read the links that might link to a data value
  175. if e1 in self.outgoing:
  176. for e2 in self.outgoing[e1]:
  177. # Now read out the target of the link
  178. target = self.edges[e2][1]
  179. # And access its value
  180. if target in self.values and self.values[target] == value:
  181. # Found a match
  182. matches.append(e1)
  183. return [self.edges[e][0] for e in matches]
  184. def delete_node(self, node: Node) -> None:
  185. if node == self.root:
  186. return
  187. elif node not in self.nodes:
  188. return
  189. self.nodes.remove(node)
  190. if node in self.values:
  191. del self.values[node]
  192. s = set()
  193. if node in self.outgoing:
  194. for e in self.outgoing[node]:
  195. s.add(e)
  196. del self.outgoing[node]
  197. if node in self.incoming:
  198. for e in self.incoming[node]:
  199. s.add(e)
  200. del self.incoming[node]
  201. for e in s:
  202. self.delete_edge(e)
  203. if node in self.outgoing:
  204. del self.outgoing[node]
  205. if node in self.incoming:
  206. del self.incoming[node]
  207. def delete_edge(self, edge: Edge) -> None:
  208. if edge not in self.edges:
  209. return
  210. s, t = self.edges[edge]
  211. if t in self.incoming:
  212. self.incoming[t].remove(edge)
  213. if s in self.outgoing:
  214. self.outgoing[s].remove(edge)
  215. del self.edges[edge]
  216. s = set()
  217. if edge in self.outgoing:
  218. for e in self.outgoing[edge]:
  219. s.add(e)
  220. if edge in self.incoming:
  221. for e in self.incoming[edge]:
  222. s.add(e)
  223. for e in s:
  224. self.delete_edge(e)
  225. if edge in self.outgoing:
  226. del self.outgoing[edge]
  227. if edge in self.incoming:
  228. del self.incoming[edge]
  229. if self.GC and (t in self.incoming and not self.incoming[t]) and (t not in self.edges):
  230. # Remove this node as well
  231. # Edges aren't deleted like this, as they might have a reachable target and source!
  232. # If they haven't, they will be removed because the source was removed.
  233. self.to_delete.add(t)
  234. def purge(self):
  235. while self.to_delete:
  236. t = self.to_delete.pop()
  237. if t in self.incoming and not self.incoming[t]:
  238. self.delete_node(t)
  239. values = set(self.edges)
  240. values.update(self.nodes)
  241. visit_list = [self.root]
  242. while visit_list:
  243. elem = visit_list.pop()
  244. if elem in values:
  245. # Remove it from the leftover values
  246. values.remove(elem)
  247. if elem in self.edges:
  248. visit_list.extend(self.edges[elem])
  249. if elem in self.outgoing:
  250. visit_list.extend(self.outgoing[elem])
  251. if elem in self.incoming:
  252. visit_list.extend(self.incoming[elem])
  253. dset = set()
  254. for key in self.cache:
  255. if key not in self.nodes and key not in self.edges:
  256. dset.add(key)
  257. for key in dset:
  258. del self.cache[key]
  259. del self.cache_all[key]
  260. dset = set()
  261. for key in self.cache_node:
  262. if key not in self.nodes and key not in self.edges:
  263. dset.add(key)
  264. for key in dset:
  265. del self.cache_node[key]
  266. # All remaining elements are to be purged
  267. if len(values) > 0:
  268. while values:
  269. v = values.pop()
  270. if v in self.nodes:
  271. self.delete_node(v)