some more tests and improvements
This commit is contained in:
parent
743d6f4548
commit
823fdbc307
@ -8,4 +8,10 @@ TOTAL TIME: 0.1171s # Only set on change
|
|||||||
SIZE: 21.7525MB # process size 51,4 / main(75,9)
|
SIZE: 21.7525MB # process size 51,4 / main(75,9)
|
||||||
|
|
||||||
TOTAL TIME: 0.1161s
|
TOTAL TIME: 0.1161s
|
||||||
SIZE: 20.3338MB # removed grid
|
SIZE: 20.3338MB # removed grid
|
||||||
|
|
||||||
|
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
|
@ -4,7 +4,7 @@ from cellular_automaton.ca_cell_state import CellState
|
|||||||
from cellular_automaton.ca_rule import Rule
|
from cellular_automaton.ca_rule import Rule
|
||||||
|
|
||||||
from cellular_automaton.cellular_automaton import CellularAutomaton, CellularAutomatonProcessor
|
from cellular_automaton.cellular_automaton import CellularAutomaton, CellularAutomatonProcessor
|
||||||
from cellular_automaton.ca_factory import Factory
|
from cellular_automaton.ca_factory import CAFactory
|
||||||
|
|
||||||
|
|
||||||
class TestRule(Rule):
|
class TestRule(Rule):
|
||||||
@ -35,8 +35,7 @@ class MyState(CellState):
|
|||||||
|
|
||||||
|
|
||||||
def make_cellular_automaton(dimension, neighborhood, rule, state_class):
|
def make_cellular_automaton(dimension, neighborhood, rule, state_class):
|
||||||
ca_factory = Factory()
|
cells = CAFactory.make_cellular_automaton(dimension=dimension, neighborhood=neighborhood, state_class=state_class)
|
||||||
cells = ca_factory.make_cellular_automaton(dimension=dimension, neighborhood=neighborhood, state_class=state_class)
|
|
||||||
return CellularAutomaton(cells, dimension, rule)
|
return CellularAutomaton(cells, dimension, rule)
|
||||||
|
|
||||||
|
|
||||||
|
BIN
scripts/performance_test
Normal file
BIN
scripts/performance_test
Normal file
Binary file not shown.
@ -0,0 +1,7 @@
|
|||||||
|
from .ca_cell import *
|
||||||
|
from .ca_cell_state import *
|
||||||
|
from .ca_display import *
|
||||||
|
from .ca_factory import *
|
||||||
|
from .ca_neighborhood import *
|
||||||
|
from .ca_rule import *
|
||||||
|
from .cellular_automaton import *
|
@ -3,34 +3,34 @@ from typing import Type
|
|||||||
|
|
||||||
|
|
||||||
class Cell:
|
class Cell:
|
||||||
def __init__(self, state_class: Type[CellState], coordinate: list):
|
def __init__(self, state_class: Type[CellState], coordinate):
|
||||||
self._coordinate = coordinate
|
self._coordinate = coordinate
|
||||||
self._state = state_class()
|
self.state = state_class()
|
||||||
self._neighbours = []
|
self.neighbours = []
|
||||||
|
|
||||||
def set_neighbours(self, neighbours):
|
def set_neighbours(self, neighbours):
|
||||||
self._neighbours = neighbours
|
self.neighbours = neighbours
|
||||||
|
|
||||||
def get_state(self):
|
def get_state(self):
|
||||||
return self._state
|
return self.state
|
||||||
|
|
||||||
def get_coordinate(self):
|
def get_coordinate(self):
|
||||||
return self._coordinate
|
return self._coordinate
|
||||||
|
|
||||||
def evolve_if_ready(self, rule, iteration):
|
def evolve_if_ready(self, rule, iteration):
|
||||||
if self._state.is_active(iteration):
|
if self.state.is_active(iteration):
|
||||||
new_state = rule(self._state.get_state_of_last_iteration(iteration), self.get_neighbour_states(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)
|
self.set_new_state_and_activate(new_state, iteration)
|
||||||
|
|
||||||
def get_neighbour_states(self, index):
|
def get_neighbour_states(self, index):
|
||||||
return [n.get_state_of_last_iteration(index) for n in self._neighbours]
|
return [n.get_state_of_last_iteration(index) for n in self.neighbours]
|
||||||
|
|
||||||
def set_new_state_and_activate(self, new_state: CellState, iteration):
|
def set_new_state_and_activate(self, new_state: CellState, iteration):
|
||||||
changed = self._state.set_state_of_iteration(new_state, iteration)
|
changed = self.state.set_state_of_iteration(new_state, iteration)
|
||||||
if changed:
|
if changed:
|
||||||
self._set_active_for_next_iteration(iteration)
|
self._set_active_for_next_iteration(iteration)
|
||||||
|
|
||||||
def _set_active_for_next_iteration(self, iteration):
|
def _set_active_for_next_iteration(self, iteration):
|
||||||
self._state.set_active_for_next_iteration(iteration)
|
self.state.set_active_for_next_iteration(iteration)
|
||||||
for n in self._neighbours:
|
for n in self.neighbours:
|
||||||
n.set_active_for_next_iteration(iteration)
|
n.set_active_for_next_iteration(iteration)
|
||||||
|
@ -60,11 +60,12 @@ class PyGameFor2D:
|
|||||||
running = True
|
running = True
|
||||||
cellular_automaton_processor.evolve()
|
cellular_automaton_processor.evolve()
|
||||||
first = True
|
first = True
|
||||||
|
|
||||||
while running:
|
while running:
|
||||||
pygame.event.get()
|
pygame.event.get()
|
||||||
time_ca_start = time.time()
|
time_ca_start = time.time()
|
||||||
if first:
|
if first:
|
||||||
self._evolve_with_performance(cellular_automaton_processor, time_ca_start)
|
self._evolve_with_performance(cellular_automaton_processor)
|
||||||
first = False
|
first = False
|
||||||
else:
|
else:
|
||||||
cellular_automaton_processor.evolve()
|
cellular_automaton_processor.evolve()
|
||||||
@ -73,9 +74,11 @@ class PyGameFor2D:
|
|||||||
time_ds_end = time.time()
|
time_ds_end = time.time()
|
||||||
self._print_process_duration(time_ca_end, time_ca_start, time_ds_end)
|
self._print_process_duration(time_ca_end, time_ca_start, time_ds_end)
|
||||||
|
|
||||||
def _evolve_with_performance(self, cap, time_ca_start):
|
def _evolve_with_performance(self, cap):
|
||||||
size = asizeof.asizeof(self._cellular_automaton)
|
size = asizeof.asizeof(self._cellular_automaton)
|
||||||
|
time_ca_start = time.time()
|
||||||
cProfile.runctx("cap.evolve_x_times(10)", None, locals(), "performance_test")
|
cProfile.runctx("cap.evolve_x_times(10)", None, locals(), "performance_test")
|
||||||
|
time_ca_end = time.time()
|
||||||
print("PERFORMANCE")
|
print("PERFORMANCE")
|
||||||
p = pstats.Stats('performance_test')
|
p = pstats.Stats('performance_test')
|
||||||
p.strip_dirs()
|
p.strip_dirs()
|
||||||
@ -83,7 +86,6 @@ class PyGameFor2D:
|
|||||||
p.sort_stats('cumulative').print_stats(10)
|
p.sort_stats('cumulative').print_stats(10)
|
||||||
# sort by time spent in a function
|
# sort by time spent in a function
|
||||||
p.sort_stats('time').print_stats(10)
|
p.sort_stats('time').print_stats(10)
|
||||||
time_ca_end = time.time()
|
|
||||||
print("TOTAL TIME: " + "{0:.4f}".format(time_ca_end - time_ca_start) + "s")
|
print("TOTAL TIME: " + "{0:.4f}".format(time_ca_end - time_ca_start) + "s")
|
||||||
print("SIZE: " + "{0:.4f}".format(size / (1024 * 1024)) + "MB")
|
print("SIZE: " + "{0:.4f}".format(size / (1024 * 1024)) + "MB")
|
||||||
|
|
||||||
|
@ -1,61 +1,35 @@
|
|||||||
from cellular_automaton.ca_cell import Cell, CellState
|
from cellular_automaton.ca_cell import Cell, CellState
|
||||||
from cellular_automaton.ca_neighborhood import Neighborhood
|
from cellular_automaton.ca_neighborhood import Neighborhood
|
||||||
from typing import Type
|
from typing import Type
|
||||||
|
import itertools
|
||||||
|
|
||||||
|
|
||||||
class Factory:
|
class CAFactory:
|
||||||
def __init__(self):
|
@staticmethod
|
||||||
self._dimension = None
|
def make_cellular_automaton(dimension,
|
||||||
self._state_class = None
|
|
||||||
self._cells = {}
|
|
||||||
|
|
||||||
def make_cellular_automaton(self,
|
|
||||||
dimension,
|
|
||||||
neighborhood: Type[Neighborhood],
|
neighborhood: Type[Neighborhood],
|
||||||
state_class: Type[CellState]):
|
state_class: Type[CellState]):
|
||||||
self._dimension = dimension
|
|
||||||
self._state_class = state_class
|
|
||||||
|
|
||||||
self.__create_cells()
|
cells = CAFactory._make_cells(dimension, state_class)
|
||||||
self.__set_cell_neighbours(self._cells, neighborhood)
|
CAFactory._apply_neighbourhood_to_cells(cells, neighborhood, dimension)
|
||||||
return tuple(self._cells.values())
|
return tuple(cells.values())
|
||||||
|
|
||||||
def __create_cells(self, dimension_index=0, coordinate=None):
|
@staticmethod
|
||||||
""" Recursively steps down the dimensions to create cells in n dimensions and adds them to a dict.
|
def _make_cells(dimension, state_class):
|
||||||
:param dimension_index: The index indicating which dimension is currently traversed.
|
cells = {}
|
||||||
:param coordinate: The coordinate generated so far.
|
for c in itertools.product(*[range(d) for d in dimension]):
|
||||||
(each recursion adds one dimension to the coordinate.
|
coordinate_string = _join_coordinate(c)
|
||||||
"""
|
cells[coordinate_string] = Cell(state_class, c)
|
||||||
coordinate = _instantiate_coordinate_if_necessary(coordinate)
|
return cells
|
||||||
|
|
||||||
try:
|
@staticmethod
|
||||||
self.__recursive_step_down_dimensions(coordinate, dimension_index)
|
def _apply_neighbourhood_to_cells(cells, neighborhood, dimension):
|
||||||
except IndexError:
|
|
||||||
coordinate_string = _join_coordinate(coordinate)
|
|
||||||
self._cells[coordinate_string] = Cell(self._state_class, coordinate)
|
|
||||||
|
|
||||||
def __recursive_step_down_dimensions(self, coordinate, dimension_index):
|
|
||||||
""" For the range of the current dimension, recalls the recursion method.
|
|
||||||
:param coordinate: The coordinate so far.
|
|
||||||
:param dimension_index: The current dimension lvl.
|
|
||||||
"""
|
|
||||||
for cell_index in range(self._dimension[dimension_index]):
|
|
||||||
new_cod = coordinate + [cell_index]
|
|
||||||
self.__create_cells(dimension_index + 1, new_cod)
|
|
||||||
|
|
||||||
def __set_cell_neighbours(self, cells, neighborhood):
|
|
||||||
for cell in cells.values():
|
for cell in cells.values():
|
||||||
n_coordinates = neighborhood.calculate_cell_neighbor_coordinates(cell.get_coordinate(),
|
n_coordinates = neighborhood.calculate_cell_neighbor_coordinates(cell.get_coordinate(),
|
||||||
self._dimension)
|
dimension)
|
||||||
cell.set_neighbours([cells[_join_coordinate(coordinate)].get_state() for coordinate in n_coordinates])
|
cell.set_neighbours([cells[_join_coordinate(coordinate)].get_state() for coordinate in n_coordinates])
|
||||||
|
|
||||||
|
|
||||||
def _instantiate_coordinate_if_necessary(coordinate):
|
|
||||||
if coordinate is None:
|
|
||||||
coordinate = []
|
|
||||||
return coordinate
|
|
||||||
|
|
||||||
|
|
||||||
def _join_coordinate(coordinate):
|
def _join_coordinate(coordinate):
|
||||||
return '-'.join(str(x) for x in coordinate)
|
return '-'.join(str(x) for x in coordinate)
|
||||||
|
|
||||||
|
@ -14,10 +14,11 @@ class CellularAutomaton:
|
|||||||
class CellularAutomatonProcessor:
|
class CellularAutomatonProcessor:
|
||||||
def __init__(self, cellular_automaton, process_count: int = 1):
|
def __init__(self, cellular_automaton, process_count: int = 1):
|
||||||
self.ca = cellular_automaton
|
self.ca = cellular_automaton
|
||||||
|
cells = {i: self.ca.cells[i] for i in range(len(self.ca.cells))}
|
||||||
self.evolve_range = range(len(self.ca.cells))
|
self.evolve_range = range(len(self.ca.cells))
|
||||||
self.pool = multiprocessing.Pool(processes=process_count,
|
self.pool = multiprocessing.Pool(processes=process_count,
|
||||||
initializer=_init_process,
|
initializer=_init_process,
|
||||||
initargs=(self.ca.cells,
|
initargs=(cells,
|
||||||
self.ca.evolution_rule,
|
self.ca.evolution_rule,
|
||||||
self.ca.evolution_iteration_index))
|
self.ca.evolution_iteration_index))
|
||||||
for cell in self.ca.cells:
|
for cell in self.ca.cells:
|
||||||
|
50
test/test_factory.py
Normal file
50
test/test_factory.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import sys
|
||||||
|
sys.path.append('../src')
|
||||||
|
|
||||||
|
from cellular_automaton import *
|
||||||
|
import unittest
|
||||||
|
import mock
|
||||||
|
|
||||||
|
|
||||||
|
class TestFac(CAFactory):
|
||||||
|
@staticmethod
|
||||||
|
def make_cells(dimension, state_class):
|
||||||
|
return CAFactory._make_cells(dimension, state_class)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def apply_neighbourhood(cells, neighborhood, dimension):
|
||||||
|
return CAFactory._apply_neighbourhood_to_cells(cells, neighborhood, dimension)
|
||||||
|
|
||||||
|
|
||||||
|
class TestCAFactory(unittest.TestCase):
|
||||||
|
def test_make_ca_calls_correct_methods(self):
|
||||||
|
with mock.patch.object(CAFactory, '_make_cells', return_value={1: True}) as m1:
|
||||||
|
with mock.patch.object(CAFactory, '_apply_neighbourhood_to_cells') as m2:
|
||||||
|
CAFactory.make_cellular_automaton([10], Neighborhood, CellState)
|
||||||
|
m1.assert_called_once_with([10], CellState)
|
||||||
|
m2.assert_called_once_with({1: True}, Neighborhood, [10])
|
||||||
|
|
||||||
|
def test_make_ca_returns_correct_values(self):
|
||||||
|
with mock.patch.object(CAFactory, '_make_cells', return_value={1: True}):
|
||||||
|
with mock.patch.object(CAFactory, '_apply_neighbourhood_to_cells'):
|
||||||
|
cells = CAFactory.make_cellular_automaton([10], Neighborhood, CellState)
|
||||||
|
self.assertEqual(cells, (True, ))
|
||||||
|
|
||||||
|
def test_1dimension_coordinates(self):
|
||||||
|
fac = TestFac()
|
||||||
|
c = fac.make_cells([3], CellState)
|
||||||
|
self.assertEqual(list(c.keys()), ['0', '1', '2'])
|
||||||
|
|
||||||
|
def test_2dimension_coordinates(self):
|
||||||
|
fac = TestFac()
|
||||||
|
c = fac.make_cells([2, 2], CellState)
|
||||||
|
self.assertEqual(list(c.keys()), ['0-0', '0-1', '1-0', '1-1'])
|
||||||
|
|
||||||
|
def test_3dimension_coordinates(self):
|
||||||
|
fac = TestFac()
|
||||||
|
c = fac.make_cells([2, 2, 2], CellState)
|
||||||
|
self.assertEqual(list(c.keys()), ['0-0-0', '0-0-1', '0-1-0', '0-1-1', '1-0-0', '1-0-1', '1-1-0', '1-1-1'])
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
@ -5,8 +5,9 @@ import cellular_automaton.ca_neighborhood as csn
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
class TestCellState(unittest.TestCase):
|
class TestNeighborhood(unittest.TestCase):
|
||||||
def check_neighbors(self, neighborhood, neighborhood_sets):
|
@staticmethod
|
||||||
|
def check_neighbors(neighborhood, neighborhood_sets):
|
||||||
for neighborhood_set in neighborhood_sets:
|
for neighborhood_set in neighborhood_sets:
|
||||||
neighbors = neighborhood.calculate_cell_neighbor_coordinates(neighborhood_set[0], [3, 3])
|
neighbors = neighborhood.calculate_cell_neighbor_coordinates(neighborhood_set[0], [3, 3])
|
||||||
if neighborhood_set[1] != neighbors:
|
if neighborhood_set[1] != neighbors:
|
||||||
|
Loading…
Reference in New Issue
Block a user