devstone.py 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  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. def intTransition(self):
  17. if self.int_delay:
  18. pystones(self.int_delay)
  19. return "passive"
  20. def timeAdvance(self):
  21. if self.state == "active":
  22. return self.prep_time
  23. else:
  24. return INFINITY
  25. def outputFnc(self):
  26. if hasattr(self, "o_out"):
  27. return {self.o_out: [0]}
  28. return {}
  29. def extTransition(self, inputs):
  30. if self.ext_delay:
  31. pystones(self.ext_delay)
  32. return "active"
  33. class DelayedAtomicStats(DelayedAtomic):
  34. def __init__(self, name: str, int_delay: float, ext_delay: float, add_out_port: bool = False, prep_time=0):
  35. super().__init__(name, int_delay, ext_delay, add_out_port, prep_time)
  36. self.int_count = 0
  37. self.ext_count = 0
  38. def intTransition(self):
  39. self.int_count += 1
  40. return super().intTransition()
  41. def extTransition(self, inputs):
  42. self.ext_count += 1
  43. return super().extTransition(inputs)
  44. class DEVStoneWrapper(CoupledDEVS, ABC):
  45. def __init__(self, name: str, depth: int, width: int, int_delay: float, ext_delay: float,
  46. add_atomic_out_ports: bool = False, prep_time=0, stats=False):
  47. super().__init__(name)
  48. self.depth = depth
  49. self.width = width
  50. self.int_delay = int_delay
  51. self.ext_delay = ext_delay
  52. self.prep_time = prep_time
  53. self.stats = stats
  54. self.add_atomic_out_ports = add_atomic_out_ports
  55. self.i_in = self.addInPort("i_in")
  56. self.o_out = self.addOutPort("o_out")
  57. if depth < 1:
  58. raise ValueError("Invalid depth")
  59. if width < 1:
  60. raise ValueError("Invalid width")
  61. if int_delay < 0:
  62. raise ValueError("Invalid int_delay")
  63. if ext_delay < 0:
  64. raise ValueError("Invalid ext_delay")
  65. if depth == 1:
  66. if self.stats:
  67. atomic = DelayedAtomicStats("Atomic_0_0", int_delay, ext_delay, add_out_port=True, prep_time=prep_time)
  68. else:
  69. atomic = DelayedAtomic("Atomic_0_0", int_delay, ext_delay, add_out_port=True, prep_time=prep_time)
  70. self.addSubModel(atomic)
  71. self.connectPorts(self.i_in, atomic.i_in)
  72. self.connectPorts(atomic.o_out, self.o_out)
  73. else:
  74. coupled = self.gen_coupled()
  75. self.addSubModel(coupled)
  76. self.connectPorts(self.i_in, coupled.i_in)
  77. self.connectPorts(coupled.o_out, self.o_out)
  78. for idx in range(width - 1):
  79. if self.stats:
  80. atomic = DelayedAtomicStats("Atomic_%d_%d" % (depth - 1, idx), int_delay, ext_delay,
  81. add_out_port=add_atomic_out_ports, prep_time=prep_time)
  82. else:
  83. atomic = DelayedAtomic("Atomic_%d_%d" % (depth - 1, idx), int_delay, ext_delay,
  84. add_out_port=add_atomic_out_ports, prep_time=prep_time)
  85. self.addSubModel(atomic)
  86. @abstractmethod
  87. def gen_coupled(self):
  88. """ :return a coupled method with i_in and o_out ports"""
  89. pass
  90. class LI(DEVStoneWrapper):
  91. def __init__(self, name: str, depth: int, width: int, int_delay: float, ext_delay: float, prep_time=0, stats=False):
  92. super().__init__(name, depth, width, int_delay, ext_delay, add_atomic_out_ports=False, prep_time=prep_time,
  93. stats=stats)
  94. for idx in range(1, len(self.component_set)):
  95. assert isinstance(self.component_set[idx], AtomicDEVS)
  96. self.connectPorts(self.i_in, self.component_set[idx].i_in)
  97. def gen_coupled(self):
  98. return LI("Coupled_%d" % (self.depth - 1), self.depth - 1, self.width, self.int_delay, self.ext_delay,
  99. prep_time=self.prep_time, stats=self.stats)
  100. class HI(DEVStoneWrapper):
  101. def __init__(self, name: str, depth: int, width: int, int_delay: float, ext_delay: float, prep_time=0, stats=False):
  102. super().__init__(name, depth, width, int_delay, ext_delay, add_atomic_out_ports=True, prep_time=prep_time,
  103. stats=stats)
  104. if len(self.component_set) > 1:
  105. assert isinstance(self.component_set[-1], AtomicDEVS)
  106. self.connectPorts(self.i_in, self.component_set[-1].i_in)
  107. for idx in range(1, len(self.component_set) - 1):
  108. assert isinstance(self.component_set[idx], AtomicDEVS)
  109. self.connectPorts(self.component_set[idx].o_out, self.component_set[idx + 1].i_in)
  110. self.connectPorts(self.i_in, self.component_set[idx].i_in)
  111. def gen_coupled(self):
  112. return HI("Coupled_%d" % (self.depth - 1), self.depth - 1, self.width, self.int_delay, self.ext_delay,
  113. prep_time=self.prep_time, stats=self.stats)
  114. class HO(DEVStoneWrapper):
  115. def __init__(self, name: str, depth: int, width: int, int_delay: float, ext_delay: float, prep_time=0, stats=False):
  116. super().__init__(name, depth, width, int_delay, ext_delay, add_atomic_out_ports=True, prep_time=prep_time,
  117. stats=stats)
  118. self.i_in2 = self.addInPort("i_in2")
  119. self.o_out2 = self.addOutPort("o_out2")
  120. assert len(self.component_set) > 0
  121. if isinstance(self.component_set[0], CoupledDEVS):
  122. self.connectPorts(self.i_in, self.component_set[0].i_in2)
  123. if len(self.component_set) > 1:
  124. assert isinstance(self.component_set[-1], AtomicDEVS)
  125. self.connectPorts(self.i_in2, self.component_set[-1].i_in)
  126. self.connectPorts(self.component_set[-1].o_out, self.o_out2)
  127. for idx in range(1, len(self.component_set) - 1):
  128. assert isinstance(self.component_set[idx], AtomicDEVS)
  129. self.connectPorts(self.component_set[idx].o_out, self.component_set[idx + 1].i_in)
  130. self.connectPorts(self.i_in2, self.component_set[idx].i_in)
  131. self.connectPorts(self.component_set[idx].o_out, self.o_out2)
  132. def gen_coupled(self):
  133. return HO("Coupled_%d" % (self.depth - 1), self.depth - 1, self.width, self.int_delay, self.ext_delay,
  134. prep_time=self.prep_time, stats=self.stats)
  135. class HOmod(CoupledDEVS):
  136. def __init__(self, name: str, depth: int, width: int, int_delay: float, ext_delay: float):
  137. super().__init__(name)
  138. self.depth = depth
  139. self.width = width
  140. self.int_delay = int_delay
  141. self.ext_delay = ext_delay
  142. self.i_in = self.addInPort("i_in")
  143. self.i_in2 = self.addInPort("i_in2")
  144. self.o_out = self.addOutPort("o_out")
  145. if depth < 1:
  146. raise ValueError("Invalid depth")
  147. if width < 1:
  148. raise ValueError("Invalid width")
  149. if int_delay < 0:
  150. raise ValueError("Invalid int_delay")
  151. if ext_delay < 0:
  152. raise ValueError("Invalid ext_delay")
  153. if depth == 1:
  154. atomic = DelayedAtomic("Atomic_0_0", int_delay, ext_delay, add_out_port=True)
  155. self.addSubModel(atomic)
  156. self.connectPorts(self.i_in, atomic.i_in)
  157. self.connectPorts(atomic.o_out, self.o_out)
  158. else:
  159. coupled = HOmod("Coupled_%d" % (self.depth - 1), self.depth - 1, self.width, self.int_delay, self.ext_delay)
  160. self.addSubModel(coupled)
  161. self.connectPorts(self.i_in, coupled.i_in)
  162. self.connectPorts(coupled.o_out, self.o_out)
  163. if width >= 2:
  164. atomics = defaultdict(list)
  165. # Generate atomic components
  166. for i in range(width):
  167. min_row_idx = 0 if i < 2 else i - 1
  168. for j in range(min_row_idx, width - 1):
  169. atomic = DelayedAtomic("Atomic_%d_%d_%d" % (depth - 1, i, j), int_delay, ext_delay,
  170. add_out_port=True)
  171. self.addSubModel(atomic)
  172. atomics[i].append(atomic)
  173. # Connect EIC
  174. for atomic in atomics[0]:
  175. self.connectPorts(self.i_in2, atomic.i_in)
  176. for i in range(1, width):
  177. atomic_set = atomics[i]
  178. self.connectPorts(self.i_in2, atomic_set[0].i_in)
  179. # Connect IC
  180. for atomic in atomics[0]: # First row to coupled component
  181. self.connectPorts(atomic.o_out, coupled.i_in2)
  182. for i in range(len(atomics[1])): # Second to first rows
  183. self.connectPorts(atomics[1][i].o_out, atomics[0][i].i_in)
  184. for i in range(2, width): # Rest of rows
  185. for j in range(len(atomics[i])):
  186. self.connectPorts(atomics[i][j].o_out, atomics[i - 1][j + 1].i_in)
  187. if __name__ == '__main__':
  188. import sys
  189. sys.setrecursionlimit(10000)
  190. root = HOmod("Root", 4, 3, 0, 0)
  191. sim = Simulator(root)
  192. sim.setVerbose(None)
  193. # sim.setTerminationTime(10.0)
  194. sim.setStateSaving("custom")
  195. sim.simulate()