second multiprocessing try, better but still bad

This commit is contained in:
Richard Feistenauer 2019-02-02 09:11:48 +01:00
parent be57f89e78
commit d1820ebc58
8 changed files with 98 additions and 105 deletions

View File

@ -10,6 +10,7 @@ class TestRule(Rule):
try: try:
return last_neighbour_states[0] return last_neighbour_states[0]
except IndexError: except IndexError:
print("damn neighbours")
pass pass
return False return False
@ -21,7 +22,7 @@ class MyState(CellState):
if rand > 99: if rand > 99:
init = 1 init = 1
super().__init__(init, draw_first_state=False) super().__init__((float(init),), draw_first_state=False)
def get_state_draw_color(self, iteration): def get_state_draw_color(self, iteration):
red = 0 red = 0
@ -41,7 +42,7 @@ if __name__ == "__main__":
freeze_support() freeze_support()
random.seed(1000) random.seed(1000)
rule = TestRule() rule = TestRule()
grid = Grid(dimension=[400, 400], grid = Grid(dimension=[200, 200], # best is 400/400 with 0,2 ca speed and 0,09 redraw
neighborhood=MooreNeighborhood(EdgeRule.FIRST_AND_LAST_CELL_OF_DIMENSION_ARE_NEIGHBORS), neighborhood=MooreNeighborhood(EdgeRule.FIRST_AND_LAST_CELL_OF_DIMENSION_ARE_NEIGHBORS),
state_class=MyState) state_class=MyState)
ca = CellularAutomaton(grid, rule) ca = CellularAutomaton(grid, rule)

View File

@ -1,61 +1,44 @@
import multiprocessing
from cellular_automaton.ca_cell_state import CellState from cellular_automaton.ca_cell_state import CellState
class Cell: class Cell:
def __init__(self, name, state_class: CellState.__class__, coordinate: list): def __init__(self, state_class: CellState.__class__, coordinate: list):
self._name = name
self._coordinate = coordinate self._coordinate = coordinate
self._state = state_class() self._state = state_class()
self._neighbours = [] self._neighbours = []
self._active = multiprocessing.Value('i', 1)
self._age = multiprocessing.Value('i', 0)
def set_neighbours(self, neighbours): def set_neighbours(self, neighbours):
self._neighbours = neighbours self._neighbours = neighbours
def get_state(self):
return self._state
def get_coordinate(self): def get_coordinate(self):
return self._coordinate return self._coordinate
def evolve_if_ready(self, rule): def evolve_if_ready(self, rule):
if self._neighbours_are_younger(): if self._neighbours_are_younger():
if self._is_active(): if self._state.is_active():
new_state = rule(self.get_current_state(), self.get_neighbour_states()) new_state = rule(self._state.get_current_state(), self.get_neighbour_states())
self.set_new_state_and_activate(new_state) self.set_new_state_and_activate(new_state)
self.increase_age() self._state.increase_age()
def _neighbours_are_younger(self): def _neighbours_are_younger(self):
for n in self._neighbours: for n in self._neighbours:
if n.get_age() < self.get_age(): if n.get_age() < self._state.get_age():
return False return False
return True
def get_age(self):
return self._age.value
def _is_active(self):
return self._active.value > self._age.value
def get_current_state(self):
return self._state.get_state_of_iteration(self._age.value)
def get_neighbour_states(self): def get_neighbour_states(self):
return [n.get_state_from_iteration(self._age.value) for n in self._neighbours] return [n.get_state_of_iteration(self._state.get_age()) for n in self._neighbours]
def set_new_state_and_activate(self, new_state: CellState): def set_new_state_and_activate(self, new_state: CellState):
changed = self._state.set_current_state(new_state, self._age.value + 1) changed = self._state.set_current_state(new_state)
if changed: if changed:
self._set_active() self._set_active()
def _set_active(self): def _set_active(self):
self.set_active_for_next_iteration(self._age.value) self._state.set_active_for_next_iteration(self._state.get_age() + 1)
for n in self._neighbours: for n in self._neighbours:
n.set_active_for_next_iteration(self._age.value) n.set_active_for_next_iteration(self._state.get_age() + 1)
def set_active_for_next_iteration(self, iteration):
self._active.value = max(self._active.value, iteration + 1)
def increase_age(self):
with self._age.get_lock():
self._age += 1

View File

@ -2,32 +2,51 @@ from multiprocessing import Array, Value
class CellState: class CellState:
_state_save_slot_count = 2
""" """
This is the base class for all cell states. This is the base class for all cell states.
When using the cellular automaton display, inherit this class and implement get_state_draw_color. When using the cellular automaton display, inherit this class and implement get_state_draw_color.
""" """
def __init__(self, initial_state=(0., ), state_save_slot_count=2, draw_first_state=True): def __init__(self, initial_state=(0., ), draw_first_state=True):
self._state_save_slot_count = state_save_slot_count self._state_slots = [Array('d', initial_state) for i in range(self.__class__._state_save_slot_count)]
self._state_slots = [Array('d', initial_state)] * state_save_slot_count self._active = Value('i', 1)
self._age = Value('i', 0)
if draw_first_state: if draw_first_state:
self._dirty = Value('i', 1) self._dirty = Value('i', 1)
else: else:
self._dirty = Value('i', 0) self._dirty = Value('i', 0)
def get_age(self):
return self._age.value
def get_current_state(self):
return self.get_state_of_iteration(self.get_age())
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[iteration % self.__class__._state_save_slot_count]
def is_active(self):
return self._active.value > self._age.value
def set_active_for_next_iteration(self, iteration):
self._active.value = max(self._active.value, iteration + 1)
def increase_age(self):
with self._age.get_lock():
self._age.value += 1
def is_set_for_redraw(self): def is_set_for_redraw(self):
return self._dirty != 1 return self._dirty != 1
def get_state_changes(self):
return self._dirty
def set_for_redraw(self):
self._dirty = 1
def was_redrawn(self): def was_redrawn(self):
self._dirty = 0 self._dirty = 0
def set_current_state(self, new_state, current_iteration_index): def set_current_state(self, new_state):
return self.set_state_of_iteration(new_state, current_iteration_index) return self.set_state_of_iteration(new_state, self.get_age() + 1)
def get_state_of_last_iteration(self, current_iteration_index): def get_state_of_last_iteration(self, current_iteration_index):
return self.get_state_of_iteration(current_iteration_index - 1) return self.get_state_of_iteration(current_iteration_index - 1)
@ -38,10 +57,8 @@ class CellState:
:param iteration: Uses the iteration index, to differ between concurrent states. :param iteration: Uses the iteration index, to differ between concurrent states.
:return True if state has changed. :return True if state has changed.
""" """
slot_count = self._state_save_slot_count
states = self._state_slots
current_state = states[iteration % slot_count] current_state = self.get_state_of_iteration(iteration)
changed = False changed = False
for i in range(len(current_state)): for i in range(len(current_state)):
@ -53,16 +70,9 @@ class CellState:
except IndexError: except IndexError:
raise IndexError("New State length or type is invalid!") raise IndexError("New State length or type is invalid!")
self._dirty |= changed self._dirty.value |= changed
return changed return changed
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[iteration % self._state_save_slot_count]
def get_state_draw_color(self, iteration): def get_state_draw_color(self, iteration):
raise NotImplementedError raise NotImplementedError

View File

@ -74,13 +74,13 @@ class PyGameFor2D:
def _cell_redraw_rectangles(cells, evolution_index, display_info): def _cell_redraw_rectangles(cells, evolution_index, display_info):
for cell in cells: for cell in cells:
if cell.state.is_set_for_redraw(): if cell.get_state().is_set_for_redraw():
cell_color = cell.state.get_state_draw_color(evolution_index) cell_color = cell.get_state().get_state_draw_color(evolution_index)
cell_pos = _calculate_cell_position(display_info.cell_size, cell) cell_pos = _calculate_cell_position(display_info.cell_size, cell)
surface_pos = list(map(operator.add, cell_pos, display_info.grid_pos)) 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)) yield display_info.screen.fill(cell_color, (surface_pos, display_info.cell_size))
cell.state.was_redrawn() cell.get_state().was_redrawn()
def _calculate_cell_position(cell_size, cell): def _calculate_cell_position(cell_size, cell):
return list(map(operator.mul, cell_size, cell.coordinate)) return list(map(operator.mul, cell_size, cell.get_coordinate()))

View File

@ -17,36 +17,6 @@ class Grid:
self._active_cells = self._cells.copy() self._active_cells = self._cells.copy()
self._set_all_cells_active() self._set_all_cells_active()
def get_active_cell_names(self):
return list(self._active_cells.keys())
def get_active_cells(self):
return self._active_cells
def get_cells(self):
return self._cells
def clear_active_cells(self):
self._active_cells = {}
def set_cells_active(self, cells: list):
""" Consider the cells in the next evolution cycle.
:param cells: A list of Cell objects, that shall be considered in the next evolution cycle.
"""
for cell in cells:
self._active_cells[cell.name] = cell
def get_cell_and_neighbors(self, cell_name):
cell = self._cells[cell_name]
return [cell, cell.neighbours]
def get_dimension(self):
return self._dimension
def _set_all_cells_active(self):
for cell_key, cell in self._cells.items():
self._active_cells[cell_key] = cell
def _create_cells(self, dimension_index=0, coordinate=None): def _create_cells(self, dimension_index=0, coordinate=None):
""" Recursively steps down the dimensions to create cells in n dimensions and adds them to a dict. """ Recursively steps down the dimensions to create cells in n dimensions and adds them to a dict.
:param dimension_index: The index indicating which dimension is currently traversed. :param dimension_index: The index indicating which dimension is currently traversed.
@ -59,7 +29,7 @@ class Grid:
self._recursive_step_down_dimensions(coordinate, dimension_index, self._create_cells) self._recursive_step_down_dimensions(coordinate, dimension_index, self._create_cells)
except IndexError: except IndexError:
coordinate_string = _join_coordinate(coordinate) coordinate_string = _join_coordinate(coordinate)
self._cells[coordinate_string] = Cell(coordinate_string, self._state_class, coordinate) self._cells[coordinate_string] = Cell(self._state_class, coordinate)
def _recursive_step_down_dimensions(self, coordinate, dimension_index, recursion_method): def _recursive_step_down_dimensions(self, coordinate, dimension_index, recursion_method):
""" For the range of the current dimension, recalls the recursion method. """ For the range of the current dimension, recalls the recursion method.
@ -75,10 +45,26 @@ class Grid:
for cell in self._cells.values(): for cell in self._cells.values():
neighbours_coordinates = neighborhood.calculate_cell_neighbor_coordinates(cell.get_coordinate(), neighbours_coordinates = neighborhood.calculate_cell_neighbor_coordinates(cell.get_coordinate(),
self._dimension) self._dimension)
cell.set_neighbours(list(map(self._get_cell_by_coordinate, neighbours_coordinates))) cell.set_neighbours(list(map(self._get_cell_state_by_coordinate, neighbours_coordinates)))
def _get_cell_by_coordinate(self, coordinate): def _get_cell_state_by_coordinate(self, coordinate):
return self._cells[_join_coordinate(coordinate)] return self._cells[_join_coordinate(coordinate)].get_state()
def _set_all_cells_active(self):
for cell_key, cell in self._cells.items():
self._active_cells[cell_key] = cell
def get_active_cell_names(self):
return list(self._active_cells.keys())
def get_active_cells(self):
return self._active_cells
def get_cells(self):
return self._cells
def get_dimension(self):
return self._dimension
def _instantiate_coordinate_if_necessary(coordinate): def _instantiate_coordinate_if_necessary(coordinate):

View File

@ -35,5 +35,5 @@ class CellularAutomatonProcessor:
def _process_routine(cells, rule, active): def _process_routine(cells, rule, active):
while active.value == 1: while active.value == 1:
for cell in cells: for cell in cells:
cell.evolve_if_ready(rule) cell.evolve_if_ready(rule.evolve_cell)

13
test/test_cell.py Normal file
View File

@ -0,0 +1,13 @@
import sys
sys.path.append('../src')
import cellular_automaton.ca_cell as cac
import unittest
class TestCellState(unittest.TestCase):
pass
if __name__ == '__main__':
unittest.main()

View File

@ -7,20 +7,20 @@ import unittest
class TestCellState(unittest.TestCase): class TestCellState(unittest.TestCase):
def test_get_state_of_iteration(self): def test_get_state_with_overflow(self):
cell_state = cs.CellState(0, state_save_slot_count=3) cell_state = cs.CellState(initial_state=(0,))
cell_state.set_status_of_iteration(1, 0) cell_state.set_state_of_iteration(new_state=(1,), iteration=0)
self.assertEqual(cell_state.get_status_of_iteration(3), 1) self.assertEqual(tuple(cell_state.get_state_of_iteration(2)), (1,))
def test_set_state_applies_overflow(self): def test_set_state_with_overflow(self):
cell_state = cs.CellState(0, state_save_slot_count=4) cell_state = cs.CellState(initial_state=(0,))
cell_state.set_status_of_iteration(1, 4) cell_state.set_state_of_iteration(new_state=(1,), iteration=2)
self.assertEqual(cell_state.get_status_of_iteration(0), 1) self.assertEqual(tuple(cell_state.get_state_of_iteration(0)), (1,))
def test_set_state_only_applies_to_iteration_slot(self): def test_set_state_does_not_effect_all_slots(self):
cell_state = cs.CellState(0, state_save_slot_count=2) cell_state = cs.CellState(initial_state=(0,))
cell_state.set_status_of_iteration(1, 0) cell_state.set_state_of_iteration(new_state=(1,), iteration=0)
self.assertEqual(cell_state.get_status_of_iteration(1), 0) self.assertEqual(tuple(cell_state.get_state_of_iteration(1)), (0,))
if __name__ == '__main__': if __name__ == '__main__':