devstone.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. from abc import ABC, abstractmethod
  2. from collections import defaultdict
  3. from pystone import pystones
  4. from pypdevs.minimal 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, prep_time=0, stats=False):
  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.prep_time = prep_time
  143. self.stats = stats
  144. self.i_in = self.addInPort("i_in")
  145. self.i_in2 = self.addInPort("i_in2")
  146. self.o_out = self.addOutPort("o_out")
  147. if depth < 1:
  148. raise ValueError("Invalid depth")
  149. if width < 1:
  150. raise ValueError("Invalid width")
  151. if int_delay < 0:
  152. raise ValueError("Invalid int_delay")
  153. if ext_delay < 0:
  154. raise ValueError("Invalid ext_delay")
  155. if depth == 1:
  156. if self.stats:
  157. atomic = DelayedAtomicStats("Atomic_0_0", int_delay, ext_delay, add_out_port=True, prep_time=prep_time)
  158. else:
  159. atomic = DelayedAtomic("Atomic_0_0", int_delay, ext_delay, add_out_port=True, prep_time=prep_time)
  160. self.addSubModel(atomic)
  161. self.connectPorts(self.i_in, atomic.i_in)
  162. self.connectPorts(atomic.o_out, self.o_out)
  163. else:
  164. coupled = HOmod("Coupled_%d" % (self.depth - 1), self.depth - 1, self.width, self.int_delay, self.ext_delay, prep_time=prep_time, stats=stats)
  165. self.addSubModel(coupled)
  166. self.connectPorts(self.i_in, coupled.i_in)
  167. self.connectPorts(coupled.o_out, self.o_out)
  168. if width >= 2:
  169. atomics = defaultdict(list)
  170. # Generate atomic components
  171. for i in range(width):
  172. min_row_idx = 0 if i < 2 else i - 1
  173. for j in range(min_row_idx, width - 1):
  174. if self.stats:
  175. atomic = DelayedAtomicStats("Atomic_%d_%d_%d" % (depth - 1, i, j), int_delay, ext_delay,
  176. add_out_port=True, prep_time=prep_time)
  177. else:
  178. atomic = DelayedAtomic("Atomic_%d_%d_%d" % (depth - 1, i, j), int_delay, ext_delay,
  179. add_out_port=True, prep_time=prep_time)
  180. self.addSubModel(atomic)
  181. atomics[i].append(atomic)
  182. # Connect EIC
  183. for atomic in atomics[0]:
  184. self.connectPorts(self.i_in2, atomic.i_in)
  185. for i in range(1, width):
  186. atomic_set = atomics[i]
  187. self.connectPorts(self.i_in2, atomic_set[0].i_in)
  188. # Connect IC
  189. for atomic in atomics[0]: # First row to coupled component
  190. self.connectPorts(atomic.o_out, coupled.i_in2)
  191. for i in range(len(atomics[1])): # Second to first rows
  192. for j in range(len(atomics[0])):
  193. self.connectPorts(atomics[1][i].o_out, atomics[0][j].i_in)
  194. for i in range(2, width): # Rest of rows
  195. for j in range(len(atomics[i])):
  196. self.connectPorts(atomics[i][j].o_out, atomics[i - 1][j + 1].i_in)
  197. if __name__ == '__main__':
  198. import sys
  199. sys.setrecursionlimit(10000)
  200. root = HOmod("Root", 4, 3, 0, 0)
  201. sim = Simulator(root)
  202. sim.setVerbose(None)
  203. # sim.setTerminationTime(10.0)
  204. sim.setStateSaving("custom")
  205. sim.simulate()