devstone.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
  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. from graphviz import Source
  8. import time
  9. class DelayedAtomic(AtomicDEVS):
  10. def __init__(self, name: str, int_delay: float, ext_delay: float, add_out_port: bool = False, prep_time=0.0, mode: str = ""):
  11. super().__init__(name)
  12. self.int_delay = int_delay
  13. self.ext_delay = ext_delay
  14. self.prep_time = prep_time
  15. self.i_in = self.addInPort("i_in")
  16. if add_out_port: # for HI and HO models
  17. self.o_out = self.addOutPort("o_out")
  18. # Dynamic Structure extras
  19. self.mode = mode
  20. self.out_ports = [] # used for dynamic structure model LI2HI
  21. def intTransition(self):
  22. if self.int_delay:
  23. pystones(self.int_delay)
  24. return "passive"
  25. def timeAdvance(self):
  26. if self.state == "active":
  27. return self.prep_time
  28. else:
  29. return INFINITY
  30. def outputFnc(self):
  31. if hasattr(self, "o_out") and self.o_out is not None:
  32. return {self.o_out: [0]}
  33. # for DSDEVS LI2HI
  34. if len(self.out_ports) == 1 and self.out_ports[0].outline != []:
  35. # print(f"{self.name}: should be last call!!")
  36. # print("Send to ", self.out_ports[0].outline)
  37. return {self.out_ports[0]: [0]}
  38. return {}
  39. def extTransition(self, inputs):
  40. if self.ext_delay:
  41. pystones(self.ext_delay)
  42. return "active"
  43. def modelTransition(self, state):
  44. result = False
  45. if self.mode == "LI2HI":
  46. result = self.LI2HI(state)
  47. elif self.mode == "HI2LI":
  48. result = self.HI2LI(state)
  49. return result
  50. def LI2HI(self, state):
  51. if len(self.out_ports) == 0 and not hasattr(self, "o_out"):
  52. # every automic dev gets the extra out port
  53. # even though the last atomic could miss it see fig
  54. # except the atomic model in the coupled model at max depth
  55. # which has a output port by default
  56. state["LI2HI"] = True
  57. state["name"] = self.name
  58. self.out_ports.append(self.addOutPort("o_HI")) # standard LI model doesn't have by default
  59. # print(f"{self.name}: should be first call!!")
  60. return True # send to the parent to connect ports
  61. return False
  62. def HI2LI(self, state):
  63. if hasattr(self, "o_out") and self.o_out is not None:
  64. state["HI2LI"] = True
  65. state["name"] = self.name
  66. self.removePort(self.o_out)
  67. self.o_out = None
  68. return False
  69. class DelayedAtomicStats(DelayedAtomic):
  70. def __init__(self, name: str, int_delay: float, ext_delay: float, add_out_port: bool = False, prep_time=0, mode:str = ""):
  71. super().__init__(name, int_delay, ext_delay, add_out_port, prep_time, mode)
  72. self.int_count = 0
  73. self.ext_count = 0
  74. def intTransition(self):
  75. self.int_count += 1
  76. return super().intTransition()
  77. def extTransition(self, inputs):
  78. self.ext_count += 1
  79. return super().extTransition(inputs)
  80. class DEVStoneWrapper(CoupledDEVS, ABC):
  81. def __init__(self, name: str, depth: int, width: int, int_delay: float, ext_delay: float,
  82. add_atomic_out_ports: bool = False, prep_time=0, stats=False, mode: str = "", dynamic: bool = False):
  83. super().__init__(name)
  84. self.depth = depth
  85. self.width = width
  86. self.int_delay = int_delay
  87. self.ext_delay = ext_delay
  88. self.prep_time = prep_time
  89. self.stats = stats
  90. self.add_atomic_out_ports = add_atomic_out_ports
  91. self.i_in = self.addInPort("i_in")
  92. self.o_out = self.addOutPort("o_out")
  93. self.models = [] # stores all models part of the coupled model
  94. # added to deal with DS DEVS which don't keep it all in the component_set
  95. if depth < 1:
  96. raise ValueError("Invalid depth")
  97. if width < 1:
  98. raise ValueError("Invalid width")
  99. if int_delay < 0:
  100. raise ValueError("Invalid int_delay")
  101. if ext_delay < 0:
  102. raise ValueError("Invalid ext_delay")
  103. if depth == 1:
  104. if self.stats:
  105. atomic = DelayedAtomicStats("Atomic_0_0", int_delay, ext_delay, add_out_port=True, prep_time=1.0, mode=mode)
  106. else:
  107. atomic = DelayedAtomic("Atomic_0_0", int_delay, ext_delay, add_out_port=True, prep_time=1.0, mode=mode)
  108. self.models.append(self.addSubModel(atomic))
  109. self.connectPorts(self.i_in, atomic.i_in)
  110. self.connectPorts(atomic.o_out, self.o_out)
  111. else:
  112. coupled = self.gen_coupled()
  113. self.addSubModel(coupled)
  114. self.connectPorts(self.i_in, coupled.i_in)
  115. self.connectPorts(coupled.o_out, self.o_out)
  116. if dynamic:
  117. return # width will be dynamically generated
  118. # + (idx * 1e-6) to address too many transitions at the same time
  119. # => problem for Classic DEVS only it seems
  120. for idx in range(width - 1):
  121. if self.stats:
  122. atomic = DelayedAtomicStats("Atomic_%d_%d" % (depth - 1, idx), int_delay, ext_delay,
  123. add_out_port=add_atomic_out_ports, prep_time=1.0 + (idx * 1e-6), mode=mode)
  124. else:
  125. atomic = DelayedAtomic("Atomic_%d_%d" % (depth - 1, idx), int_delay, ext_delay,
  126. add_out_port=add_atomic_out_ports, prep_time=1.0 + (idx * 1e-6), mode=mode)
  127. self.models.append(self.addSubModel(atomic))
  128. def dot(self, name) -> None:
  129. result = ("\n\ndigraph {\n\tlayout=dot;"
  130. "\n\tnodesep=0.25;"
  131. "\n\tranksep=0.2;"
  132. "\n\trankdir=LR;"
  133. "\n\tsplines=ortho;\n")
  134. result += f"\tCoupled_{self.depth}[shape=box, label=\"C_{self.depth}\"];\n"
  135. coupled = self.component_set[0]
  136. result += f"\t{coupled.name}[shape=box, label=\"{'C_' + '_'.join(coupled.name.split('_')[1:3])}\"];\n"
  137. for model in self.models:
  138. result += f"\t{model.name}[shape=ellipse, label=\"{'A_' + '_'.join(model.name.split('_')[1:3])}\"];\n"
  139. for connections in self.i_in.outline:
  140. result += f"\tCoupled_{self.depth}:i_in -> {str(connections).split('.')[-2]}:{connections.name};\n"
  141. for model in self.models:
  142. if not hasattr(model, "o_out"):
  143. continue
  144. for connections in model.o_out.outline:
  145. result += f"\t{model.name}:o_out -> {str(connections).split('.')[2]}:{connections.name};\n"
  146. result += "}\n"
  147. src = Source(result)
  148. src.view(filename=f'{name}_{self.depth}')
  149. @abstractmethod
  150. def gen_coupled(self):
  151. """ :return a coupled method with i_in and o_out ports"""
  152. pass
  153. class LI(DEVStoneWrapper):
  154. def __init__(self, name: str, depth: int, width: int, int_delay: float, ext_delay: float, prep_time=0, stats=False):
  155. super().__init__(name, depth, width, int_delay, ext_delay, add_atomic_out_ports=False, prep_time=prep_time,
  156. stats=stats)
  157. for idx in range(1, len(self.component_set)):
  158. assert isinstance(self.component_set[idx], AtomicDEVS)
  159. self.connectPorts(self.i_in, self.component_set[idx].i_in)
  160. def gen_coupled(self):
  161. return LI("Coupled_%d" % (self.depth - 1), self.depth - 1, self.width, self.int_delay, self.ext_delay,
  162. prep_time=self.prep_time, stats=self.stats)
  163. class HI(DEVStoneWrapper):
  164. def __init__(self, name: str, depth: int, width: int, int_delay: float, ext_delay: float, prep_time=0, stats=False):
  165. super().__init__(name, depth, width, int_delay, ext_delay, add_atomic_out_ports=True, prep_time=prep_time,
  166. stats=stats)
  167. if len(self.component_set) > 1:
  168. assert isinstance(self.component_set[-1], AtomicDEVS)
  169. self.connectPorts(self.i_in, self.component_set[-1].i_in)
  170. for idx in range(1, len(self.component_set) - 1):
  171. assert isinstance(self.component_set[idx], AtomicDEVS)
  172. self.connectPorts(self.component_set[idx].o_out, self.component_set[idx + 1].i_in)
  173. self.connectPorts(self.i_in, self.component_set[idx].i_in)
  174. def gen_coupled(self):
  175. return HI("Coupled_%d" % (self.depth - 1), self.depth - 1, self.width, self.int_delay, self.ext_delay,
  176. prep_time=self.prep_time, stats=self.stats)
  177. class HO(DEVStoneWrapper):
  178. def __init__(self, name: str, depth: int, width: int, int_delay: float, ext_delay: float, prep_time=0, stats=False):
  179. super().__init__(name, depth, width, int_delay, ext_delay, add_atomic_out_ports=True, prep_time=prep_time,
  180. stats=stats)
  181. self.i_in2 = self.addInPort("i_in2")
  182. self.o_out2 = self.addOutPort("o_out2")
  183. assert len(self.component_set) > 0
  184. if isinstance(self.component_set[0], CoupledDEVS):
  185. self.connectPorts(self.i_in, self.component_set[0].i_in2)
  186. if len(self.component_set) > 1:
  187. assert isinstance(self.component_set[-1], AtomicDEVS)
  188. self.connectPorts(self.i_in2, self.component_set[-1].i_in)
  189. self.connectPorts(self.component_set[-1].o_out, self.o_out2)
  190. for idx in range(1, len(self.component_set) - 1):
  191. assert isinstance(self.component_set[idx], AtomicDEVS)
  192. self.connectPorts(self.component_set[idx].o_out, self.component_set[idx + 1].i_in)
  193. self.connectPorts(self.i_in2, self.component_set[idx].i_in)
  194. self.connectPorts(self.component_set[idx].o_out, self.o_out2)
  195. def gen_coupled(self):
  196. return HO("Coupled_%d" % (self.depth - 1), self.depth - 1, self.width, self.int_delay, self.ext_delay,
  197. prep_time=self.prep_time, stats=self.stats)
  198. class HOmod(CoupledDEVS):
  199. def __init__(self, name: str, depth: int, width: int, int_delay: float, ext_delay: float, prep_time=0, stats=False):
  200. super().__init__(name)
  201. self.depth = depth
  202. self.width = width
  203. self.int_delay = int_delay
  204. self.ext_delay = ext_delay
  205. self.prep_time = prep_time
  206. self.stats = stats
  207. self.i_in = self.addInPort("i_in")
  208. self.i_in2 = self.addInPort("i_in2")
  209. self.o_out = self.addOutPort("o_out")
  210. if depth < 1:
  211. raise ValueError("Invalid depth")
  212. if width < 1:
  213. raise ValueError("Invalid width")
  214. if int_delay < 0:
  215. raise ValueError("Invalid int_delay")
  216. if ext_delay < 0:
  217. raise ValueError("Invalid ext_delay")
  218. if depth == 1:
  219. if self.stats:
  220. atomic = DelayedAtomicStats("Atomic_0_0", int_delay, ext_delay, add_out_port=True, prep_time=prep_time)
  221. else:
  222. atomic = DelayedAtomic("Atomic_0_0", int_delay, ext_delay, add_out_port=True, prep_time=prep_time)
  223. self.addSubModel(atomic)
  224. self.connectPorts(self.i_in, atomic.i_in)
  225. self.connectPorts(atomic.o_out, self.o_out)
  226. else:
  227. coupled = HOmod("Coupled_%d" % (self.depth - 1), self.depth - 1, self.width, self.int_delay, self.ext_delay, prep_time=prep_time, stats=stats)
  228. self.addSubModel(coupled)
  229. self.connectPorts(self.i_in, coupled.i_in)
  230. self.connectPorts(coupled.o_out, self.o_out)
  231. if width >= 2:
  232. atomics = defaultdict(list)
  233. # Generate atomic components
  234. for i in range(width):
  235. min_row_idx = 0 if i < 2 else i - 1
  236. for j in range(min_row_idx, width - 1):
  237. if self.stats:
  238. atomic = DelayedAtomicStats("Atomic_%d_%d_%d" % (depth - 1, i, j), int_delay, ext_delay,
  239. add_out_port=True, prep_time=prep_time)
  240. else:
  241. atomic = DelayedAtomic("Atomic_%d_%d_%d" % (depth - 1, i, j), int_delay, ext_delay,
  242. add_out_port=True, prep_time=prep_time)
  243. self.addSubModel(atomic)
  244. atomics[i].append(atomic)
  245. # Connect EIC
  246. for atomic in atomics[0]:
  247. self.connectPorts(self.i_in2, atomic.i_in)
  248. for i in range(1, width):
  249. atomic_set = atomics[i]
  250. self.connectPorts(self.i_in2, atomic_set[0].i_in)
  251. # Connect IC
  252. for atomic in atomics[0]: # First row to coupled component
  253. self.connectPorts(atomic.o_out, coupled.i_in2)
  254. for i in range(len(atomics[1])): # Second to first rows
  255. for j in range(len(atomics[0])):
  256. self.connectPorts(atomics[1][i].o_out, atomics[0][j].i_in)
  257. for i in range(2, width): # Rest of rows
  258. for j in range(len(atomics[i])):
  259. self.connectPorts(atomics[i][j].o_out, atomics[i - 1][j + 1].i_in)
  260. class LI2HI(DEVStoneWrapper):
  261. """
  262. Dynamic DEVStone variant that starts as an LI
  263. and (gradually??) transforms into an HI model
  264. """
  265. def __init__(self, name: str, depth: int, width: int, int_delay: float, ext_delay: float, prep_time=0, stats=False):
  266. super().__init__(name, depth, width, int_delay, ext_delay, add_atomic_out_ports=False, prep_time=prep_time,
  267. stats=stats, mode="LI2HI")
  268. for idx in range(1, len(self.component_set)):
  269. assert isinstance(self.component_set[idx], AtomicDEVS)
  270. self.connectPorts(self.i_in, self.component_set[idx].i_in)
  271. def gen_coupled(self):
  272. return LI2HI("Coupled_%d" % (self.depth - 1), self.depth - 1, self.width, self.int_delay, self.ext_delay,
  273. prep_time=self.prep_time, stats=self.stats)
  274. def modelTransition(self, state):
  275. if state.get("LI2HI", True):
  276. for i in range(len(self.component_set) - 1): # -1 because the last model can't be connected
  277. # basically checks if it is an atomic model and if it got the extra output port
  278. if hasattr(self.component_set[i], "out_ports") and len(self.component_set[i].out_ports) == 1:
  279. # print(f"Connect {self.component_set[i].name} to {self.component_set[i+1].name}")
  280. self.connectPorts(self.component_set[i].out_ports[0], self.component_set[i + 1].i_in)
  281. return False
  282. return False
  283. class HI2LI(DEVStoneWrapper):
  284. """
  285. Dynamic DEVStone variant that starts as an HI
  286. and (gradually??) transforms into an LI model
  287. """
  288. def __init__(self, name: str, depth: int, width: int, int_delay: float, ext_delay: float, prep_time=0, stats=False):
  289. super().__init__(name, depth, width, int_delay, ext_delay, add_atomic_out_ports=True, prep_time=prep_time,
  290. stats=stats, mode="HI2LI")
  291. if len(self.component_set) > 1:
  292. assert isinstance(self.component_set[-1], AtomicDEVS)
  293. self.connectPorts(self.i_in, self.component_set[-1].i_in)
  294. for idx in range(1, len(self.component_set) - 1):
  295. assert isinstance(self.component_set[idx], AtomicDEVS)
  296. self.connectPorts(self.component_set[idx].o_out, self.component_set[idx + 1].i_in)
  297. self.connectPorts(self.i_in, self.component_set[idx].i_in)
  298. def gen_coupled(self):
  299. return HI2LI("Coupled_%d" % (self.depth - 1), self.depth - 1, self.width, self.int_delay, self.ext_delay,
  300. prep_time=self.prep_time, stats=self.stats)
  301. TRANSITION_TIME = 0
  302. class DynamicGenerator(AtomicDEVS):
  303. def __init__(self, name: str, period: float = 1, repeat: int = 1):
  304. super().__init__(name)
  305. self.period = period
  306. self.repeat = repeat
  307. self._counter = 0
  308. def intTransition(self):
  309. return "passive" # state is unimportant
  310. def timeAdvance(self):
  311. if self._counter >= self.repeat:
  312. return INFINITY
  313. return self.period
  314. def outputFnc(self):
  315. return {}
  316. def modelTransition(self, state):
  317. # every time TA elapses, this will be called on the atomic:
  318. # set a flag the parent coupled can read
  319. # increment counter and request modelTransition (return True)
  320. global TRANSITION_TIME
  321. start = time.time()
  322. if self._counter >= self.repeat:
  323. TRANSITION_TIME += time.time() - start
  324. return False
  325. self._counter += 1
  326. state["generate"] = True
  327. state["created"] = 0
  328. TRANSITION_TIME += time.time() - start
  329. return True
  330. class dLI(DEVStoneWrapper):
  331. """
  332. A LI model which grows in width
  333. """
  334. def __init__(self, name: str, depth: int, width: int, int_delay: float, ext_delay: float, generator_mode: str = "uniform", root=True, prep_time=0, stats=False):
  335. super().__init__(name, depth, width, int_delay, ext_delay, add_atomic_out_ports=False, prep_time=prep_time,
  336. stats=stats, mode="LI-Dynamic", dynamic=True)
  337. self.root = root
  338. for idx in range(1, len(self.component_set)):
  339. assert isinstance(self.component_set[idx], AtomicDEVS)
  340. self.connectPorts(self.i_in, self.component_set[idx].i_in)
  341. self.created = 0
  342. self.current_width = 1
  343. self.max_gen = width - 1 # dynamically generate atomic components until width is reached excludes the coupled model
  344. if self.depth == 1:
  345. self.generator = self.addSubModel(DynamicGenerator(self.name + "_gen", repeat=self.max_gen))
  346. def gen_coupled(self):
  347. return dLI("Coupled_%d" % (self.depth - 1), self.depth - 1, self.width, self.int_delay, self.ext_delay,
  348. prep_time=self.prep_time, stats=self.stats, root=False)
  349. def modelTransition(self, state):
  350. if state.get("generate", True) and self.depth == 1:
  351. return True
  352. if state.get("generate", True) and self.depth > 1:
  353. name = "Atomic_%d_%d" % (self.depth - 1, self.current_width) if self.depth > 1 else "Atomic_0_%d" % self.current_width
  354. if self.stats:
  355. new_atom = DelayedAtomicStats(name, self.int_delay, self.ext_delay,
  356. add_out_port=self.add_atomic_out_ports, prep_time=self.prep_time, mode="LI-Dynamic")
  357. else:
  358. new_atom = DelayedAtomic(name, self.int_delay, self.ext_delay,
  359. add_out_port=self.add_atomic_out_ports, prep_time=self.prep_time, mode="LI-Dynamic")
  360. self.models.append(self.addSubModel(new_atom))
  361. self.connectPorts(self.i_in, new_atom.i_in)
  362. self.current_width += 1
  363. if self.current_width > self.width:
  364. raise RuntimeError(f"The width has grown beyond what was specified!"
  365. f"\n current width: {self.current_width}"
  366. f"\n specified width: {self.width}")
  367. # enables visualisation of each level (coupled model)
  368. # if self.current_width == self.width:
  369. # self.dot(f"dLI_{self.depth}")
  370. if not self.root:
  371. state["created"] += 1
  372. return True
  373. self.created += state["created"] + 1
  374. # print(f"Created {self.created} atomic models")
  375. return False
  376. return False
  377. class dHI(DEVStoneWrapper):
  378. """
  379. A HI model which grows in width
  380. """
  381. def __init__(self, name: str, depth: int, width: int, int_delay: float, ext_delay: float, generator_mode: str = "uniform", root=True, prep_time=0, stats=False):
  382. super().__init__(name, depth, width, int_delay, ext_delay, add_atomic_out_ports=True, prep_time=prep_time,
  383. stats=stats, mode="HI-Dynamic", dynamic=True)
  384. self.root = root
  385. if len(self.component_set) > 1:
  386. assert isinstance(self.component_set[-1], AtomicDEVS)
  387. self.connectPorts(self.i_in, self.component_set[-1].i_in)
  388. for idx in range(1, len(self.component_set) - 1):
  389. assert isinstance(self.component_set[idx], AtomicDEVS)
  390. self.connectPorts(self.component_set[idx].o_out, self.component_set[idx + 1].i_in)
  391. self.connectPorts(self.i_in, self.component_set[idx].i_in)
  392. self.created = 0
  393. self.current_width = 1
  394. self.max_gen = width - 1 # dynamically generate atomic components until width is reached excludes the coupled model
  395. if self.depth == 1:
  396. self.generator = self.addSubModel(DynamicGenerator(self.name + "_gen", repeat=self.max_gen))
  397. def gen_coupled(self):
  398. return dHI("Coupled_%d" % (self.depth - 1), self.depth - 1, self.width, self.int_delay, self.ext_delay,
  399. prep_time=self.prep_time, stats=self.stats, root=False)
  400. def modelTransition(self, state):
  401. start = time.time()
  402. global TRANSITION_TIME
  403. if state.get("generate", True) and self.depth == 1:
  404. TRANSITION_TIME += time.time() - start
  405. return True
  406. if state.get("generate", True) and self.depth > 1:
  407. name = "Atomic_%d_%d" % (self.depth - 1, self.current_width) if self.depth > 1 else "Atomic_0_%d" % self.current_width
  408. if self.stats:
  409. new_atom = DelayedAtomicStats(name, self.int_delay, self.ext_delay,
  410. add_out_port=self.add_atomic_out_ports, prep_time=self.prep_time, mode="LI-Dynamic")
  411. else:
  412. new_atom = DelayedAtomic(name, self.int_delay, self.ext_delay,
  413. add_out_port=self.add_atomic_out_ports, prep_time=self.prep_time, mode="LI-Dynamic")
  414. self.models.append(self.addSubModel(new_atom))
  415. self.connectPorts(self.i_in, new_atom.i_in)
  416. if len(self.models) > 1:
  417. prev = self.models[-2]
  418. if isinstance(prev, AtomicDEVS) and hasattr(prev, "o_out"):
  419. self.connectPorts(prev.o_out, new_atom.i_in)
  420. self.current_width += 1
  421. if self.current_width > self.width:
  422. TRANSITION_TIME += time.time() - start
  423. raise RuntimeError(f"The width has grown beyond what was specified!"
  424. f"\n current width: {self.current_width}"
  425. f"\n specified width: {self.width}")
  426. # enables visualisation of each level (coupled model)
  427. # if self.current_width == self.width:
  428. # self.dot(f"dLI_{self.depth}")
  429. if not self.root:
  430. state["created"] += 1
  431. TRANSITION_TIME += time.time() - start
  432. return True
  433. self.created += state["created"] + 1
  434. # print(f"Created {self.created} atomic models")
  435. TRANSITION_TIME += time.time() - start
  436. # print(f"TRANSITION TIME: {TRANSITION_TIME}")
  437. return False
  438. return False
  439. if __name__ == '__main__':
  440. import sys
  441. sys.setrecursionlimit(10000)
  442. root = HOmod("Root", 4, 3, 0, 0)
  443. sim = Simulator(root)
  444. sim.setVerbose(None)
  445. # sim.setTerminationTime(10.0)
  446. sim.setStateSaving("custom")
  447. sim.simulate()