Insert Neuropercolation(+Coupled) class

This commit is contained in:
timofej 2023-08-16 22:35:36 +02:00
parent afea084886
commit 6b0af5ba4d
4 changed files with 163 additions and 58 deletions

26
examples/just a test.py Normal file
View 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()

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 CellularAutomaton from .automaton import Neuropercolation, NeuropercolationCoupled
from .display import CAWindow from .display import CAWindow

View File

@ -19,9 +19,12 @@ from typing import Sequence
import abc import abc
import itertools import itertools
import recordclass 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", CELL = recordclass.make_dataclass("Cell",
("state", "is_active", "is_dirty", "neighbors"), ("state", "is_active", "is_dirty", "neighbors"),
@ -42,6 +45,7 @@ class CellularAutomatonCreator(abc.ABC):
self._current_state = {} self._current_state = {}
self._next_state = {} self._next_state = {}
self.__make_cellular_automaton_state() self.__make_cellular_automaton_state()
self.dumped = False
def get_dimension(self): def get_dimension(self):
return self._dimension return self._dimension
@ -49,22 +53,23 @@ class CellularAutomatonCreator(abc.ABC):
dimension = property(get_dimension) dimension = property(get_dimension)
def __make_cellular_automaton_state(self): def __make_cellular_automaton_state(self):
self.__make_cells() self.__make_cells()
self.__add_neighbors() self.__add_neighbors()
def __make_cells(self): 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) cell_state = self.init_cell_state(coord)
self._current_state[coord] = CELL(cell_state) self._current_state[coord] = CELL(cell_state)
self._next_state[coord] = CELL(cell_state) self._next_state[coord] = CELL(cell_state)
def __add_neighbors(self): def __add_neighbors(self):
calculate_neighbor_coordinates = self._neighborhood.calculate_cell_neighbor_coordinates calculate_neighbor_coordinates = self._neighborhood.calculate_cell_neighbor_coordinates
coordinates = self._current_state.keys() coordinates = self._current_state.keys()
for coordinate, cell_c, cell_n in zip(coordinates, self._current_state.values(), self._next_state.values()): 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) pre_coord = calculate_neighbor_coordinates(coordinate, [*self._dimension[:2]])
cell_c.neighbors = list([self._current_state[nc] for nc in n_coord]) n_coord = tuple([(*coord,*coordinate[2:]) for coord in pre_coord])
cell_n.neighbors = list([self._next_state[nc] for nc in n_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 def init_cell_state(self, cell_coordinate: Sequence) -> Sequence: # pragma: no cover
""" Will be called to initialize a cells state. """ Will be called to initialize a cells state.
@ -74,35 +79,17 @@ class CellularAutomatonCreator(abc.ABC):
raise NotImplementedError raise NotImplementedError
class CellularAutomaton(CellularAutomatonCreator, abc.ABC): class Neuropercolation(CellularAutomatonCreator, abc.ABC):
""" This class represents a cellular automaton. """ Cellular automaton with the evolution rules of conways game of life """
It can be created with n dimensions and can handle different neighborhood definitions.
def __init__(self, dim, eps, *args, **kwargs):
It is intended to be uses as base class. super().__init__(dimension=[dim, dim, 2],
Override `init_cell_state()` to define the state the cell(s) are initiated with. neighborhood=VonNeumannNeighborhood(EdgeRule.FIRST_AND_LAST_CELL_OF_DIMENSION_ARE_NEIGHBORS))
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)
self._evolution_step = 0 self._evolution_step = 0
self._active = True self.epsilon = eps
def is_active(self): def init_cell_state(self, coord): # pylint: disable=no-self-use
return self._active return DEAD
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 get_cells(self): def get_cells(self):
return self._current_state 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. :param times: The number of evolution steps processed with one call of this method.
""" """
for _ in itertools.repeat(None, times): for _ in itertools.repeat(None, times):
self._active = False
self.__evolve_cells(self._current_state, self._next_state) self.__evolve_cells(self._current_state, self._next_state)
self._current_state, self._next_state = self._next_state, self._current_state self._current_state, self._next_state = self._next_state, self._current_state
self._evolution_step += 1 self._evolution_step += 1
@ -134,28 +120,122 @@ class CellularAutomaton(CellularAutomatonCreator, abc.ABC):
def __evolve_cells(self, this_state, next_state): def __evolve_cells(self, this_state, next_state):
evolve_cell = self.__evolve_cell evolve_cell = self.__evolve_cell
evolution_rule = self.evolve_rule evolution_rule = self.evolve_rule
for old, new in zip(this_state.values(), next_state.values()): for coord, old, new in zip(this_state.keys(), this_state.values(), next_state.values()):
if old.is_active: coord_c = tuple([*coord[:2],int(1-coord[2])])
new_state = evolution_rule(old.state.copy(), [n.state for n in old.neighbors]) old_c = this_state[coord_c]
old.is_active = False 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)
evolve_cell(old, new, new_state)
def __evolve_cell(self, old, cell, new_state): def __evolve_cell(self, old, cell, new_state):
changed = new_state != old.state changed = new_state != old.state
cell.state = new_state cell.state = new_state
cell.is_dirty |= changed cell.is_dirty |= changed
old.is_dirty |= changed old.is_dirty |= changed
self._active |= changed
if changed: def evolve_rule(self, last_cell_state, link_last_state, neighbors_last_states, cell_lay, link_cell_lay):
cell.is_active = True new_cell_state = last_cell_state
for n in cell.neighbors: if link_cell_lay==0:
n.is_active = True 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 evolve_rule(self, last_cell_state: Sequence, neighbors_last_states: Sequence) -> Sequence: # pragma: no cover def set_cells(self, cells):
""" Calculates and sets new state of 'cell'. """ Sets the cell states both as current and next states """
A cells evolution will only be called if it or at least one of its neighbors has changed last evolution_step. for (coordinate, c_cell), n_cell in zip(self._current_state.items(), self._next_state.values()):
:param last_cell_state: The cells state previous to the evolution step. new_cell_state = cells[coordinate].state
:param neighbors_last_states: The cells neighbors current states. c_cell.state = new_cell_state
:return: New state. The state after this evolution step 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

View File

@ -22,7 +22,7 @@ import collections
import contextlib import contextlib
from typing import Sequence from typing import Sequence
from . import CellularAutomaton from . import Neuropercolation, NeuropercolationCoupled
_Rect = collections.namedtuple(typename="Rect", _Rect = collections.namedtuple(typename="Rect",
field_names=["left", "top", "width", "height"]) field_names=["left", "top", "width", "height"])
@ -65,7 +65,7 @@ class PygameEngine:
class CAWindow: class CAWindow:
def __init__(self, def __init__(self,
cellular_automaton: CellularAutomaton, cellular_automaton: Neuropercolation,
window_size=(1000, 800), window_size=(1000, 800),
stretch_cells=False, stretch_cells=False,
draw_engine=None, draw_engine=None,
@ -117,8 +117,7 @@ class CAWindow:
time.sleep(rest_time) time.sleep(rest_time)
def _not_at_the_end(self, last_evolution_step): def _not_at_the_end(self, last_evolution_step):
return (self._cellular_automaton.evolution_step < last_evolution_step or last_evolution_step <= 0) \ return (self._cellular_automaton.evolution_step < last_evolution_step or last_evolution_step <= 0)
and self._cellular_automaton.active
def __calculate_cell_display_size(self, stretch_cells): # pragma: no cover def __calculate_cell_display_size(self, stretch_cells): # pragma: no cover
grid_dimension = self._cellular_automaton.dimension grid_dimension = self._cellular_automaton.dimension