|
|
@@ -43,8 +43,6 @@ class DelayedAtomic(AtomicDEVS):
|
|
|
|
|
|
# for DSDEVS LI2HI
|
|
|
if len(self.out_ports) == 1 and self.out_ports[0].outline != []:
|
|
|
- # print(f"{self.name}: should be last call!!")
|
|
|
- # print("Send to ", self.out_ports[0].outline)
|
|
|
return {self.out_ports[0]: [0]}
|
|
|
return {}
|
|
|
|
|
|
@@ -168,7 +166,6 @@ class DEVStoneWrapper(CoupledDEVS, ABC):
|
|
|
result += f"\tCoupled_{self.depth}[shape=box, label=\"C_{self.depth}\"];\n"
|
|
|
coupled = self.component_set[0]
|
|
|
result += f"\t{coupled.name}[shape=box, label=\"{'C_' + '_'.join(coupled.name.split('_')[1:3])}\"];\n"
|
|
|
-
|
|
|
for model in self.models:
|
|
|
result += f"\t{model.name}[shape=ellipse, label=\"{'A_' + '_'.join(model.name.split('_')[1:3])}\"];\n"
|
|
|
|
|
|
@@ -380,13 +377,13 @@ class HI2LI(DEVStoneWrapper):
|
|
|
return HI2LI("Coupled_%d" % (self.depth - 1), self.depth - 1, self.width, self.int_delay, self.ext_delay,
|
|
|
prep_time=self.prep_time, stats=self.stats)
|
|
|
|
|
|
-TRANSITION_TIME = 0
|
|
|
|
|
|
class DynamicGenerator(AtomicDEVS):
|
|
|
- def __init__(self, name: str, period: float = 1, repeat: int = 1):
|
|
|
+ def __init__(self, name: str, period: float = 1, repeat: int = 1, num: int = 1):
|
|
|
super().__init__(name)
|
|
|
self.period = period
|
|
|
self.repeat = repeat
|
|
|
+ self.number = num # how many models need to be generated per cycle
|
|
|
|
|
|
self._counter = 0
|
|
|
|
|
|
@@ -405,147 +402,124 @@ class DynamicGenerator(AtomicDEVS):
|
|
|
# every time TA elapses, this will be called on the atomic:
|
|
|
# set a flag the parent coupled can read
|
|
|
# increment counter and request modelTransition (return True)
|
|
|
- global TRANSITION_TIME
|
|
|
- start = time.time()
|
|
|
if self._counter >= self.repeat:
|
|
|
- TRANSITION_TIME += time.time() - start
|
|
|
return False
|
|
|
self._counter += 1
|
|
|
state["generate"] = True
|
|
|
+ state["create"] = self.number
|
|
|
state["created"] = 0
|
|
|
- TRANSITION_TIME += time.time() - start
|
|
|
return True
|
|
|
|
|
|
+class DynamicDEVStoneWrapper(DEVStoneWrapper, ABC):
|
|
|
+ def __init__(self, name: str, depth: int, width: int, int_delay: float, ext_delay: float,
|
|
|
+ add_atomic_out_ports: bool = False, /, prep_time=0, stats=False, root=False, mode: str = "", number: int = 1, gen_mode: str="uniform"):
|
|
|
+ if number <= 0:
|
|
|
+ raise ValueError("number of models to be generated must be greater than zero")
|
|
|
|
|
|
-class dLI(DEVStoneWrapper):
|
|
|
- """
|
|
|
- A LI model which grows in width
|
|
|
- """
|
|
|
- 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):
|
|
|
- super().__init__(name, depth, width, int_delay, ext_delay, add_atomic_out_ports=False, prep_time=prep_time,
|
|
|
- stats=stats, mode="LI-Dynamic", dynamic=True)
|
|
|
+ # makes it easier to reason about width should be divisible by the number provided
|
|
|
+ # but the width provided also includes a coupled model so the actual width to be generated
|
|
|
+ # would be width - 1, this mitigates that
|
|
|
+ if number > 1 and root:
|
|
|
+ width += 1
|
|
|
|
|
|
self.root = root
|
|
|
- for idx in range(1, len(self.component_set)):
|
|
|
- assert isinstance(self.component_set[idx], AtomicDEVS)
|
|
|
- self.connectPorts(self.i_in, self.component_set[idx].i_in)
|
|
|
-
|
|
|
- self.created = 0
|
|
|
+ self.mode = mode
|
|
|
+ self.created = 0 # keeps track of how many atomic models have been created
|
|
|
self.current_width = 1
|
|
|
- self.max_gen = width - 1 # dynamically generate atomic components until width is reached excludes the coupled model
|
|
|
+ self.number = number
|
|
|
+ self.max_gen = (width - 1) // self.number # dynamically generate atomic components until width is reached excludes the coupled model
|
|
|
+
|
|
|
+ super().__init__(name, depth, width, int_delay, ext_delay, add_atomic_out_ports, prep_time=prep_time,
|
|
|
+ stats=stats, mode=mode, dynamic=True)
|
|
|
if self.depth == 1:
|
|
|
- self.generator = self.addSubModel(DynamicGenerator(self.name + "_gen", repeat=self.max_gen))
|
|
|
+ self.generator = self.addSubModel(DynamicGenerator(self.name + "_gen", repeat=self.max_gen, num=self.number))
|
|
|
|
|
|
- def gen_coupled(self):
|
|
|
- return dLI("Coupled_%d" % (self.depth - 1), self.depth - 1, self.width, self.int_delay, self.ext_delay,
|
|
|
- prep_time=self.prep_time, stats=self.stats, root=False)
|
|
|
+ def createAtomic(self):
|
|
|
+ name = f"Atomic_0_{self.current_width}"
|
|
|
+ if self.depth > 1:
|
|
|
+ name = f"Atomic_{self.depth-1}_{self.current_width}"
|
|
|
+
|
|
|
+ if self.stats:
|
|
|
+ atomic = DelayedAtomicStats(name, self.int_delay, self.ext_delay,
|
|
|
+ add_out_port=self.add_atomic_out_ports, prep_time=self.prep_time,
|
|
|
+ mode=self.mode)
|
|
|
+ else:
|
|
|
+ atomic = DelayedAtomic(name, self.int_delay, self.ext_delay,
|
|
|
+ add_out_port=self.add_atomic_out_ports, prep_time=self.prep_time,
|
|
|
+ mode=self.mode)
|
|
|
+
|
|
|
+ self.models.append(self.addSubModel(atomic))
|
|
|
+ return atomic
|
|
|
|
|
|
def modelTransition(self, state):
|
|
|
if state.get("generate", True) and self.depth == 1:
|
|
|
return True
|
|
|
|
|
|
if state.get("generate", True) and self.depth > 1:
|
|
|
- name = "Atomic_%d_%d" % (self.depth - 1, self.current_width) if self.depth > 1 else "Atomic_0_%d" % self.current_width
|
|
|
- if self.stats:
|
|
|
- new_atom = DelayedAtomicStats(name, self.int_delay, self.ext_delay,
|
|
|
- add_out_port=self.add_atomic_out_ports, prep_time=self.prep_time, mode="LI-Dynamic")
|
|
|
- else:
|
|
|
- new_atom = DelayedAtomic(name, self.int_delay, self.ext_delay,
|
|
|
- add_out_port=self.add_atomic_out_ports, prep_time=self.prep_time, mode="LI-Dynamic")
|
|
|
-
|
|
|
- self.models.append(self.addSubModel(new_atom))
|
|
|
- self.connectPorts(self.i_in, new_atom.i_in)
|
|
|
-
|
|
|
- self.current_width += 1
|
|
|
- if self.current_width > self.width:
|
|
|
- raise RuntimeError(f"The width has grown beyond what was specified!"
|
|
|
+ number = state.get("create")
|
|
|
+ for _ in range(number):
|
|
|
+ atom = self.createAtomic()
|
|
|
+ self.connectPorts(self.i_in, atom.i_in)
|
|
|
+
|
|
|
+ if len(self.models) > 1:
|
|
|
+ prev = self.models[-2] # new model was already added so the previous one isn't the last in the list anymore
|
|
|
+ if isinstance(prev, AtomicDEVS) and hasattr(prev, "o_out"):
|
|
|
+ self.connectPorts(prev.o_out, atom.i_in)
|
|
|
+ self.current_width += 1
|
|
|
+
|
|
|
+ if self.current_width > self.width:
|
|
|
+ raise RuntimeError(f"The width has grown beyond what was specified!"
|
|
|
f"\n current width: {self.current_width}"
|
|
|
f"\n specified width: {self.width}")
|
|
|
|
|
|
# enables visualisation of each level (coupled model)
|
|
|
- # if self.current_width == self.width:
|
|
|
- # self.dot(f"dLI_{self.depth}")
|
|
|
+ # if self.current_width == self.width:
|
|
|
+ # self.dot(f"dLI_{self.depth}")
|
|
|
+
|
|
|
if not self.root:
|
|
|
- state["created"] += 1
|
|
|
+ state["created"] += number
|
|
|
return True
|
|
|
- self.created += state["created"] + 1
|
|
|
- # print(f"Created {self.created} atomic models")
|
|
|
+ self.created += state["created"] + number
|
|
|
return False
|
|
|
return False
|
|
|
|
|
|
|
|
|
-class dHI(DEVStoneWrapper):
|
|
|
+class dLI(DynamicDEVStoneWrapper):
|
|
|
+ """
|
|
|
+ A LI model which grows in width
|
|
|
+ """
|
|
|
+ def __init__(self, name: str, depth: int, width: int, int_delay: float, ext_delay: float, /, prep_time=0, stats=False, root=True, number: int = 1, gen_mode: str = "uniform"):
|
|
|
+ super().__init__(name, depth, width, int_delay, ext_delay, False, prep_time, stats, mode="LI-Dynamic", root=root, number=number, gen_mode=gen_mode)
|
|
|
+
|
|
|
+ for idx in range(1, len(self.models)):
|
|
|
+ assert isinstance(self.component_set[idx], AtomicDEVS)
|
|
|
+ self.connectPorts(self.i_in, self.component_set[idx].i_in)
|
|
|
+
|
|
|
+ def gen_coupled(self):
|
|
|
+ return dLI("Coupled_%d" % (self.depth - 1), self.depth - 1, self.width, self.int_delay, self.ext_delay,
|
|
|
+ prep_time=self.prep_time, stats=self.stats, root=False, number=self.number)
|
|
|
+
|
|
|
+
|
|
|
+class dHI(DynamicDEVStoneWrapper):
|
|
|
"""
|
|
|
A HI model which grows in width
|
|
|
"""
|
|
|
- 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):
|
|
|
- super().__init__(name, depth, width, int_delay, ext_delay, add_atomic_out_ports=True, prep_time=prep_time,
|
|
|
- stats=stats, mode="HI-Dynamic", dynamic=True)
|
|
|
+ def __init__(self, name: str, depth: int, width: int, int_delay: float, ext_delay: float, /, prep_time=0, stats=False, root=True, number: int = 1, gen_mode: str = "uniform"):
|
|
|
+ super().__init__(name, depth, width, int_delay, ext_delay, True, prep_time, stats, mode="HI-Dynamic", root=root, number=number, gen_mode=gen_mode)
|
|
|
|
|
|
- self.root = root
|
|
|
- if len(self.component_set) > 1:
|
|
|
+ if len(self.models) > 1:
|
|
|
assert isinstance(self.component_set[-1], AtomicDEVS)
|
|
|
self.connectPorts(self.i_in, self.component_set[-1].i_in)
|
|
|
|
|
|
- for idx in range(1, len(self.component_set) - 1):
|
|
|
+ for idx in range(1, len(self.models) - 1):
|
|
|
assert isinstance(self.component_set[idx], AtomicDEVS)
|
|
|
self.connectPorts(self.component_set[idx].o_out, self.component_set[idx + 1].i_in)
|
|
|
self.connectPorts(self.i_in, self.component_set[idx].i_in)
|
|
|
|
|
|
- self.created = 0
|
|
|
- self.current_width = 1
|
|
|
- self.max_gen = width - 1 # dynamically generate atomic components until width is reached excludes the coupled model
|
|
|
- if self.depth == 1:
|
|
|
- self.generator = self.addSubModel(DynamicGenerator(self.name + "_gen", repeat=self.max_gen))
|
|
|
|
|
|
def gen_coupled(self):
|
|
|
return dHI("Coupled_%d" % (self.depth - 1), self.depth - 1, self.width, self.int_delay, self.ext_delay,
|
|
|
- prep_time=self.prep_time, stats=self.stats, root=False)
|
|
|
-
|
|
|
- def modelTransition(self, state):
|
|
|
- start = time.time()
|
|
|
- global TRANSITION_TIME
|
|
|
- if state.get("generate", True) and self.depth == 1:
|
|
|
- TRANSITION_TIME += time.time() - start
|
|
|
- return True
|
|
|
-
|
|
|
- if state.get("generate", True) and self.depth > 1:
|
|
|
- name = "Atomic_%d_%d" % (self.depth - 1, self.current_width) if self.depth > 1 else "Atomic_0_%d" % self.current_width
|
|
|
- if self.stats:
|
|
|
- new_atom = DelayedAtomicStats(name, self.int_delay, self.ext_delay,
|
|
|
- add_out_port=self.add_atomic_out_ports, prep_time=self.prep_time, mode="LI-Dynamic")
|
|
|
- else:
|
|
|
- new_atom = DelayedAtomic(name, self.int_delay, self.ext_delay,
|
|
|
- add_out_port=self.add_atomic_out_ports, prep_time=self.prep_time, mode="LI-Dynamic")
|
|
|
-
|
|
|
- self.models.append(self.addSubModel(new_atom))
|
|
|
- self.connectPorts(self.i_in, new_atom.i_in)
|
|
|
- if len(self.models) > 1:
|
|
|
- prev = self.models[-2]
|
|
|
- if isinstance(prev, AtomicDEVS) and hasattr(prev, "o_out"):
|
|
|
- self.connectPorts(prev.o_out, new_atom.i_in)
|
|
|
- self.current_width += 1
|
|
|
- if self.current_width > self.width:
|
|
|
- TRANSITION_TIME += time.time() - start
|
|
|
- raise RuntimeError(f"The width has grown beyond what was specified!"
|
|
|
- f"\n current width: {self.current_width}"
|
|
|
- f"\n specified width: {self.width}")
|
|
|
-
|
|
|
- # enables visualisation of each level (coupled model)
|
|
|
- # if self.current_width == self.width:
|
|
|
- # self.dot(f"dLI_{self.depth}")
|
|
|
-
|
|
|
- if not self.root:
|
|
|
- state["created"] += 1
|
|
|
- TRANSITION_TIME += time.time() - start
|
|
|
- return True
|
|
|
- self.created += state["created"] + 1
|
|
|
- # print(f"Created {self.created} atomic models")
|
|
|
- TRANSITION_TIME += time.time() - start
|
|
|
- # print(f"TRANSITION TIME: {TRANSITION_TIME}")
|
|
|
- return False
|
|
|
- return False
|
|
|
-
|
|
|
+ prep_time=self.prep_time, stats=self.stats, root=False, number=self.number)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
import sys
|