diff --git a/scripts/main_ui.py b/scripts/main_ui.py index baad7a5..55d371a 100644 --- a/scripts/main_ui.py +++ b/scripts/main_ui.py @@ -43,8 +43,8 @@ if __name__ == "__main__": random.seed(1000) # 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_processor = CellularAutomatonProcessor(process_count=4, cellular_automaton=ca) + ca = make_cellular_automaton(dimension=[400, 400], neighborhood=neighborhood, rule=TestRule(), state_class=MyState) + ca_processor = CellularAutomatonProcessor(process_count=1, cellular_automaton=ca) ca_window = PyGameFor2D(window_size=[1000, 800], cellular_automaton=ca) - ca_window.main_loop(cellular_automaton_processor=ca_processor, ca_iterations_per_draw=10) + ca_window.main_loop(cellular_automaton_processor=ca_processor, ca_iterations_per_draw=1) diff --git a/scripts/performance_test b/scripts/performance_test index 7ab56b9..0bc20ef 100644 Binary files a/scripts/performance_test and b/scripts/performance_test differ diff --git a/src/cellular_automaton/ca_cell_state.py b/src/cellular_automaton/ca_cell_state.py index 4aa0020..bc70f78 100644 --- a/src/cellular_automaton/ca_cell_state.py +++ b/src/cellular_automaton/ca_cell_state.py @@ -3,31 +3,30 @@ from ctypes import c_float, c_bool class CellState: - _state_save_slot_count = 2 """ This is the base class for all cell states. When using the cellular automaton display, inherit this class and implement get_state_draw_color. """ + + _state_save_slot_count = 2 + def __init__(self, initial_state=(0., ), draw_first_state=True): - self._state_slots = [RawArray(c_float, initial_state) for i in range(self.__class__._state_save_slot_count)] - self._active = [RawValue(c_bool, False) for i in range(self.__class__._state_save_slot_count)] - self._active[0].value = True - if draw_first_state: - self._dirty = RawValue(c_bool, True) - else: - self._dirty = RawValue(c_bool, False) + self._state_slots = [list(initial_state) for i in range(self.__class__._state_save_slot_count)] + self._active = [False for i in range(self.__class__._state_save_slot_count)] + self._active[0] = True + self._dirty = draw_first_state def is_active(self, iteration): - return self._active[self.__calculate_slot(iteration)] + return self._active[self._calculate_slot(iteration)] def set_active_for_next_iteration(self, iteration): - self._active[self.__calculate_slot(iteration + 1)].value = True + self._active[self._calculate_slot(iteration + 1)] = True def is_set_for_redraw(self): - return self._dirty.value + return self._dirty def was_redrawn(self): - self._dirty.value = False + self._dirty = False def get_state_of_last_iteration(self, current_iteration_index): return self.get_state_of_iteration(current_iteration_index - 1) @@ -37,7 +36,7 @@ class CellState: :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)] + 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. @@ -45,25 +44,50 @@ class CellState: :param iteration: Uses the iteration index, to differ between concurrent states. :return True if state has changed. """ + changed = self._change_state(new_state, iteration) + self._dirty |= changed + self._active[self._calculate_slot(iteration)] = False + return changed + def _change_state(self, new_state, iteration): current_state = self.get_state_of_iteration(iteration) - changed = False - for i in range(len(current_state)): + for i, ns in enumerate(new_state): try: - if current_state[i] != new_state[i]: + if current_state[i] != ns: changed = True - current_state[i] = new_state[i] + current_state[i] = ns except IndexError: raise IndexError("New State length or type is invalid!") - - self._dirty.value |= changed - self._active[self.__calculate_slot(iteration)].value = False return changed def get_state_draw_color(self, iteration): raise NotImplementedError @classmethod - def __calculate_slot(cls, iteration): + def _calculate_slot(cls, iteration): return iteration % cls._state_save_slot_count + + +class SynchronousCellState(CellState): + def __init__(self, initial_state=(0., ), draw_first_state=True): + super().__init__(initial_state, draw_first_state) + self._state_slots = [RawArray(c_float, initial_state) for i in range(self.__class__._state_save_slot_count)] + self._active = [RawValue(c_bool, False) for i in range(self.__class__._state_save_slot_count)] + self._active[0].value = True + self._dirty = RawValue(c_bool, draw_first_state) + + def set_active_for_next_iteration(self, iteration): + self._active[self._calculate_slot(iteration + 1)].value = True + + def is_set_for_redraw(self): + return self._dirty.value + + def was_redrawn(self): + self._dirty.value = False + + def set_state_of_iteration(self, new_state, iteration): + changed = self._change_state(new_state, iteration) + self._dirty.value |= changed + self._active[self._calculate_slot(iteration)].value = False + return changed diff --git a/src/cellular_automaton/cellular_automaton.py b/src/cellular_automaton/cellular_automaton.py index c258fdd..e745107 100644 --- a/src/cellular_automaton/cellular_automaton.py +++ b/src/cellular_automaton/cellular_automaton.py @@ -17,11 +17,18 @@ class CellularAutomatonProcessor: self.ca = cellular_automaton 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, - initargs=(cells, - self.ca.evolution_rule, - self.ca.evolution_iteration_index)) + self._evolve_method = lambda x, y: None + + if process_count > 1: + self.pool = multiprocessing.Pool(processes=process_count, + initializer=_init_process, + initargs=(cells, + self.ca.evolution_rule, + self.ca.evolution_iteration_index)) + self._evolve_method = self.pool.map + else: + _init_process(cells, self.ca.evolution_rule, self.ca.evolution_iteration_index) + self._evolve_method = lambda x, y: list(map(x, y)) for cell in self.ca.cells: del cell.neighbours @@ -31,7 +38,7 @@ class CellularAutomatonProcessor: def evolve(self): self.ca.evolution_iteration_index.value += 1 - self.pool.map(_process_routine, self.evolve_range) + self._evolve_method(_process_routine, self.evolve_range) global_cells = None