diff --git a/b_asic/operation.py b/b_asic/operation.py index 49b7833794e68ab8d296719ee392eae5f6f9936c..79798a5221cb9133124256f9650c070e1ed73f03 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 d36cc56876a2cce5dc82a36f7e77cf9ba6863927..b6d08bc1297deac1c5e1b9b8ec7945e72d66f811 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 fcc3976faffdc4e5f837a00b31d63f31325f835e..83555482daf8671fb18ee6a3a3b5139c18bc0ac3 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 a8103aed800736fedf2b425bf3c23fab377a4094..faa1f75eb12acccf26169f31849f61da83df598e 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