Add Simulate1Layer

This commit is contained in:
timofej 2023-08-17 23:45:53 +02:00
parent 47d22e59ac
commit 7d5f579a34
3 changed files with 195 additions and 4 deletions

View File

@ -17,5 +17,5 @@ limitations under the License.
from .neighborhood import Neighborhood, MooreNeighborhood, RadialNeighborhood, VonNeumannNeighborhood, \ from .neighborhood import Neighborhood, MooreNeighborhood, RadialNeighborhood, VonNeumannNeighborhood, \
HexagonalNeighborhood, EdgeRule HexagonalNeighborhood, EdgeRule
from .automaton import Neuropercolation, NeuropercolationCoupled from .automaton import Neurolattice, Neuropercolation, NeuropercolationCoupled
from .display import Simulate2Layers, Simulate4Layers from .display import Simulate1Layer, Simulate2Layers, Simulate4Layers

View File

@ -79,6 +79,74 @@ class CellularAutomatonCreator(abc.ABC):
raise NotImplementedError raise NotImplementedError
class Neurolattice(CellularAutomatonCreator, abc.ABC):
""" Cellular automaton with the evolution rules of conways game of life """
def __init__(self, dim, eps, *args, **kwargs):
super().__init__(dimension=[dim, dim],
neighborhood=VonNeumannNeighborhood(EdgeRule.FIRST_AND_LAST_CELL_OF_DIMENSION_ARE_NEIGHBORS))
self._evolution_step = 0
self.epsilon = eps
def init_cell_state(self, coord): # pylint: disable=no-self-use
return DEAD
def get_cells(self):
return self._current_state
def set_cells(self, cells):
""" Sets the cell states both as current and next states """
for (coordinate, c_cell), n_cell in zip(self._current_state.items(), self._next_state.values()):
new_cell_state = cells[coordinate].state
c_cell.state = new_cell_state
n_cell.state = new_cell_state
cells = property(get_cells, set_cells)
def get_evolution_step(self):
return self._evolution_step
evolution_step = property(get_evolution_step)
def evolve(self, times=1):
""" Evolve all cells x times.
:param times: The number of evolution steps processed with one call of this method.
"""
for _ in itertools.repeat(None, times):
self.__evolve_cells(self._current_state, self._next_state)
self._current_state, self._next_state = self._next_state, self._current_state
self._evolution_step += 1
def __evolve_cells(self, this_state, next_state):
evolve_cell = self.__evolve_cell
evolution_rule = self.evolve_rule
for coord, old, new in zip(this_state.keys(), this_state.values(), next_state.values()):
new_state = evolution_rule(old.state.copy(), [n.state[0] for n in old.neighbors])
evolve_cell(old, new, new_state)
def __evolve_cell(self, old, cell, new_state):
changed = new_state != old.state
cell.state = new_state
cell.is_dirty |= changed
old.is_dirty |= changed
def evolve_rule(self, last_cell_state, neighbors_last_states):
new_cell_state = last_cell_state
alive_neighbours = sum(neighbors_last_states)+last_cell_state[0]
CASE = (random.random()>=self.epsilon)
if alive_neighbours > 2:
if CASE:
new_cell_state = ALIVE
else:
new_cell_state = DEAD
else:
if CASE:
new_cell_state = DEAD
else:
new_cell_state = ALIVE
return new_cell_state
class Neuropercolation(CellularAutomatonCreator, abc.ABC): class Neuropercolation(CellularAutomatonCreator, abc.ABC):
""" Cellular automaton with the evolution rules of conways game of life """ """ Cellular automaton with the evolution rules of conways game of life """

View File

@ -18,14 +18,13 @@ limitations under the License.
import os import os
import json import json
import pbjson
import time import time
import operator import operator
import collections import collections
import contextlib import contextlib
from typing import Sequence from typing import Sequence
from . import Neuropercolation, NeuropercolationCoupled from . import Neurolattice, Neuropercolation, NeuropercolationCoupled
_Rect = collections.namedtuple(typename="Rect", _Rect = collections.namedtuple(typename="Rect",
field_names=["left", "top", "width", "height"]) field_names=["left", "top", "width", "height"])
@ -66,6 +65,130 @@ class PygameEngine:
return True return True
class Simulate1Layer:
def __init__(self,
dim,
eps,
res=4,
draw='pygame',
path='/cloud/Public/_data/neuropercolation/test/',
state_to_color_cb=None,
*args, **kwargs):
"""
Creates a window to render a 2D CellularAutomaton.
:param cellular_automaton: The automaton to display and evolve
:param window_size: The Window size (default: 1000 x 800)
:param stretch_cells: Stretches cells to fit into window size. (default: false)
Activating it can result in black lines throughout the automaton.
:param draw_engine: The draw_engine (default: pygame)
:param state_to_color_cb: A callback to define the draw color of CA states (default: red for states != 0)
"""
super().__init__(*args, **kwargs)
self._cellular_automaton = Neurolattice(dim,eps)
self.__cell_size = [res,res]
self.__dimension = dim
self.__epsilon = eps
self.__gridside = dim*res
self.__rect = _Rect(left=0, top=30, width=dim*res, height=dim*res)
self.__draw_engine = PygameEngine((self.__rect.width,self.__rect.height+self.__rect.top)) if draw == 'pygame' else draw
self.__state_to_color = self._get_cell_color if state_to_color_cb is None else state_to_color_cb
self.__path = path
self.__state_list = []
def run(self,
evolutions_per_second=0,
evolutions_per_draw=1,
last_evolution_step=0,):
"""
Evolves and draws the CellularAutomaton
:param evolutions_per_second: 0 = as fast as possible | > 0 to slow down the CellularAutomaton
:param evolutions_per_draw: Amount of evolutions done before screen gets redrawn.
:param last_evolution_step: 0 = infinite | > 0 evolution step at which this method will stop
Warning: is blocking until finished
"""
self._append_state()
with contextlib.suppress(KeyboardInterrupt):
while self._is_not_user_terminated() and self._not_at_the_end(last_evolution_step):
time_ca_start = time.time()
self._cellular_automaton.evolve(evolutions_per_draw)
self._append_state()
time_ca_end = time.time()
self._redraw_dirty_cells()
time_ds_end = time.time()
self.print_process_info(evolve_duration=(time_ca_end - time_ca_start),
draw_duration=(time_ds_end - time_ca_end),
evolution_step=self._cellular_automaton.evolution_step,
runlendig=len(str(last_evolution_step)))
self._sleep_to_keep_rate(time.time() - time_ca_start, evolutions_per_second)
try:
self.__draw_engine._pygame.quit()
except:
print('Failed to quit pygame')
self._save_state_list()
def _append_state(self):
automaton_state = [[0 for n in range(self.__dimension)] for m in range(self.__dimension)]
for coord, cell in self._cellular_automaton._current_state.items():
x,y = coord
automaton_state[y][x] = int(cell.state[0])
automaton_state = [''.join(str(cells) for cells in rows) for rows in automaton_state]
automaton_state = '.'.join(str(rows) for rows in automaton_state)
self.__state_list.append(automaton_state)
def _save_state_list(self):
if not os.path.exists(self.__path):
os.makedirs(self.__path)
with open(self.__path+f"eps={round(self.__epsilon,3):.3f}_states.txt", 'w', encoding='utf-8') as f:
json.dump(self.__state_list, f, indent=1)
def _sleep_to_keep_rate(self, time_taken, evolutions_per_second): # pragma: no cover
if evolutions_per_second > 0:
rest_time = 1.0 / evolutions_per_second - time_taken
if rest_time > 0:
time.sleep(rest_time)
def _not_at_the_end(self, last_evolution_step):
return (self._cellular_automaton.evolution_step < last_evolution_step or last_evolution_step <= 0)
def _redraw_dirty_cells(self):
self.__draw_engine.update_rectangles(list(self.__redraw_dirty_cells()))
def __redraw_dirty_cells(self):
for coordinate, cell in self._cellular_automaton.cells.items():
if cell.is_dirty:
yield self.__redraw_cell(cell, coordinate)
def __redraw_cell(self, cell, coordinate):
cell_color = self.__state_to_color(cell.state)
cell_pos = self.__calculate_cell_position_in_the_grid(coordinate)
surface_pos = self.__calculate_cell_position_on_screen(cell_pos)
cell.is_dirty = False
return self.__draw_cell_surface(surface_pos, cell_color)
def _get_cell_color(self, current_state: Sequence) -> Sequence:
""" Returns the color of the cell depending on its current state """
return 255 if current_state[0] else 0, 0, 0
def __calculate_cell_position_in_the_grid(self, coord):
return list(map(operator.mul,
self.__cell_size,
coord[:2]))
def __calculate_cell_position_on_screen(self, cell_pos):
return [self.__rect.left + cell_pos[0], self.__rect.top + cell_pos[1]]
def __draw_cell_surface(self, surface_pos, cell_color):
return self.__draw_engine.fill_surface_with_color((surface_pos, self.__cell_size), cell_color)
def print_process_info(self, evolve_duration, draw_duration, evolution_step, runlendig):
self.__draw_engine.fill_surface_with_color(((0, 0), (self.__rect.width, 30)))
self.__draw_engine.write_text((0, 5), f'Step: {evolution_step:>{runlendig}} FPS: {int(1/(evolve_duration+draw_duration))}')
def _is_not_user_terminated(self):
return self.__draw_engine.is_active()
class Simulate2Layers: class Simulate2Layers:
def __init__(self, def __init__(self,
dim, dim,