From 40aa3a4d84b338c3ee249ee301d282672560ab34 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ivar=20H=C3=A4rnqvist?= <ivaha717@student.liu.se>
Date: Tue, 14 Apr 2020 20:54:53 +0200
Subject: [PATCH] add ability to choose if/when simulation results are saved

---
 b_asic/operation.py          |  3 ++-
 b_asic/simulation.py         | 25 ++++++++++++++++++---
 b_asic/special_operations.py | 13 ++++++-----
 test/test_simulation.py      | 42 ++++++++++++++++++------------------
 4 files changed, 51 insertions(+), 32 deletions(-)

diff --git a/b_asic/operation.py b/b_asic/operation.py
index 49b78337..79798a52 100644
--- a/b_asic/operation.py
+++ b/b_asic/operation.py
@@ -283,7 +283,8 @@ class AbstractOperation(Operation, AbstractGraphComponent):
         return key
     
     def current_output(self, index: int, results: Optional[MutableMapping[ResultKey, Optional[Number]]] = None, registers: Optional[Mapping[ResultKey, Number]] = None, prefix: str = "") -> Optional[Number]:
-        results[self.key(index, prefix)] = None
+        if results is not None:
+            results[self.key(index, prefix)] = None
         return None
 
     def evaluate_output(self, index: int, input_values: Sequence[Number], results: Optional[MutableMapping[ResultKey, Optional[Number]]] = None, registers: Optional[MutableMapping[ResultKey, Number]] = None, prefix: str = "") -> Number:
diff --git a/b_asic/simulation.py b/b_asic/simulation.py
index d36cc568..b6d08bc1 100644
--- a/b_asic/simulation.py
+++ b/b_asic/simulation.py
@@ -23,8 +23,9 @@ class Simulation:
     _input_functions: Sequence[Callable[[int], Number]]
     _current_input_values: Sequence[Number]
     _latest_output_values: Sequence[Number]
+    _save_results: bool
 
-    def __init__(self, sfg: SFG, input_providers: Optional[Sequence[Union[None, Sequence[Number], Callable[[int], Number]]]] = None):
+    def __init__(self, sfg: SFG, input_providers: Optional[Sequence[Union[None, Sequence[Number], Callable[[int], Number]]]] = None, save_results: bool = False):
         self._sfg = sfg
         self._results = defaultdict(dict)
         self._registers = {}
@@ -32,6 +33,7 @@ class Simulation:
         self._input_functions = [lambda n: 0 for _ in range(self._sfg.input_count)]
         self._current_input_values = []
         self._latest_output_values = [0 for _ in range(self._sfg.output_count)]
+        self._save_results = save_results
         if input_providers is not None:
             self.set_inputs(input_providers)
 
@@ -54,16 +56,28 @@ class Simulation:
             else:
                 self._input_functions[index] = lambda n: input_provider[n]
 
+    @property
+    def save_results(self) -> bool:
+        """Get the flag that determines if the results of ."""
+        return self._save_results
+
+    @save_results.setter
+    def save_results(self, save_results) -> None:
+        self._save_results = save_results
+
     def run(self) -> Sequence[Number]:
         """Run one iteration of the simulation and return the resulting output values."""
         return self.run_for(1)
 
     def run_until(self, iteration: int) -> Sequence[Number]:
         """Run the simulation until its iteration is greater than or equal to the given iteration
-        and return the resulting output values."""
+        and return the resulting output values.
+        """
         while self._iteration < iteration:
             self._current_input_values = [self._input_functions[i](self._iteration) for i in range(self._sfg.input_count)]
             self._latest_output_values = self._sfg.evaluate_outputs(self._current_input_values, self._results[self._iteration], self._registers)
+            if not self._save_results:
+                del self._results[self.iteration]
             self._iteration += 1
         return self._latest_output_values
 
@@ -80,5 +94,10 @@ class Simulation:
     def results(self) -> Mapping[int, Mapping[ResultKey, Number]]:
         """Get a mapping of all results, including intermediate values, calculated for each iteration up until now.
         The outer mapping maps from iteration number to value mapping. The value mapping maps output port identifiers to values.
-        Example: {0: {"c1": 3, "c2": 4, "bfly1.0": 7, "bfly1.1": -1, "0": 7}}"""
+        Example: {0: {"c1": 3, "c2": 4, "bfly1.0": 7, "bfly1.1": -1, "0": 7}}
+        """
         return self._results
+
+    def clear_results(self) -> None:
+        """Clear all results that were saved until now."""
+        self._results.clear()
\ No newline at end of file
diff --git a/b_asic/special_operations.py b/b_asic/special_operations.py
index fcc3976f..83555482 100644
--- a/b_asic/special_operations.py
+++ b/b_asic/special_operations.py
@@ -75,7 +75,8 @@ class Register(AbstractOperation):
         value = self.param("initial_value")
         if registers is not None:
             value = registers.get(key, value)
-        results[key] = value
+        if results is not None:
+            results[key] = value
         return value
     
     def evaluate_output(self, index: int, input_values: Sequence[Number], results: Optional[MutableMapping[ResultKey, Optional[Number]]] = None, registers: Optional[MutableMapping[ResultKey, Number]] = None, prefix: str = ""):
@@ -85,12 +86,10 @@ class Register(AbstractOperation):
             raise ValueError(f"Wrong number of inputs supplied to SFG for evaluation (expected 1, got {len(input_values)})")
         
         key = self.key(index, prefix)
+        value = self.param("initial_value")
         if registers is not None:
-            value = registers.get(key, self.param("initial_value"))
-            results[key] = value
+            value = registers.get(key, value)
             registers[key] = self.truncate_inputs(input_values)[0]
-            return value
-        
-        value = self.param("initial_value")
-        results[key] = value
+        if results is not None:
+            results[key] = value
         return value
\ No newline at end of file
diff --git a/test/test_simulation.py b/test/test_simulation.py
index a8103aed..faa1f75e 100644
--- a/test/test_simulation.py
+++ b/test/test_simulation.py
@@ -6,7 +6,7 @@ from b_asic import SFG, Output, Simulation
 
 class TestSimulation:
     def test_simulate_with_lambdas_as_input(self, sfg_two_inputs_two_outputs):
-        simulation = Simulation(sfg_two_inputs_two_outputs, [lambda n: n + 3, lambda n: 1 + n * 2])
+        simulation = Simulation(sfg_two_inputs_two_outputs, [lambda n: n + 3, lambda n: 1 + n * 2], save_results = True)
 
         output = simulation.run_for(101)
 
@@ -48,15 +48,13 @@ class TestSimulation:
         input0 = np.array([5, 9, 25, -5, 7])
         input1 = np.array([7, 3, 3,  54, 2])
         simulation = Simulation(sfg_two_inputs_two_outputs, [input0, input1])
+        simulation.save_results = True
 
         output = simulation.run_for(5)
 
         assert output[0] == 9
         assert output[1] == 11
 
-        assert simulation.results[4]["0"] == 9
-        assert simulation.results[4]["1"] == 11
-
         assert simulation.results[0]["in1"] == 5
         assert simulation.results[0]["in2"] == 7
         assert simulation.results[0]["add1"] == 12
@@ -85,6 +83,9 @@ class TestSimulation:
         assert simulation.results[3]["0"] == 49
         assert simulation.results[3]["1"] == 103
 
+        assert simulation.results[4]["0"] == 9
+        assert simulation.results[4]["1"] == 11
+
     def test_simulate_with_numpy_array_overflow(self, sfg_two_inputs_two_outputs):
         input0 = np.array([5, 9, 25, -5, 7])
         input1 = np.array([7, 3, 3,  54, 2])
@@ -98,19 +99,17 @@ class TestSimulation:
         input1 = np.array([7, 3])
         simulation = Simulation(sfg_nested, [input0, input1])
 
+        output0 = simulation.run()
         output1 = simulation.run()
-        output2 = simulation.run()
 
-        assert output1[0] == 11405
-        assert output2[0] == 4221
+        assert output0[0] == 11405
+        assert output1[0] == 4221
 
     def test_simulate_delay(self, sfg_delay):
-        simulation = Simulation(sfg_delay)
+        simulation = Simulation(sfg_delay, save_results = True)
         simulation.set_input(0, [5, -2, 25, -6, 7, 0])
         simulation.run_for(6)
 
-        print(simulation.results)
-
         assert simulation.results[0]["0"] == 0
         assert simulation.results[1]["0"] == 5
         assert simulation.results[2]["0"] == -2
@@ -122,14 +121,15 @@ class TestSimulation:
         data_in = np.array([5, -2, 25, -6, 7, 0])
         reset   = np.array([0, 0,  0,  1,  0, 0])
         simulation = Simulation(sfg_accumulator, [data_in, reset])
-
-        output = simulation.run_for(6)
-
-        assert output[0] == 7
-
-        assert simulation.results[0]["0"] == 0
-        assert simulation.results[1]["0"] == 5
-        assert simulation.results[2]["0"] == 3
-        assert simulation.results[3]["0"] == 28
-        assert simulation.results[4]["0"] == 0
-        assert simulation.results[5]["0"] == 7
\ No newline at end of file
+        output0 = simulation.run()
+        output1 = simulation.run()
+        output2 = simulation.run()
+        output3 = simulation.run()
+        output4 = simulation.run()
+        output5 = simulation.run()
+        assert output0[0] == 0
+        assert output1[0] == 5
+        assert output2[0] == 3
+        assert output3[0] == 28
+        assert output4[0] == 0
+        assert output5[0] == 7
\ No newline at end of file
-- 
GitLab