Browse Source

Remove outliers from data when plotting. Devstone Models are now printable and dynamic LI and HI models now ignore the model at max depth

Snej69420 11 months ago
parent
commit
85fc113495
2 changed files with 47 additions and 19 deletions
  1. 14 8
      devstone/compare.py
  2. 33 11
      devstone/pythonpdevs/devstone.py

+ 14 - 8
devstone/compare.py

@@ -5,20 +5,26 @@ import pandas as pd
 import matplotlib.pyplot as plt
 from pathlib import Path
 
-def load_and_filter(csv_path, fixed_param, fixed_value):
-    """Load a CSV file and filter rows by the fixed parameter."""
-    df = pd.read_csv(csv_path)
-    df = df[df[fixed_param] == fixed_value]
-    return df
-
 def process_files(csv_files, fixed_param, fixed_value):
     """Combine and average simulation times by the varying parameter."""
     results = {}
     varying_param = "depth" if fixed_param == "width" else "width"
 
     for path in csv_files:
-        df = load_and_filter(path, fixed_param, fixed_value)
-        # Average simulation times for identical setups
+        # df = pd.read_csv(path)
+        # df = df[df[fixed_param] == fixed_value]
+        # grouped = df.groupby(varying_param, as_index=False)["simulation_time"].mean()
+        # results[path.parent.name + "-" + path.stem] = grouped
+
+        df = pd.read_csv(path)
+        df = df[df[fixed_param] == fixed_value]
+
+        Q1 = df["simulation_time"].quantile(0.25)
+        Q3 = df["simulation_time"].quantile(0.75)
+        IQR = Q3 - Q1
+        lower_bound = Q1 - 1.5 * IQR
+        upper_bound = Q3 + 1.5 * IQR
+        df = df[(df["simulation_time"] >= lower_bound) & (df["simulation_time"] <= upper_bound)]
         grouped = df.groupby(varying_param, as_index=False)["simulation_time"].mean()
         results[path.parent.name + "-" + path.stem] = grouped
 

+ 33 - 11
devstone/pythonpdevs/devstone.py

@@ -37,12 +37,10 @@ class DelayedAtomic(AtomicDEVS):
 
     def outputFnc(self):
         if hasattr(self, "o_out") and self.o_out is not None:
-            print(f"{self.name}: outputFnc() called at t={self.elapsed}")
             return {self.o_out: [0]}
 
         # for DSDEVS LI2HI
         if len(self.out_ports) == 1 and self.out_ports[0].outline != []:
-            print(f"{self.name}: outputFnc() called at t={self.elapsed}")
             # print(f"{self.name}: should be last call!!")
             # print("Send to ", self.out_ports[0].outline)
             return {self.out_ports[0]: [0]}
@@ -116,6 +114,9 @@ class DEVStoneWrapper(CoupledDEVS, ABC):
         self.i_in = self.addInPort("i_in")
         self.o_out = self.addOutPort("o_out")
 
+        self.models = [] # stores all models part of the coupled model
+        # added to deal with DS DEVS which don't keep it all in the component_set
+
         if depth < 1:
             raise ValueError("Invalid depth")
         if width < 1:
@@ -131,7 +132,7 @@ class DEVStoneWrapper(CoupledDEVS, ABC):
             else:
                 atomic = DelayedAtomic("Atomic_0_0", int_delay, ext_delay, add_out_port=True, prep_time=1.0, mode=mode)
 
-            self.addSubModel(atomic)
+            self.models.append(self.addSubModel(atomic))
 
             self.connectPorts(self.i_in, atomic.i_in)
             self.connectPorts(atomic.o_out, self.o_out)
@@ -150,7 +151,26 @@ class DEVStoneWrapper(CoupledDEVS, ABC):
                 else:
                     atomic = DelayedAtomic("Atomic_%d_%d" % (depth - 1, idx), int_delay, ext_delay,
                                            add_out_port=add_atomic_out_ports, prep_time=1.0 + (idx * 1e-6), mode=mode)
-                self.addSubModel(atomic)
+                self.models.append(self.addSubModel(atomic))
+
+    def extend(self, string: str, width: int):
+        length = len(string)
+        return string + " "*(width - length)
+
+    def __str__(self):
+        result = [self.extend(self.name, 16) + "\n", "-" * 20 + "\n"]
+
+        first = self.component_set[0]
+        coupled = ""
+        if isinstance(first, CoupledDEVS):
+            result.append("| " + self.extend(first.name, 16) + " |\n")
+            coupled = "\n\n" + first.__str__()
+
+        for model in self.models:
+            result.append("| " + self.extend(model.name, 16) + " |\n")
+        result.append("-"*20 + "\n")
+        result.append(coupled)
+        return "".join(result)
 
     @abstractmethod
     def gen_coupled(self):
@@ -388,7 +408,7 @@ class dLI(DEVStoneWrapper):
         super().__init__(name, depth, width, int_delay, ext_delay, add_atomic_out_ports=False, prep_time=prep_time,
                          stats=stats, mode="LI-Dynamic")
 
-        self.max_gen = 1
+        self.max_gen = 5
         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)
@@ -400,7 +420,8 @@ class dLI(DEVStoneWrapper):
                    prep_time=self.prep_time, stats=self.stats)
 
     def modelTransition(self, state):
-        if state.get("generate", True) and len(self.component_set) > 1:
+        if state.get("generate", True) and self.depth > 1:
+            # print("model", self.name)
             self.connectPorts(self.i_in, self.component_set[0].i_in)
             name = "Atomic_%d_%d" % (self.depth - 1, self.width) if self.depth > 1 else "Atomic_0_%d" % self.width
             if self.stats:
@@ -409,7 +430,8 @@ class dLI(DEVStoneWrapper):
             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.addSubModel(new_atom)
+
+            self.models.append(self.addSubModel(new_atom))
             self.connectPorts(self.i_in, new_atom.i_in)
 
             self.width += 1
@@ -426,7 +448,7 @@ class dHI(DEVStoneWrapper):
         super().__init__(name, depth, width, int_delay, ext_delay, add_atomic_out_ports=True, prep_time=prep_time,
                          stats=stats, mode="HI-Dynamic")
 
-        self.max_gen = 10
+        self.max_gen = 5
         if len(self.component_set) > 1:
             assert isinstance(self.component_set[-1], AtomicDEVS)
             self.connectPorts(self.i_in, self.component_set[-1].i_in)
@@ -443,7 +465,7 @@ class dHI(DEVStoneWrapper):
                    prep_time=self.prep_time, stats=self.stats)
 
     def modelTransition(self, state):
-        if state.get("generate", True) and len(self.component_set) > 1:
+        if state.get("generate", True) and self.depth > 1:
             name = "Atomic_%d_%d" % (self.depth - 1, self.width) if self.depth > 1 else "Atomic_0_%d" % self.width
             if self.stats:
                 new_atom = DelayedAtomicStats(name, self.int_delay, self.ext_delay,
@@ -454,10 +476,10 @@ class dHI(DEVStoneWrapper):
 
             last = self.component_set[-2]
 
-            self.addSubModel(new_atom)
+            self.models.append(self.addSubModel(new_atom))
+            self.connectPorts(self.i_in, new_atom.i_in)
             if isinstance(last, AtomicDEVS) and hasattr(last, "o_out"):
                 self.connectPorts(last.o_out, new_atom.i_in)
-            self.connectPorts(self.i_in, new_atom.i_in)
 
             self.width += 1
             # print(f"{self.name}: added {new_atom.name} (width is now {self.width})")