[PYTHON] Complètement compris le chapitre 3 de "Créer et déplacer ALife"

Création d'individu

Modèle SCL

Aperçu du modèle

――Il se compose de diverses molécules qui se déplacent dans un réseau bidimensionnel et de leurs formules de réaction chimique. --Il existe trois types de molécules dans la cellule en forme de grille --Substrat: cercle vert --Catalyseur: cercle violet

スクリーンショット 2019-12-13 10.56.11.png (69.9 kB)

――Chaque molécule se déplace entre les cellules et subit des réactions chimiques telles que la liaison et la décomposition avec d'autres molécules adjacentes. - 1) 2S + C → L + C --Une molécule de membrane (L) est générée par la molécule de catalyseur (C) à partir de deux molécules de substrat (S). - 2) L + L → L - L -La molécule de membrane générée (L) est fixée dans l'espace en se liant à la molécule de membrane adjacente (L). - 3) L → 2S -La molécule de membrane (L) est à nouveau décomposée en molécule de substrat (S) avec une certaine probabilité. -Dans le processus de réaction chimique, le film est formé et maintenu dans son ensemble.

Regardons comment les «individus» se forment et comment ils sont maintenus.

État de la formation individuelle

Auto-entretien

—— Les molécules de substrat se déplacent d'avant en arrière à l'intérieur et à l'extérieur de la membrane --Lorsque la molécule de substrat dans la membrane est convertie en molécule de membrane par la molécule de catalyseur, un lien est créé à l'intérieur de la membrane. ――Ce faisant, même si le lien faisant le film est démonté et qu'un trou est créé, le lien dans le film remplit le trou et répare le film cassé.

Implémentation du modèle

Le modèle SCL est implémenté comme une sorte d'automate cellulaire bidimensionnel. La cellule peut être dans les cinq états suivants.

-CATALYST (molécule de catalyseur) -SUBSTRAT (molécule de substrat) --LINK (molécule membranaire) --LINK-SUBSTRATE (molécule membranaire et molécule substrat coexistent) --HOLE (vide)

L'état de la cellule est décrit en Python comme suit. Les éléments autres que «type» seront décrits plus tard.

{ 'type': 'LINK', 'disintegrating_flag': False, 'bonds': [(6, 5, 8, 5)] }

Les réactions et liaisons moléculaires peuvent être exprimées par les six réactions suivantes. Les détails seront expliqués dans la partie calcul.

Les paramètres du modèle sont les suivants.

MOBILITY_FACTOR = {
    'HOLE':           0.1,
    'SUBSTRATE':      0.1,
    'CATALYST':       0.0001,
    'LINK':           0.05,
    'LINK_SUBSTRATE': 0.05,}
PRODUCTION_PROBABILITY             = 0.95
DISINTEGRATION_PROBABILITY         = 0.0005
BONDING_CHAIN_INITIATE_PROBABILITY = 0.1
BONDING_CHAIN_EXTEND_PROBABILITY   = 0.6
BONDING_CHAIN_SPLICE_PROBABILITY   = 0.9
BOND_DECAY_PROBABILITY             = 0.0005
ABSORPTION_PROBABILITY             = 0.5
EMISSION_PROBABILITY               = 0.5

«MOBILITY_FACTOR» est la facilité de mouvement de chaque molécule. «PROBABILITÉ» est la facilité avec laquelle chaque réaction se produit.

La mise en œuvre du modèle SCL peut être divisée en trois parties:

--1) Initialisation ―― 2) Mouvement des molécules ―― 3) Réaction moléculaire

Regardons-les dans l'ordre.

Initialisation

Dans la phase d'initialisation, un tableau bidimensionnel qui stocke les informations sur les cellules est préparé et les molécules sont disposées.

#Initialisation
particles = np.empty((SPACE_SIZE, SPACE_SIZE), dtype=object)
# INITIAL_SUBSTRATE_Placer le SUBSTRAT et le TROU selon la DENSITÉ.
for x in range(SPACE_SIZE):
    for y in range(SPACE_SIZE):
        if evaluate_probability(INITIAL_SUBSTRATE_DENSITY):
            p = {'type': 'SUBSTRATE', 'disintegrating_flag': False, 'bonds': []}
        else:
            p = {'type': 'HOLE', 'disintegrating_flag': False, 'bonds': []}
        particles[x,y] = p
# INITIAL_CATALYST_Placez CATALYSEUR en POSITIONS.
for x, y in INITIAL_CATALYST_POSITIONS:
    particles[x, y]['type'] = 'CATALYST'

Tout d'abord, nous avons préparé un tableau bidimensionnel de SPACE_SIZE x SPACE_SIZE.

particles = np.empty((SPACE_SIZE, SPACE_SIZE), dtype=object)
particles 
# => array([[None, None, None, None, None, None, None, None, None, None, None,
        None, None, None, None, None],
       [None, None, None, None, None, None, None, None, None, None, None,
        None, None, None, None, None],
       [None, None, None, None, None, None, None, None, None, None, None,
        None, None, None, None, None],
       [None, None, None, None, None, None, None, None, None, None, None,
        None, None, None, None, None],
       [None, None, None, None, None, None, None, None, None, None, None,
        None, None, None, None, None],
       [None, None, None, None, None, None, None, None, None, None, None,
        None, None, None, None, None],
       [None, None, None, None, None, None, None, None, None, None, None,
        None, None, None, None, None],
       [None, None, None, None, None, None, None, None, None, None, None,
        None, None, None, None, None],
       [None, None, None, None, None, None, None, None, None, None, None,
        None, None, None, None, None],
       [None, None, None, None, None, None, None, None, None, None, None,
        None, None, None, None, None],
       [None, None, None, None, None, None, None, None, None, None, None,
        None, None, None, None, None],
       [None, None, None, None, None, None, None, None, None, None, None,
        None, None, None, None, None],
       [None, None, None, None, None, None, None, None, None, None, None,
        None, None, None, None, None],
       [None, None, None, None, None, None, None, None, None, None, None,
        None, None, None, None, None],
       [None, None, None, None, None, None, None, None, None, None, None,
        None, None, None, None, None],
       [None, None, None, None, None, None, None, None, None, None, None,
        None, None, None, None, None]], dtype=object)

Ensuite, définissez le type sur SUBSTRAT ou HOLE avec une certaine probabilité pour la cellule d'observation. ʻEvaluate_probability (possibilité) `retourne TRUE ou FALSE avec une probabilité d'argument. Cela définit le type.

for x in range(SPACE_SIZE):
    for y in range(SPACE_SIZE):
        if evaluate_probability(INITIAL_SUBSTRATE_DENSITY):
            p = {'type': 'SUBSTRATE', 'disintegrating_flag': False, 'bonds': []}
        else:
            p = {'type': 'HOLE', 'disintegrating_flag': False, 'bonds': []}
        particles[x,y] = p

#Renvoie Vrai ou Faux selon la probabilité de probabilité
#la probabilité doit être comprise entre 0 et 1
def evaluate_probability(probability):
    return np.random.rand() < probability

Enfin, placez la molécule de catalyseur.

for x, y in INITIAL_CATALYST_POSITIONS:
    particles[x, y]['type'] = 'CATALYST'

Mouvement moléculaire

Ensuite, j'expliquerai le mouvement des molécules. Le mouvement moléculaire est obtenu en échangeant les informations dans deux cellules adjacentes. Sélectionnez au hasard une cellule du voisinage de Neumann dans une cellule et décidez si vous souhaitez vous déplacer en fonction de MOBILITY_FACTOR.

Pour compléter le quartier Neumann, les quatre cellules en haut, en bas, à gauche et à droite sont considérées les unes à côté des autres. En revanche, à proximité de Moore, huit cellules comprenant des cellules diagonales sont côte à côte.

noiman.png (16.8 kB)

La méthode qui exprime réellement le voisinage est la suivante.

def get_neumann_neighborhood_list(x, y):
    """
Obtient une liste contenant les quatre coordonnées près de Neuman aux coordonnées spécifiées.

    Parameters
    ----------
    x : int
La coordonnée x de la cible.
    y : int
La coordonnée y de la cible.

    Returns
    -------
    neumann_neighborhood_list : list of tuple
Une liste contenant les quatre coordonnées près de Neuman. Droite, gauche, bas, haut dans l'ordre
Il est stocké.
    """
    neumann_neighborhood_list = [
        ((x + 1) % SPACE_SIZE, y),
        ((x - 1) % SPACE_SIZE, y),
        (x, (y + 1) % SPACE_SIZE),
        (x, (y - 1) % SPACE_SIZE),
    ]
    return neumann_neighborhood_list

Regardons maintenant la mise en œuvre du mouvement.

moved = np.full(particles.shape, False, dtype=bool)
    for x in range(SPACE_SIZE):
        for y in range(SPACE_SIZE):
            p = particles[x,y]
            n_x, n_y = get_random_neumann_neighborhood(x, y, SPACE_SIZE)
            n_p = particles[n_x, n_y]
            mobility_factor = np.sqrt(MOBILITY_FACTOR[p['type']] * MOBILITY_FACTOR[n_p['type']])
            if not moved[x, y] and not moved[n_x, n_y] and \
               len(p['bonds']) == 0 and len(n_p['bonds']) == 0 and \
               evaluate_probability(mobility_factor):
                    particles[x,y], particles[n_x,n_y] = n_p, p
                    moved[x, y] = moved[n_x, n_y] = True

Après avoir sélectionné une cellule ([x, y]), get_random_neumann_ne Neighborhood (x, y, SPACE_SIZE) sélectionne aléatoirement la cellule vers laquelle se déplacer.

Il y a une variable appelée «déplacé» sur la première ligne, qu'est-ce que c'est? En fait, les particules ne stockaient pas «Aucun», mais déplaçaient les magasins «False».

moved = np.full(particles.shape, False, dtype=bool)
moved
# => array([[False, False, False, False, False, False, False, False, False,
        False, False, False, False, False, False, False],
       [False, False, False, False, False, False, False, False, False,
        False, False, False, False, False, False, False],
       [False, False, False, False, False, False, False, False, False,
        False, False, False, False, False, False, False],
       [False, False, False, False, False, False, False, False, False,
        False, False, False, False, False, False, False],
       [False, False, False, False, False, False, False, False, False,
        False, False, False, False, False, False, False],
       [False, False, False, False, False, False, False, False, False,
        False, False, False, False, False, False, False],
       [False, False, False, False, False, False, False, False, False,
        False, False, False, False, False, False, False],
       [False, False, False, False, False, False, False, False, False,
        False, False, False, False, False, False, False],
       [False, False, False, False, False, False, False, False, False,
        False, False, False, False, False, False, False],
       [False, False, False, False, False, False, False, False, False,
        False, False, False, False, False, False, False],
       [False, False, False, False, False, False, False, False, False,
        False, False, False, False, False, False, False],
       [False, False, False, False, False, False, False, False, False,
        False, False, False, False, False, False, False],
       [False, False, False, False, False, False, False, False, False,
        False, False, False, False, False, False, False],
       [False, False, False, False, False, False, False, False, False,
        False, False, False, False, False, False, False],
       [False, False, False, False, False, False, False, False, False,
        False, False, False, False, False, False, False],
       [False, False, False, False, False, False, False, False, False,
        False, False, False, False, False, False, False]])

déplacé regarde si la cellule sélectionnée a déjà été déplacée. En établissant la règle selon laquelle "une cellule ne peut être déplacée qu'une seule fois dans une boucle", on suppose que le changement de cellule se fait minute. Les cellules déplacées sont définies sur True et les cellules qui n'ont pas encore été déplacées sont définies sur False afin de pouvoir être référencées.

Dans l'instruction for, le déplacement est défini pour se produire uniquement si les conditions suivantes sont remplies.

Réaction moléculaire

Enfin, j'expliquerai le programme principal de réaction. Une fonction est créée pour chaque réaction comme indiqué ci-dessous. La fonction est importée d'un autre fichier, alors jetons un œil là-bas.

for x in range(SPACE_SIZE):
        for y in range(SPACE_SIZE):
            production(particles, x, y, PRODUCTION_PROBABILITY)
            disintegration(particles, x, y, DISINTEGRATION_PROBABILITY)
            bonding(particles, x, y, BONDING_CHAIN_INITIATE_PROBABILITY,
                                     BONDING_CHAIN_SPLICE_PROBABILITY,
                                     BONDING_CHAIN_EXTEND_PROBABILITY)
            bond_decay(particles, x, y, BOND_DECAY_PROBABILITY)
            absorption(particles, x, y, ABSORPTION_PROBABILITY)
            emission(particles, x, y, EMISSION_PROBABILITY)

production La production est une réaction dans laquelle un catalyseur transforme deux substrats voisins en une seule membrane. La formule de réaction est la suivante.

2S + C → L + C

Voyons comment cela est réellement programmé.

def production(particles, x, y, probability):
    p = particles[x,y]
    #Sélectionnez au hasard deux particules d'intérêt proches
    n0_x, n0_y, n1_x, n1_y = get_random_2_moore_neighborhood(x, y, particles.shape[0])
    n0_p = particles[n0_x, n0_y]
    n1_p = particles[n1_x, n1_y]
    if p['type'] != 'CATALYST' or n0_p['type'] != 'SUBSTRATE' or n1_p['type'] != 'SUBSTRATE':
        return
    if evaluate_probability(probability):
        n0_p['type'] = 'HOLE'
        n1_p['type'] = 'LINK'

Sélectionnez au hasard deux particules à proximité de la cible et videz une molécule de substrat et l'autre molécule de substrat avec une certaine probabilité (= ʻévaluer_probabilité (probabilité) `) uniquement dans les conditions suivantes. ..

À propos, la méthode get_random_2_moore_ne Neighborhood qui sélectionne au hasard deux particules voisines est la suivante.

def get_random_2_moore_neighborhood(x, y, space_size):
    n0_x, n0_y = get_random_moore_neighborhood(x, y, space_size)
    if x == n0_x:
        n1_x = np.random.choice([(n0_x+1)%space_size, (n0_x-1)%space_size])
        n1_y = n0_y
    elif y == n0_y:
        n1_x = n0_y
        n1_y = np.random.choice([(n0_y+1)%space_size, (n0_y-1)%space_size])
    else:
        n= [(x, n0_y), (n0_x, y)]
        n1_x, n1_y = n[np.random.randint(len(n))]
    return n0_x, n0_y, n1_x, n1_y
def get_random_moore_neighborhood(x, y, space_size):
    neighborhood = get_moore_neighborhood(x, y, space_size)
    nx, ny = neighborhood[np.random.randint(len(neighborhood))]
    return nx, ny

disintegration La désintégration est une réaction dans laquelle les molécules de la membrane se désintègrent en deux substrats. Les molécules membranaires ont une certaine probabilité de s'effondrer, mais selon les conditions environnantes, elles peuvent ne pas s'effondrer immédiatement. Plus précisément, la situation est la suivante.

--Il n'y a pas d'espace autour pour libérer les molécules de substrat coexistantes --Il n'y a pas d'espace autour pour accueillir les deux molécules de substrat divisées.

Par conséquent, s'il est évalué que la molécule de membrane s'effondre avec une certaine probabilité, définissez désintégration_flag sur True. Aucun repli ne se produit lorsque désintégration_flag a la valeur False. En conséquence, même si les molécules de la membrane ne s'effondrent pas dans les conditions environnantes, elles essaieront de s'effondrer à nouveau dans les réactions suivantes et ultérieures.

def disintegration(particles, x, y, probability):
    p = particles[x,y]
    #La désintégration peut ne pas se produire immédiatement, alors signalez-la une fois
    if p['type'] in ('LINK', 'LINK_SUBSTRATE') and evaluate_probability(probability):
        p['disintegrating_flag'] = True

    if not p['disintegrating_flag']:
        return
    #Si LINK contient SUBSTRAT, exécutez l'émission avec la probabilité 1 pour forcer l'émission
    emission(particles, x, y, 1.0)
    #Sélectionnez au hasard les particules d'intérêt proches
    n_x, n_y = get_random_moore_neighborhood(x, y, particles.shape[0])
    n_p = particles[n_x, n_y]
    if p['type'] == 'LINK' and n_p['type'] == 'HOLE':
        #Bond pour éliminer toutes les interconnexions LINK_Exécuter la désintégration avec la probabilité 1
        bond_decay(particles, x, y, 1.0)
        # disintegration
        p['type']   = 'SUBSTRATE'
        n_p['type'] = 'SUBSTRATE'
        p['disintegrating_flag'] = False

Une explication spécifique de «bond_decay» et «émission» sera donnée plus tard.

bonding La liaison est une réaction dans laquelle une molécule membranaire se lie à une molécule membranaire voisine. Ce programme est plus long que les autres, donc je vais vous montrer tout d'abord.

def bonding(particles, x, y,
            chain_initiate_probability, chain_splice_probability, chain_extend_probability,
            chain_inhibit_bond_flag=True, catalyst_inhibit_bond_flag=True):
    p = particles[x,y]
    #Sélectionnez au hasard les particules d'intérêt proches
    n_x, n_y = get_random_moore_neighborhood(x, y, particles.shape[0])
    #Vérifiez le type de deux molécules, le nombre de liaisons, l'angle et l'intersection
    n_p = particles[n_x, n_y]
    if not p['type'] in ('LINK', 'LINK_SUBSTRATE'):
        return
    if not n_p['type'] in ('LINK', 'LINK_SUBSTRATE'):
        return
    if (n_x, n_y) in p['bonds']:
        return
    if len(p['bonds']) >= 2 or len(n_p['bonds']) >= 2:
        return
    an0_x, an0_y, an1_x, an1_y = get_adjacent_moore_neighborhood(x, y, n_x, n_y, particles.shape[0])
    if (an0_x, an0_y) in p['bonds'] or (an1_x, an1_y) in p['bonds']:
        return
    an0_x, an0_y, an1_x, an1_y = get_adjacent_moore_neighborhood(n_x, n_y, x, y, particles.shape[0])
    if (an0_x, an0_y) in n_p['bonds'] or (an1_x, an1_y) in n_p['bonds']:
        return
    an0_x, an0_y, an1_x, an1_y = get_adjacent_moore_neighborhood(x, y, n_x, n_y, particles.shape[0])
    if (an0_x, an0_y) in particles[an1_x,an1_y]['bonds']:
        return
    #Le collage ne se produit pas dans les deux cas suivants
    # 1)Quand il y a une chaîne membranaire près de Moore(chain_inhibit_bond_flag)
    # 2)Quand il y a une molécule de catalyseur près de Moore(catalyst_inhibit_bond_flag)
    mn_list = get_moore_neighborhood(x, y, particles.shape[0]) + get_moore_neighborhood(n_x, n_y, particles.shape[0])
    if catalyst_inhibit_bond_flag:
        for mn_x, mn_y in mn_list:
            if particles[mn_x,mn_y]['type'] is 'CATALYST':
                return
    if chain_inhibit_bond_flag:
        for mn_x, mn_y in mn_list:
            if len(particles[mn_x,mn_y]['bonds']) >= 2:
                if not (x, y) in particles[mn_x,mn_y]['bonds'] and not (n_x, n_y) in particles[mn_x,mn_y]['bonds']:
                    return
    # Bonding
    if len(p['bonds'])==0 and len(n_p['bonds'])==0:
        prob = chain_initiate_probability
    elif len(p['bonds'])==1 and len(n_p['bonds'])==1:
        prob = chain_splice_probability
    else:
        prob = chain_extend_probability
    if evaluate_probability(prob):
        p['bonds'].append((n_x, n_y))
        n_p['bonds'].append((x, y))

Avant de l'examiner en détail, il existe certaines conditions pour la liaison des molécules membranaires, alors vérifions-les d'abord.

--Jusqu'à 2 liaisons peuvent former une molécule membranaire -L'angle entre les deux liaisons est de 90 degrés ou plus (45 degrés est interdit)

De plus, les restrictions suivantes ont été ajoutées pour faciliter la formation du film.

--Limitée par une chaîne combinée

Quand j'ai lu ceci pour la première fois, je me demandais beaucoup. Pourquoi est-il si facile de former une membrane alors qu'elle limite ou supprime la liaison des molécules membranaires?

bond_decay Il s'agit d'une réaction dans laquelle les liaisons maintenues par les molécules membranaires disparaissent avec une certaine probabilité en raison de la réaction inverse de la liaison. Le plaisir de l'autopoyèse est que la membrane est maintenue pendant que la formation et la disparition se produisent avec une certaine probabilité.

La mise en œuvre est la suivante. Si la particule cible est une molécule membranaire, il y a une certaine probabilité que l'information soit effacée des liaisons de cette cellule et de la cellule à laquelle elle est combinée.

def bond_decay(particles, x, y, probability):
    p = particles[x,y]
    if p['type'] in ('LINK', 'LINK_SUBSTRATE') and evaluate_probability(probability):
        for b in p['bonds']:
            particles[b[0], b[1]]['bonds'].remove((x, y))
        p['bonds'] = []

absorption, emission Les molécules de membrane sont perméables aux molécules de substrat. Ce mouvement est réalisé dans le modèle SCL par la membrane absorbant et libérant les substrats adjacents.

Recommended Posts

Complètement compris le chapitre 3 de "Créer et déplacer ALife"
Explication et mise en œuvre de PRML Chapitre 4
[Python] Chapitre 02-01 Bases des programmes Python (opérations et variables)
[Python of Hikari-] Chapitre 06-02 Fonction (argument et valeur de retour 1)
[Python] Chapitre 01-02 À propos de Python (Exécution et installation de l'environnement de développement)