diff --git a/performance.txt b/performance.txt index c9fec8b..7726429 100644 --- a/performance.txt +++ b/performance.txt @@ -14,4 +14,7 @@ TOTAL TIME: 0.1792s # probably wrong calculated (asizeof was in there) SIZE: 20.2575MB # Factory instead of grid TOTAL TIME: 0.1152s # dict instead of list for cells -SIZE: 20.2575MB # process size 53 / 75,8 \ No newline at end of file +SIZE: 20.2575MB # process size 53 / 75,8 + +TOTAL TIME: 0.1135s # evolve is static +SIZE: 20.2575MB # process size 50.8 / 76 (no more cell objects in processes) \ No newline at end of file diff --git a/scripts/main_ui.py b/scripts/main_ui.py index a01e5cc..8b7f345 100644 --- a/scripts/main_ui.py +++ b/scripts/main_ui.py @@ -48,7 +48,7 @@ if __name__ == "__main__": freeze_support() random.seed(1000) - # best is 400/400 with 0,2 ca speed and 0,09 redraw + # best single is 400/400 with 0,2 ca speed and 0,09 redraw / multi is 300/300 with 0.083 neighborhood = MooreNeighborhood(EdgeRule.FIRST_AND_LAST_CELL_OF_DIMENSION_ARE_NEIGHBORS) ca = make_cellular_automaton(dimension=[100, 100], neighborhood=neighborhood, rule=TestRule(), state_class=MyState) ca_window = PyGameFor2D(window_size=[1000, 800], cellular_automaton=ca) diff --git a/scripts/performance_test b/scripts/performance_test index 112495e..6b88c59 100644 Binary files a/scripts/performance_test and b/scripts/performance_test differ diff --git a/src/cellular_automaton/ca_cell.py b/src/cellular_automaton/ca_cell.py index b072e14..d463527 100644 --- a/src/cellular_automaton/ca_cell.py +++ b/src/cellular_automaton/ca_cell.py @@ -4,33 +4,21 @@ from typing import Type class Cell: def __init__(self, state_class: Type[CellState], coordinate): - self._coordinate = coordinate + self.coordinate = coordinate self.state = state_class() self.neighbours = [] - def set_neighbours(self, neighbours): - self.neighbours = neighbours + @staticmethod + def evolve_if_ready(cell, rule, iteration): + if cell[0].is_active(iteration): + new_state = rule(cell[0].get_state_of_last_iteration(iteration), + [n.get_state_of_last_iteration(iteration) for n in cell[1]]) + Cell.set_new_state_and_activate(cell, new_state, iteration) - def get_state(self): - return self.state - - def get_coordinate(self): - return self._coordinate - - def evolve_if_ready(self, rule, iteration): - if self.state.is_active(iteration): - new_state = rule(self.state.get_state_of_last_iteration(iteration), self.get_neighbour_states(iteration)) - self.set_new_state_and_activate(new_state, iteration) - - def get_neighbour_states(self, index): - return [n.get_state_of_last_iteration(index) for n in self.neighbours] - - def set_new_state_and_activate(self, new_state: CellState, iteration): - changed = self.state.set_state_of_iteration(new_state, iteration) + @staticmethod + def set_new_state_and_activate(cell, new_state: CellState, iteration): + changed = cell[0].set_state_of_iteration(new_state, iteration) if changed: - self._set_active_for_next_iteration(iteration) - - def _set_active_for_next_iteration(self, iteration): - self.state.set_active_for_next_iteration(iteration) - for n in self.neighbours: - n.set_active_for_next_iteration(iteration) + cell[0].set_active_for_next_iteration(iteration) + for n in cell[1]: + n.set_active_for_next_iteration(iteration) diff --git a/src/cellular_automaton/ca_cell_state.py b/src/cellular_automaton/ca_cell_state.py index 706a928..cc48640 100644 --- a/src/cellular_automaton/ca_cell_state.py +++ b/src/cellular_automaton/ca_cell_state.py @@ -17,16 +17,6 @@ class CellState: else: self._dirty = RawValue(c_bool, False) - def get_state_of_iteration(self, iteration): - """ Will return the state for the iteration modulo number of saved states. - :param iteration: Uses the iteration index, to differ between concurrent states. - :return The state for this iteration. - """ - return self._state_slots[self.__calculate_slot(iteration)] - - def __calculate_slot(self, iteration): - return iteration % self.__class__._state_save_slot_count - def is_active(self, iteration): return self._active[self.__calculate_slot(iteration)] @@ -42,6 +32,13 @@ class CellState: def get_state_of_last_iteration(self, current_iteration_index): return self.get_state_of_iteration(current_iteration_index - 1) + def get_state_of_iteration(self, iteration): + """ Will return the state for the iteration modulo number of saved states. + :param iteration: Uses the iteration index, to differ between concurrent states. + :return The state for this iteration. + """ + return self._state_slots[self.__calculate_slot(iteration)] + def set_state_of_iteration(self, new_state, iteration): """ Will set the new state for the iteration modulo number of saved states. :param new_state: The new state to set. @@ -66,3 +63,7 @@ class CellState: def get_state_draw_color(self, iteration): raise NotImplementedError + + @classmethod + def __calculate_slot(cls, iteration): + return iteration % cls._state_save_slot_count diff --git a/src/cellular_automaton/ca_display.py b/src/cellular_automaton/ca_display.py index 84d01cb..f642002 100644 --- a/src/cellular_automaton/ca_display.py +++ b/src/cellular_automaton/ca_display.py @@ -92,13 +92,13 @@ class PyGameFor2D: def _cell_redraw_rectangles(cells, evolution_index, display_info): for cell in cells: - if cell.get_state().is_set_for_redraw(): - cell_color = cell.get_state().get_state_draw_color(evolution_index) + if cell.state.is_set_for_redraw(): + cell_color = cell.state.get_state_draw_color(evolution_index) cell_pos = _calculate_cell_position(display_info.cell_size, cell) surface_pos = list(map(operator.add, cell_pos, display_info.grid_pos)) yield display_info.screen.fill(cell_color, (surface_pos, display_info.cell_size)) - cell.get_state().was_redrawn() + cell.state.was_redrawn() def _calculate_cell_position(cell_size, cell): - return list(map(operator.mul, cell_size, cell.get_coordinate())) + return list(map(operator.mul, cell_size, cell.coordinate)) diff --git a/src/cellular_automaton/ca_factory.py b/src/cellular_automaton/ca_factory.py index d70723d..de998f2 100644 --- a/src/cellular_automaton/ca_factory.py +++ b/src/cellular_automaton/ca_factory.py @@ -25,9 +25,9 @@ class CAFactory: @staticmethod def _apply_neighbourhood_to_cells(cells, neighborhood, dimension): for cell in cells.values(): - n_coordinates = neighborhood.calculate_cell_neighbor_coordinates(cell.get_coordinate(), + n_coordinates = neighborhood.calculate_cell_neighbor_coordinates(cell.coordinate, dimension) - cell.set_neighbours([cells[_join_coordinate(coordinate)].get_state() for coordinate in n_coordinates]) + cell.neighbours = [cells[_join_coordinate(coordinate)].state for coordinate in n_coordinates] def _join_coordinate(coordinate): diff --git a/src/cellular_automaton/cellular_automaton.py b/src/cellular_automaton/cellular_automaton.py index f7bf9c7..c258fdd 100644 --- a/src/cellular_automaton/cellular_automaton.py +++ b/src/cellular_automaton/cellular_automaton.py @@ -1,6 +1,7 @@ import multiprocessing from cellular_automaton.ca_rule import Rule +from cellular_automaton.ca_cell import Cell class CellularAutomaton: @@ -14,7 +15,7 @@ class CellularAutomaton: class CellularAutomatonProcessor: def __init__(self, cellular_automaton, process_count: int = 1): self.ca = cellular_automaton - cells = {i: self.ca.cells[i] for i in range(len(self.ca.cells))} + cells = {i: (c.state, c.neighbours) for i, c in enumerate(self.ca.cells)} self.evolve_range = range(len(self.ca.cells)) self.pool = multiprocessing.Pool(processes=process_count, initializer=_init_process, @@ -22,7 +23,7 @@ class CellularAutomatonProcessor: self.ca.evolution_rule, self.ca.evolution_iteration_index)) for cell in self.ca.cells: - cell.set_neighbours(None) + del cell.neighbours def evolve_x_times(self, x): for x in range(x): @@ -46,5 +47,5 @@ def _init_process(cells, rule, index): def _process_routine(i): - global_cells[i].evolve_if_ready(global_rule.evolve_cell, global_iteration.value) + Cell.evolve_if_ready(global_cells[i], global_rule.evolve_cell, global_iteration.value) diff --git a/test/test_cell.py b/test/test_cell.py index 17ca8ac..f10bf40 100644 --- a/test/test_cell.py +++ b/test/test_cell.py @@ -1,26 +1,25 @@ import sys sys.path.append('../src') -import cellular_automaton.ca_cell_state as cas -import cellular_automaton.ca_cell as cac +from cellular_automaton import * import unittest -class TestState(cas.CellState): +class TestState(CellState): def __init__(self): super().__init__() class TestCellState(unittest.TestCase): def setUp(self): - self.cell = cac.Cell(TestState, []) + self.cell = [TestState(), []] self.neighbours = [TestState() for x in range(5)] for neighbour in self.neighbours: neighbour.set_state_of_iteration((0, ), 0) - self.cell.set_neighbours(self.neighbours) + self.cell[1] = self.neighbours def cell_and_neighbours_active(self, iteration): - self.neighbours.append(self.cell.get_state()) + self.neighbours.append(self.cell[0]) all_active = True for state in self.neighbours: if not state.is_active(iteration): @@ -28,12 +27,12 @@ class TestCellState(unittest.TestCase): return all_active def test_evolve_activation(self): - self.cell.evolve_if_ready((lambda a, b: (1,)), 0) + Cell.evolve_if_ready(self.cell, (lambda a, b: (1,)), 0) all_active = self.cell_and_neighbours_active(1) self.assertTrue(all_active) def test_evolve_activation_on_no_change(self): - self.cell.evolve_if_ready((lambda a, b: (0,)), 0) + Cell.evolve_if_ready(self.cell, (lambda a, b: (0,)), 0) all_active = self.cell_and_neighbours_active(1) self.assertFalse(all_active)