[PYTHON] Vollständig verstandenes Kapitel 3 von "Making and Moving ALife"

Schaffung eines Individuums

SCL-Modell

Modellübersicht

――Es besteht aus verschiedenen Molekülen, die sich in einem zweidimensionalen Gitter bewegen, und ihren chemischen Reaktionsformeln.

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

――Jedes Molekül bewegt sich zwischen Zellen und unterliegt chemischen Reaktionen wie Bindung und Zersetzung mit anderen benachbarten Molekülen. - 1) 2S + C → L + C

Werfen wir einen Blick darauf, wie die "Individuen" gebildet und wie sie gepflegt werden.

Zustand der individuellen Bildung

Selbstwartung

Modellimplementierung

Das SCL-Modell ist als eine Art zweidimensionaler zellularer Automat implementiert. Die Zelle kann sich in den folgenden fünf Zuständen befinden.

Der Zellenstatus wird in Python wie folgt beschrieben. Andere Elemente als "Typ" werden später beschrieben.

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

Molekulare Reaktionen und Bindungen können durch die folgenden sechs Reaktionen ausgedrückt werden. Details werden im Berechnungsteil erläutert.

Die Parameter des Modells sind wie folgt.

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 ist die Bewegungsfreiheit jedes Moleküls. "Wahrscheinlichkeit" ist die Leichtigkeit, mit der jede Reaktion abläuft.

Die Implementierung des SCL-Modells kann in drei Teile unterteilt werden:

--1) Initialisierung ―― 2) Bewegung von Molekülen ―― 3) Molekulare Reaktion

Schauen wir sie uns der Reihe nach an.

Initialisieren

In der Initialisierungsphase wird ein zweidimensionales Array vorbereitet, das Zellinformationen speichert, und Moleküle werden angeordnet.

#Initialisieren
particles = np.empty((SPACE_SIZE, SPACE_SIZE), dtype=object)
# INITIAL_SUBSTRATE_Platzieren Sie SUBSTRATE und LOCH entsprechend der DICHTE.
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_Platzieren Sie CATALYST in POSITIONEN.
for x, y in INITIAL_CATALYST_POSITIONS:
    particles[x, y]['type'] = 'CATALYST'

Zuerst haben wir ein zweidimensionales Array von SPACE_SIZE x SPACE_SIZE vorbereitet.

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)

Setzen Sie als Nächstes den Typ mit einer bestimmten Wahrscheinlichkeit für die Fallzelle auf SUBSTRATE oder HOLE. evalu_probability (Möglichkeit) gibt TRUE oder FALSE mit einer Wahrscheinlichkeit von Argumenten zurück. Dies legt den Typ fest.

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

#Gibt je nach Wahrscheinlichkeitswahrscheinlichkeit Wahr oder Falsch zurück
#Wahrscheinlichkeit muss zwischen 0 und 1 liegen
def evaluate_probability(probability):
    return np.random.rand() < probability

Zum Schluss das Katalysatormolekül platzieren.

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

Molekulare Bewegung

Als nächstes werde ich die Bewegung von Molekülen erklären. Die molekulare Bewegung wird erreicht, indem die Informationen in zwei benachbarten Zellen ausgetauscht werden. Wählen Sie zufällig eine Zelle aus der Nähe von Neumann in einer Zelle aus und entscheiden Sie, ob Sie sich gemäß MOBILITY_FACTOR bewegen möchten.

Um das Neumann-Viertel zu ergänzen, werden die vier Zellen oben, unten, links und rechts nebeneinander betrachtet. Andererseits befinden sich in der Nähe von Moore acht Zellen einschließlich diagonaler Zellen nebeneinander.

noiman.png (16.8 kB)

Die Methode, die die Nachbarschaft tatsächlich ausdrückt, ist wie folgt.

def get_neumann_neighborhood_list(x, y):
    """
Ruft eine Liste mit den vier Koordinaten in der Nähe von Neuman an den angegebenen Koordinaten ab.

    Parameters
    ----------
    x : int
Die x-Koordinate des Ziels.
    y : int
Die y-Koordinate des Ziels.

    Returns
    -------
    neumann_neighborhood_list : list of tuple
Eine Liste mit den vier Koordinaten in der Nähe von Neuman. Rechts, links, unten, oben in der Reihenfolge
Es wird gespeichert.
    """
    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

Betrachten wir nun die Umsetzung der Bewegung.

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

Nach Auswahl einer Zelle ([x, y]) wählt get_random_neumann_neighborhood (x, y, SPACE_SIZE) zufällig die Zelle aus, in die verschoben werden soll.

In der ersten Zeile befindet sich eine Variable namens "verschoben". Was ist das? Tatsächlich haben Partikel nicht "Keine" gespeichert, sondern "Falsch" verschoben.

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]])

Verschoben prüft, ob die ausgewählte Zelle bereits verschoben wurde. Durch die Regel, dass "eine Zelle nur einmal in einer Schleife verschoben werden kann", wird angenommen, dass die Änderung der Zelle winzig erfolgt. Verschobene Zellen werden auf True gesetzt, und Zellen, die noch nicht verschoben wurden, werden auf False gesetzt, damit auf sie verwiesen werden kann.

In der for-Anweisung wird festgelegt, dass die Verschiebung nur erfolgt, wenn die folgenden Bedingungen erfüllt sind.

--Ist die verschobene Variable der beiden zu verschiebenden Zellen falsch? -Ist das zu bewegende Molekül an ein anderes gebunden? --Ist der Rückgabewert der Funktion evalu_probability True?

Molekulare Reaktion

Abschließend werde ich das Hauptreaktionsprogramm erläutern. Für jede Reaktion wird eine Funktion erstellt, wie unten gezeigt. Die Funktion wird aus einer anderen Datei importiert. Schauen wir uns das an.

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 Die Produktion ist eine Reaktion, bei der ein Katalysator zwei benachbarte Substrate in eine einzige Membran umwandelt. Die Reaktionsformel ist wie folgt.

2S + C → L + C

Mal sehen, wie das eigentlich programmiert ist.

def production(particles, x, y, probability):
    p = particles[x,y]
    #Wählen Sie zufällig zwei interessierende Partikel in der Nähe aus
    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'

Wählen Sie zufällig zwei Partikel in der Nähe des Ziels aus und leeren Sie ein Substratmolekül und das andere Substratmolekül mit einer bestimmten Wahrscheinlichkeit (= evalu_probability (Wahrscheinlichkeit)) nur unter den folgenden Bedingungen. ..

Übrigens lautet die Methode "get_random_2_moore_neighborhood", mit der zwei benachbarte Partikel zufällig ausgewählt werden, wie folgt.

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 Desintegration ist eine Reaktion, bei der Membranmoleküle wieder in zwei Substrate zerfallen. Membranmoleküle haben eine gewisse Wahrscheinlichkeit des Kollabierens, aber abhängig von den Umgebungsbedingungen können sie nicht sofort kollabieren. Insbesondere ist die Situation wie folgt.

Wenn daher bewertet wird, dass das Membranmolekül mit einer bestimmten Wahrscheinlichkeit zusammenbricht, setzen Sie "disintegrating_flag" auf "True". Wenn disintegrating_flag False ist, tritt kein Zusammenbruch auf. Selbst wenn die Membranmoleküle unter den Umgebungsbedingungen nicht kollabieren, versuchen sie daher, bei den nächsten und nachfolgenden Reaktionen erneut zu kollabieren.

def disintegration(particles, x, y, probability):
    p = particles[x,y]
    #Die Auflösung erfolgt möglicherweise nicht sofort. Markieren Sie sie daher einmal
    if p['type'] in ('LINK', 'LINK_SUBSTRATE') and evaluate_probability(probability):
        p['disintegrating_flag'] = True

    if not p['disintegrating_flag']:
        return
    #Wenn LINK SUBSTRATE enthält, führen Sie die Emission mit der Wahrscheinlichkeit 1 aus, um die Emission zu erzwingen
    emission(particles, x, y, 1.0)
    #Wählen Sie zufällig interessierende Partikel in der Nähe aus
    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, um alle LINK-Verbindungen zu beseitigen_Zerfall mit Wahrscheinlichkeit 1 ausführen
        bond_decay(particles, x, y, 1.0)
        # disintegration
        p['type']   = 'SUBSTRATE'
        n_p['type'] = 'SUBSTRATE'
        p['disintegrating_flag'] = False

Spezifische Erklärungen für "Bond_Decay" und "Emission" werden später gegeben.

bonding Bindung ist eine Reaktion, bei der ein Membranmolekül an ein nahe gelegenes Membranmolekül bindet. Dieses Programm ist länger als die anderen, daher zeige ich Ihnen zuerst das Ganze.

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]
    #Wählen Sie zufällig interessierende Partikel in der Nähe aus
    n_x, n_y = get_random_moore_neighborhood(x, y, particles.shape[0])
    #Überprüfen Sie den Typ zweier Moleküle, die Anzahl der Bindungen, den Winkel und den Schnittpunkt
    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
    #In den folgenden beiden Fällen tritt keine Bindung auf
    # 1)Wenn sich in der Nähe von Moore eine Membrankette befindet(chain_inhibit_bond_flag)
    # 2)Wenn sich in der Nähe von Moore ein Katalysatormolekül befindet(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))

Bevor wir uns das genauer ansehen, gibt es einige Bedingungen für die Bindung von Membranmolekülen. Lassen Sie uns diese zuerst überprüfen.

Zusätzlich wurden die folgenden Einschränkungen hinzugefügt, um die Bildung des Films zu erleichtern.

Als ich das zum ersten Mal las, wunderte ich mich sehr. Warum ist es so einfach, eine Membran zu bilden, wenn sie die Bindung von Membranmolekülen begrenzt oder unterdrückt?

bond_decay Dies ist eine Reaktion, bei der die von den Membranmolekülen gehaltenen Bindungen mit einer gewissen Wahrscheinlichkeit aufgrund der umgekehrten Reaktion der Bindung verschwinden. Der Spaß an der Autopoyese besteht darin, dass die Membran erhalten bleibt, während Bildung und Verschwinden mit einer bestimmten Wahrscheinlichkeit auftreten.

Die Implementierung ist wie folgt. Wenn das Zielteilchen ein Membranmolekül ist, besteht eine gewisse Wahrscheinlichkeit, dass die Informationen aus den Bindungen dieser Zelle und der Zelle, mit der sie kombiniert werden, gelöscht werden.

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 Membranmoleküle sind für Substratmoleküle durchlässig. Diese Bewegung wird im SCL-Modell dadurch erreicht, dass die Membran benachbarte Substrate absorbiert und freisetzt.

Recommended Posts

Vollständig verstandenes Kapitel 3 von "Making and Moving ALife"
Erläuterung und Implementierung von PRML Kapitel 4
[Python] Kapitel 02-01 Grundlagen von Python-Programmen (Operationen und Variablen)
[Python of Hikari-] Kapitel 06-02 Funktion (Argument und Rückgabewert 1)
[Python] Kapitel 01-02 Über Python (Ausführung und Installation der Entwicklungsumgebung)