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, \
|
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
|
||||||
|
@ -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
|
||||||
@ -53,7 +57,7 @@ class CellularAutomatonCreator(abc.ABC):
|
|||||||
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)
|
||||||
@ -62,9 +66,10 @@ class CellularAutomatonCreator(abc.ABC):
|
|||||||
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.
|
|
||||||
|
|
||||||
It is intended to be uses as base class.
|
def __init__(self, dim, eps, *args, **kwargs):
|
||||||
Override `init_cell_state()` to define the state the cell(s) are initiated with.
|
super().__init__(dimension=[dim, dim, 2],
|
||||||
Override `evolve()` to define the rule that is aplied on every evolution step of this automaton.
|
neighborhood=VonNeumannNeighborhood(EdgeRule.FIRST_AND_LAST_CELL_OF_DIMENSION_ARE_NEIGHBORS))
|
||||||
"""
|
|
||||||
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:
|
|
||||||
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
|
def evolve_rule(self, last_cell_state, link_last_state, neighbors_last_states, cell_lay, link_cell_lay):
|
||||||
""" Calculates and sets new state of 'cell'.
|
new_cell_state = last_cell_state
|
||||||
A cells evolution will only be called if it or at least one of its neighbors has changed last evolution_step.
|
if link_cell_lay==0:
|
||||||
:param last_cell_state: The cells state previous to the evolution step.
|
alive_neighbours = self.__count_alive_neighbours(neighbors_last_states)+link_last_state[0] # adjust for excitatory link cells
|
||||||
:param neighbors_last_states: The cells neighbors current states.
|
else:
|
||||||
:return: New state. The state after this evolution step
|
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
|
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
|
||||||
|
Loading…
Reference in New Issue
Block a user