devstone.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. from abc import ABC, abstractmethod
  2. from collections import defaultdict
  3. from pystone import pystones
  4. from pypdevs.DEVS import AtomicDEVS, CoupledDEVS
  5. from pypdevs.infinity import INFINITY
  6. from pypdevs.simulator import Simulator
  7. class DelayedAtomic(AtomicDEVS):
  8. def __init__(self, name: str, int_delay: float, ext_delay: float, add_out_port: bool = False, prep_time=0):
  9. super().__init__(name)
  10. self.int_delay = int_delay
  11. self.ext_delay = ext_delay
  12. self.prep_time = prep_time
  13. self.i_in = self.addInPort("i_in")
  14. if add_out_port:
  15. self.o_out = self.addOutPort("o_out")
  16. # used for dynamic structure models
  17. self.in_ports = []
  18. self.out_ports = []
  19. def intTransition(self):
  20. if self.int_delay:
  21. pystones(self.int_delay)
  22. return "passive"
  23. def timeAdvance(self):
  24. if self.state == "active":
  25. return self.prep_time
  26. else:
  27. return INFINITY
  28. def outputFnc(self):
  29. if hasattr(self, "o_out"):
  30. return {self.o_out: [0]}
  31. return {}
  32. def extTransition(self, inputs):
  33. if self.ext_delay:
  34. pystones(self.ext_delay)
  35. return "active"
  36. def modelTransition(self, state):
  37. if state["LI2HI"]:
  38. if self.name.split("_")[2] != "1":
  39. self.in_ports.append(self.addInPort("i_HI"))
  40. if self.name.split("_")[2] != "2":
  41. self.out_ports.append(self.addOutPort("o_HI"))
  42. return True # send to the parent to connect ports
  43. else:
  44. return False
  45. class DelayedAtomicStats(DelayedAtomic):
  46. def __init__(self, name: str, int_delay: float, ext_delay: float, add_out_port: bool = False, prep_time=0):
  47. super().__init__(name, int_delay, ext_delay, add_out_port, prep_time)
  48. self.int_count = 0
  49. self.ext_count = 0
  50. def intTransition(self):
  51. self.int_count += 1
  52. return super().intTransition()
  53. def extTransition(self, inputs):
  54. self.ext_count += 1
  55. return super().extTransition(inputs)
  56. class DEVStoneWrapper(CoupledDEVS, ABC):
  57. def __init__(self, name: str, depth: int, width: int, int_delay: float, ext_delay: float,
  58. add_atomic_out_ports: bool = False, prep_time=0, stats=False):
  59. super().__init__(name)
  60. self.depth = depth
  61. self.width = width
  62. self.int_delay = int_delay
  63. self.ext_delay = ext_delay
  64. self.prep_time = prep_time
  65. self.stats = stats
  66. self.add_atomic_out_ports = add_atomic_out_ports
  67. self.i_in = self.addInPort("i_in")
  68. self.o_out = self.addOutPort("o_out")
  69. if depth < 1:
  70. raise ValueError("Invalid depth")
  71. if width < 1:
  72. raise ValueError("Invalid width")
  73. if int_delay < 0:
  74. raise ValueError("Invalid int_delay")
  75. if ext_delay < 0:
  76. raise ValueError("Invalid ext_delay")
  77. if depth == 1:
  78. if self.stats:
  79. atomic = DelayedAtomicStats("Atomic_0_0", int_delay, ext_delay, add_out_port=True, prep_time=prep_time)
  80. else:
  81. atomic = DelayedAtomic("Atomic_0_0", int_delay, ext_delay, add_out_port=True, prep_time=prep_time)
  82. self.addSubModel(atomic)
  83. self.connectPorts(self.i_in, atomic.i_in)
  84. self.connectPorts(atomic.o_out, self.o_out)
  85. else:
  86. coupled = self.gen_coupled()
  87. self.addSubModel(coupled)
  88. self.connectPorts(self.i_in, coupled.i_in)
  89. self.connectPorts(coupled.o_out, self.o_out)
  90. for idx in range(width - 1):
  91. if self.stats:
  92. atomic = DelayedAtomicStats("Atomic_%d_%d" % (depth - 1, idx), int_delay, ext_delay,
  93. add_out_port=add_atomic_out_ports, prep_time=prep_time)
  94. else:
  95. atomic = DelayedAtomic("Atomic_%d_%d" % (depth - 1, idx), int_delay, ext_delay,
  96. add_out_port=add_atomic_out_ports, prep_time=prep_time)
  97. self.addSubModel(atomic)
  98. @abstractmethod
  99. def gen_coupled(self):
  100. """ :return a coupled method with i_in and o_out ports"""
  101. pass
  102. class LI(DEVStoneWrapper):
  103. def __init__(self, name: str, depth: int, width: int, int_delay: float, ext_delay: float, prep_time=0, stats=False):
  104. super().__init__(name, depth, width, int_delay, ext_delay, add_atomic_out_ports=False, prep_time=prep_time,
  105. stats=stats)
  106. for idx in range(1, len(self.component_set)):
  107. assert isinstance(self.component_set[idx], AtomicDEVS)
  108. self.connectPorts(self.i_in, self.component_set[idx].i_in)
  109. def gen_coupled(self):
  110. return LI("Coupled_%d" % (self.depth - 1), self.depth - 1, self.width, self.int_delay, self.ext_delay,
  111. prep_time=self.prep_time, stats=self.stats)
  112. class HI(DEVStoneWrapper):
  113. def __init__(self, name: str, depth: int, width: int, int_delay: float, ext_delay: float, prep_time=0, stats=False):
  114. super().__init__(name, depth, width, int_delay, ext_delay, add_atomic_out_ports=True, prep_time=prep_time,
  115. stats=stats)
  116. if len(self.component_set) > 1:
  117. assert isinstance(self.component_set[-1], AtomicDEVS)
  118. self.connectPorts(self.i_in, self.component_set[-1].i_in)
  119. for idx in range(1, len(self.component_set) - 1):
  120. assert isinstance(self.component_set[idx], AtomicDEVS)
  121. self.connectPorts(self.component_set[idx].o_out, self.component_set[idx + 1].i_in)
  122. self.connectPorts(self.i_in, self.component_set[idx].i_in)
  123. def gen_coupled(self):
  124. return HI("Coupled_%d" % (self.depth - 1), self.depth - 1, self.width, self.int_delay, self.ext_delay,
  125. prep_time=self.prep_time, stats=self.stats)
  126. class HO(DEVStoneWrapper):
  127. def __init__(self, name: str, depth: int, width: int, int_delay: float, ext_delay: float, prep_time=0, stats=False):
  128. super().__init__(name, depth, width, int_delay, ext_delay, add_atomic_out_ports=True, prep_time=prep_time,
  129. stats=stats)
  130. self.i_in2 = self.addInPort("i_in2")
  131. self.o_out2 = self.addOutPort("o_out2")
  132. assert len(self.component_set) > 0
  133. if isinstance(self.component_set[0], CoupledDEVS):
  134. self.connectPorts(self.i_in, self.component_set[0].i_in2)
  135. if len(self.component_set) > 1:
  136. assert isinstance(self.component_set[-1], AtomicDEVS)
  137. self.connectPorts(self.i_in2, self.component_set[-1].i_in)
  138. self.connectPorts(self.component_set[-1].o_out, self.o_out2)
  139. for idx in range(1, len(self.component_set) - 1):
  140. assert isinstance(self.component_set[idx], AtomicDEVS)
  141. self.connectPorts(self.component_set[idx].o_out, self.component_set[idx + 1].i_in)
  142. self.connectPorts(self.i_in2, self.component_set[idx].i_in)
  143. self.connectPorts(self.component_set[idx].o_out, self.o_out2)
  144. def gen_coupled(self):
  145. return HO("Coupled_%d" % (self.depth - 1), self.depth - 1, self.width, self.int_delay, self.ext_delay,
  146. prep_time=self.prep_time, stats=self.stats)
  147. class HOmod(CoupledDEVS):
  148. def __init__(self, name: str, depth: int, width: int, int_delay: float, ext_delay: float, prep_time=0, stats=False):
  149. super().__init__(name)
  150. self.depth = depth
  151. self.width = width
  152. self.int_delay = int_delay
  153. self.ext_delay = ext_delay
  154. self.prep_time = prep_time
  155. self.stats = stats
  156. self.i_in = self.addInPort("i_in")
  157. self.i_in2 = self.addInPort("i_in2")
  158. self.o_out = self.addOutPort("o_out")
  159. if depth < 1:
  160. raise ValueError("Invalid depth")
  161. if width < 1:
  162. raise ValueError("Invalid width")
  163. if int_delay < 0:
  164. raise ValueError("Invalid int_delay")
  165. if ext_delay < 0:
  166. raise ValueError("Invalid ext_delay")
  167. if depth == 1:
  168. if self.stats:
  169. atomic = DelayedAtomicStats("Atomic_0_0", int_delay, ext_delay, add_out_port=True, prep_time=prep_time)
  170. else:
  171. atomic = DelayedAtomic("Atomic_0_0", int_delay, ext_delay, add_out_port=True, prep_time=prep_time)
  172. self.addSubModel(atomic)
  173. self.connectPorts(self.i_in, atomic.i_in)
  174. self.connectPorts(atomic.o_out, self.o_out)
  175. else:
  176. coupled = HOmod("Coupled_%d" % (self.depth - 1), self.depth - 1, self.width, self.int_delay, self.ext_delay, prep_time=prep_time, stats=stats)
  177. self.addSubModel(coupled)
  178. self.connectPorts(self.i_in, coupled.i_in)
  179. self.connectPorts(coupled.o_out, self.o_out)
  180. if width >= 2:
  181. atomics = defaultdict(list)
  182. # Generate atomic components
  183. for i in range(width):
  184. min_row_idx = 0 if i < 2 else i - 1
  185. for j in range(min_row_idx, width - 1):
  186. if self.stats:
  187. atomic = DelayedAtomicStats("Atomic_%d_%d_%d" % (depth - 1, i, j), int_delay, ext_delay,
  188. add_out_port=True, prep_time=prep_time)
  189. else:
  190. atomic = DelayedAtomic("Atomic_%d_%d_%d" % (depth - 1, i, j), int_delay, ext_delay,
  191. add_out_port=True, prep_time=prep_time)
  192. self.addSubModel(atomic)
  193. atomics[i].append(atomic)
  194. # Connect EIC
  195. for atomic in atomics[0]:
  196. self.connectPorts(self.i_in2, atomic.i_in)
  197. for i in range(1, width):
  198. atomic_set = atomics[i]
  199. self.connectPorts(self.i_in2, atomic_set[0].i_in)
  200. # Connect IC
  201. for atomic in atomics[0]: # First row to coupled component
  202. self.connectPorts(atomic.o_out, coupled.i_in2)
  203. for i in range(len(atomics[1])): # Second to first rows
  204. for j in range(len(atomics[0])):
  205. self.connectPorts(atomics[1][i].o_out, atomics[0][j].i_in)
  206. for i in range(2, width): # Rest of rows
  207. for j in range(len(atomics[i])):
  208. self.connectPorts(atomics[i][j].o_out, atomics[i - 1][j + 1].i_in)
  209. class LI2HI(DEVStoneWrapper):
  210. """
  211. Dynamic DEVStone variant that starts as an LI (loosely interconnected)
  212. and gradually transforms into an HI (highly interconnected) model
  213. by adding connections during internal transitions.
  214. """
  215. def __init__(self, name: str, depth: int, width: int, int_delay: float, ext_delay: float,
  216. prep_time=0, stats=False):
  217. super().__init__(name, depth, width, int_delay, ext_delay,
  218. add_atomic_out_ports=True, prep_time=prep_time, stats=stats)
  219. # transformation state tracking
  220. self.converted_connections = 0
  221. self.total_connections = max(0, len(self.component_set) - 2)
  222. # initially LI-like connections
  223. for idx in range(1, len(self.component_set)):
  224. assert isinstance(self.component_set[idx], AtomicDEVS)
  225. self.connectPorts(self.i_in, self.component_set[idx].i_in)
  226. # output connection
  227. if len(self.component_set) > 1:
  228. self.connectPorts(self.component_set[-1].o_out, self.o_out)
  229. def gen_coupled(self):
  230. return LI2HI("Coupled_%d" % (self.depth - 1),
  231. self.depth - 1, self.width, self.int_delay, self.ext_delay,
  232. prep_time=self.prep_time, stats=self.stats)
  233. def modelTransition(self, state):
  234. """
  235. Gradually adds HI-style interconnections between atomics.
  236. Called automatically during internal transitions.
  237. """
  238. print("CHANGE")
  239. # Each step adds one connection
  240. if self.converted_connections < self.total_connections:
  241. src_idx = self.converted_connections + 1
  242. if src_idx < len(self.component_set) - 1:
  243. src = self.component_set[src_idx]
  244. dst = self.component_set[src_idx + 1]
  245. self.connectPorts(src.o_out, dst.i_in)
  246. self.converted_connections += 1
  247. return False
  248. if __name__ == '__main__':
  249. import sys
  250. sys.setrecursionlimit(10000)
  251. root = HOmod("Root", 4, 3, 0, 0)
  252. sim = Simulator(root)
  253. sim.setVerbose(None)
  254. # sim.setTerminationTime(10.0)
  255. sim.setStateSaving("custom")
  256. sim.simulate()