Insert Neuropercolation(+Coupled) class
This commit is contained in:
parent
afea084886
commit
6b0af5ba4d
26
examples/just a test.py
Normal file
26
examples/just a test.py
Normal file
@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Wed Aug 16 22:17:28 2023
|
||||
|
||||
@author: astral
|
||||
"""
|
||||
import pygame
|
||||
pygame.init()
|
||||
win = pygame.display.set_mode((500, 500))
|
||||
pygame.display.set_caption("Test")
|
||||
black = (0, 0, 0)
|
||||
sysfont = pygame.font.SysFont(None, 50)
|
||||
win.fill((255, 255, 255))
|
||||
|
||||
running = True
|
||||
while running:
|
||||
win.fill((255, 255, 255))
|
||||
digit = sysfont.render("Test", 1, black)
|
||||
win.blit(digit, (50, 50))
|
||||
pygame.display.update()
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
running = False
|
||||
|
||||
pygame.quit()
|
@ -17,5 +17,5 @@ limitations under the License.
|
||||
|
||||
from .neighborhood import Neighborhood, MooreNeighborhood, RadialNeighborhood, VonNeumannNeighborhood, \
|
||||
HexagonalNeighborhood, EdgeRule
|
||||
from .automaton import CellularAutomaton
|
||||
from .automaton import Neuropercolation, NeuropercolationCoupled
|
||||
from .display import CAWindow
|
||||
|
@ -19,9 +19,12 @@ from typing import Sequence
|
||||
import abc
|
||||
import itertools
|
||||
import recordclass
|
||||
import random
|
||||
|
||||
from cellular_automaton import Neighborhood
|
||||
from cellular_automaton import Neighborhood, VonNeumannNeighborhood, EdgeRule
|
||||
|
||||
ALIVE = [1]
|
||||
DEAD = [0]
|
||||
|
||||
CELL = recordclass.make_dataclass("Cell",
|
||||
("state", "is_active", "is_dirty", "neighbors"),
|
||||
@ -42,6 +45,7 @@ class CellularAutomatonCreator(abc.ABC):
|
||||
self._current_state = {}
|
||||
self._next_state = {}
|
||||
self.__make_cellular_automaton_state()
|
||||
self.dumped = False
|
||||
|
||||
def get_dimension(self):
|
||||
return self._dimension
|
||||
@ -53,7 +57,7 @@ class CellularAutomatonCreator(abc.ABC):
|
||||
self.__add_neighbors()
|
||||
|
||||
def __make_cells(self):
|
||||
for coord in itertools.product(*[range(d) for d in self._dimension]):
|
||||
for coord in itertools.product(*[range(d) for d in [*self._dimension]]): # doubled the layers in thrid dimension
|
||||
cell_state = self.init_cell_state(coord)
|
||||
self._current_state[coord] = CELL(cell_state)
|
||||
self._next_state[coord] = CELL(cell_state)
|
||||
@ -62,9 +66,10 @@ class CellularAutomatonCreator(abc.ABC):
|
||||
calculate_neighbor_coordinates = self._neighborhood.calculate_cell_neighbor_coordinates
|
||||
coordinates = self._current_state.keys()
|
||||
for coordinate, cell_c, cell_n in zip(coordinates, self._current_state.values(), self._next_state.values()):
|
||||
n_coord = calculate_neighbor_coordinates(coordinate, self._dimension)
|
||||
cell_c.neighbors = list([self._current_state[nc] for nc in n_coord])
|
||||
cell_n.neighbors = list([self._next_state[nc] for nc in n_coord])
|
||||
pre_coord = calculate_neighbor_coordinates(coordinate, [*self._dimension[:2]])
|
||||
n_coord = tuple([(*coord,*coordinate[2:]) for coord in pre_coord])
|
||||
cell_c.neighbors = list([self._current_state[nc] for nc in n_coord if nc!=coordinate])
|
||||
cell_n.neighbors = list([self._next_state[nc] for nc in n_coord if nc!=coordinate])
|
||||
|
||||
def init_cell_state(self, cell_coordinate: Sequence) -> Sequence: # pragma: no cover
|
||||
""" Will be called to initialize a cells state.
|
||||
@ -74,35 +79,17 @@ class CellularAutomatonCreator(abc.ABC):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class CellularAutomaton(CellularAutomatonCreator, abc.ABC):
|
||||
""" This class represents a cellular automaton.
|
||||
It can be created with n dimensions and can handle different neighborhood definitions.
|
||||
class Neuropercolation(CellularAutomatonCreator, abc.ABC):
|
||||
""" Cellular automaton with the evolution rules of conways game of life """
|
||||
|
||||
It is intended to be uses as base class.
|
||||
Override `init_cell_state()` to define the state the cell(s) are initiated with.
|
||||
Override `evolve()` to define the rule that is aplied on every evolution step of this automaton.
|
||||
"""
|
||||
def __init__(self, neighborhood: Neighborhood, *args, **kwargs):
|
||||
""" Initiates a cellular automaton by the use of the `init_cell_state` method.
|
||||
:param neighborhood: Defines which cells are considered neighbors.
|
||||
:param dimension: Iterable of len = dimensions
|
||||
(e.g. [4, 3, 3, 3] = 4 x 3 x 3 x 3 cells in a four dimensional cube).
|
||||
"""
|
||||
super().__init__(neighborhood=neighborhood, *args, **kwargs)
|
||||
def __init__(self, dim, eps, *args, **kwargs):
|
||||
super().__init__(dimension=[dim, dim, 2],
|
||||
neighborhood=VonNeumannNeighborhood(EdgeRule.FIRST_AND_LAST_CELL_OF_DIMENSION_ARE_NEIGHBORS))
|
||||
self._evolution_step = 0
|
||||
self._active = True
|
||||
self.epsilon = eps
|
||||
|
||||
def is_active(self):
|
||||
return self._active
|
||||
|
||||
def reactivate(self):
|
||||
""" Sets all cells active again """
|
||||
for cell in self._current_state.values():
|
||||
cell.is_active = True
|
||||
cell.is_dirty = True
|
||||
self._active = True
|
||||
|
||||
active = property(is_active)
|
||||
def init_cell_state(self, coord): # pylint: disable=no-self-use
|
||||
return DEAD
|
||||
|
||||
def get_cells(self):
|
||||
return self._current_state
|
||||
@ -126,7 +113,6 @@ class CellularAutomaton(CellularAutomatonCreator, abc.ABC):
|
||||
:param times: The number of evolution steps processed with one call of this method.
|
||||
"""
|
||||
for _ in itertools.repeat(None, times):
|
||||
self._active = False
|
||||
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
|
||||
@ -134,10 +120,11 @@ class CellularAutomaton(CellularAutomatonCreator, abc.ABC):
|
||||
def __evolve_cells(self, this_state, next_state):
|
||||
evolve_cell = self.__evolve_cell
|
||||
evolution_rule = self.evolve_rule
|
||||
for old, new in zip(this_state.values(), next_state.values()):
|
||||
if old.is_active:
|
||||
new_state = evolution_rule(old.state.copy(), [n.state for n in old.neighbors])
|
||||
old.is_active = False
|
||||
for coord, old, new in zip(this_state.keys(), this_state.values(), next_state.values()):
|
||||
coord_c = tuple([*coord[:2],int(1-coord[2])])
|
||||
old_c = this_state[coord_c]
|
||||
new_state = evolution_rule(old.state.copy(), old_c.state.copy(), [n.state for n in old.neighbors], coord[2], coord_c[2]) #inverse the inhibitory layer's action
|
||||
|
||||
evolve_cell(old, new, new_state)
|
||||
|
||||
def __evolve_cell(self, old, cell, new_state):
|
||||
@ -145,17 +132,110 @@ class CellularAutomaton(CellularAutomatonCreator, abc.ABC):
|
||||
cell.state = new_state
|
||||
cell.is_dirty |= changed
|
||||
old.is_dirty |= changed
|
||||
self._active |= changed
|
||||
if changed:
|
||||
cell.is_active = True
|
||||
for n in cell.neighbors:
|
||||
n.is_active = True
|
||||
|
||||
def evolve_rule(self, last_cell_state: Sequence, neighbors_last_states: Sequence) -> Sequence: # pragma: no cover
|
||||
""" Calculates and sets new state of 'cell'.
|
||||
A cells evolution will only be called if it or at least one of its neighbors has changed last evolution_step.
|
||||
:param last_cell_state: The cells state previous to the evolution step.
|
||||
:param neighbors_last_states: The cells neighbors current states.
|
||||
:return: New state. The state after this evolution step
|
||||
def evolve_rule(self, last_cell_state, link_last_state, neighbors_last_states, cell_lay, link_cell_lay):
|
||||
new_cell_state = last_cell_state
|
||||
if link_cell_lay==0:
|
||||
alive_neighbours = self.__count_alive_neighbours(neighbors_last_states)+link_last_state[0] # adjust for excitatory link cells
|
||||
else:
|
||||
alive_neighbours = self.__count_alive_neighbours(neighbors_last_states)+(1-link_last_state[0]) # adjust for inhibitory link cells
|
||||
|
||||
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
|
||||
|
||||
@staticmethod
|
||||
def __count_alive_neighbours(neighbours):
|
||||
alive_neighbors = []
|
||||
for n in neighbours:
|
||||
if n == ALIVE:
|
||||
alive_neighbors.append(1)
|
||||
return len(alive_neighbors)
|
||||
|
||||
class NeuropercolationCoupled(CellularAutomatonCreator, abc.ABC):
|
||||
|
||||
def __init__(self, dim, eps, coupling=[], *args, **kwargs):
|
||||
super().__init__(dimension=[dim, dim, 2, 2],
|
||||
neighborhood=VonNeumannNeighborhood(EdgeRule.FIRST_AND_LAST_CELL_OF_DIMENSION_ARE_NEIGHBORS))
|
||||
self._evolution_step = 0
|
||||
self.epsilon = eps
|
||||
self.coupling = coupling
|
||||
|
||||
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.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
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): #this will be overwritten by heir
|
||||
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()):
|
||||
if coord[:2] in self.coupling:
|
||||
coord_c = tuple([*coord[:2],coord[2],int(1-coord[3])])
|
||||
old_c = this_state[coord_c]
|
||||
new_state = evolution_rule(old.state.copy(), old_c.state.copy(), [n.state for n in old.neighbors], 0)
|
||||
else:
|
||||
coord_c = tuple([*coord[:2],int(1-coord[2]),coord[3]])
|
||||
old_c = this_state[coord_c]
|
||||
new_state = evolution_rule(old.state.copy(), old_c.state.copy(), [n.state for n in old.neighbors], coord_c[2]) #inverse the inhibitory layer's action
|
||||
|
||||
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, link_last_state, neighbors_last_states, other_layer):
|
||||
new_cell_state = last_cell_state
|
||||
if other_layer==0:
|
||||
alive_neighbours = self.__count_alive_neighbours(neighbors_last_states)+link_last_state[0] # adjust for excitatory link cells
|
||||
else:
|
||||
alive_neighbours = self.__count_alive_neighbours(neighbors_last_states)+(1-link_last_state[0]) # adjust for inhibitory link cells
|
||||
|
||||
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
|
@ -22,7 +22,7 @@ import collections
|
||||
import contextlib
|
||||
from typing import Sequence
|
||||
|
||||
from . import CellularAutomaton
|
||||
from . import Neuropercolation, NeuropercolationCoupled
|
||||
|
||||
_Rect = collections.namedtuple(typename="Rect",
|
||||
field_names=["left", "top", "width", "height"])
|
||||
@ -65,7 +65,7 @@ class PygameEngine:
|
||||
|
||||
class CAWindow:
|
||||
def __init__(self,
|
||||
cellular_automaton: CellularAutomaton,
|
||||
cellular_automaton: Neuropercolation,
|
||||
window_size=(1000, 800),
|
||||
stretch_cells=False,
|
||||
draw_engine=None,
|
||||
@ -117,8 +117,7 @@ class CAWindow:
|
||||
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) \
|
||||
and self._cellular_automaton.active
|
||||
return (self._cellular_automaton.evolution_step < last_evolution_step or last_evolution_step <= 0)
|
||||
|
||||
def __calculate_cell_display_size(self, stretch_cells): # pragma: no cover
|
||||
grid_dimension = self._cellular_automaton.dimension
|
||||
|
Loading…
Reference in New Issue
Block a user