Modelo de Axelrod

Simulación Numérica


Contenido

  1. Introducción
  2. Modelo
  3. Dinámica
  4. Implementación
  5. En Tiempo Real
  6. Coméntanos




Introducción

Es impresionante el surgimiento espontáneo de la auto-organización, fenómenos cooperativos y la adaptación en sistemas compuestos por agentes en general. El uso de modelos "simples" con autómatas pueden ser una gran herramienta para estudiar los mecanismos que hay detrás de los comportamientos complejos.
Axelrod propone un modelo para imitar el como la cultura se disemina entre los individuos. La cultura es representada por un conjunto de atributos como pueden ser el idioma, religión, habilidades técnicas, dieta, principios éticos o sociales, etc. y estos atributos están sujetos a la influencia social, es decir, pueden ser cambiados o copiados debido al efecto de las interacciones mutuas entre individuos.

En este proyecto presentamos una implementación simulacional del modelo de Axelrod basada en la publicación:
Claudio Castellano, Matteo Marsili, y Alessandro Vespignani. (2000) Nonequilibrium Phase Transition in a Model for Social Influence. Physical Review Letters 85(16), 3536-3539.
Tal como se refiere en la publicación nombrada, la dinámica del modelo obedece dos principios:

1. Los individuos interactúan mayormente con otros que comparten muchos de sus atributos culturales.
2. La interacción entre individuos incrementa el número de atributos culturales que comparten.

Modelo

Iniciando una población de individuos (agentes) con atributos culturales asignados al azar (estado desordenado), la dinámica del modelo llevará a la formación de regiones (dominios culturales / estado con mayor orden) en las que se comparte la cultura y otras regiones segregadas. El modelo Axelrod está definido en una lattice cuadrada de tamaño $L$. En cada sitio $i$ de la lattice se encuentra un conjunto de $F$ variables enteras $\sigma_{i,f}$ que representan los atributos culturales del individuo o agente en dicho sitio.
Cada atributo $f = 1,...,F$ en cada sitio $i$ será inicialmente asignado de manera aleatoria con una distribución uniforme de números enteros entre 0 y $q$. El parámetro $q$ es una medida inicial de la diversidad cultural de la población o sistema.

Agentes

$$ \sigma_{i} = \begin{bmatrix} f_{1} \\ f_{2} \\ f_{3} \\ f_{4} \\ . \\ . \\ f_{F}\end{bmatrix} \ \text{donde} \ \ f_{i} \in [0,1,...,q]$$

Ejemplo de Agente

$$ \sigma_{i} = \begin{bmatrix} Idioma \\ Religión \\ Dieta \\ Deporte \\ . \\ . \\ Habilidad \end{bmatrix}$$ $$ \sigma_{i} = \begin{bmatrix} Italiano \\ Ateo \\ Vegetariano \\ Ciclista \\ . \\ . \\ Carpintería\end{bmatrix}$$

Ejemplo de Lattice
(n = 4, q = 15, F = 5)

Pakin Axelrod Lattice

Dinámica del Modelo

En cada iteración de la simulación, la dinámica es la siguiente:

1. Se eligen aleatoriamente un par de vecinos (sitios) cercanos $(i,j)$.

2. Se elige aleatoriamente un atributo $f$ y si $\sigma_{i,f} \ne \sigma_{j,f}$ no ocurre nada.
Si $\sigma_{i,f} = \sigma_{j,f}$, entonces otro atributo $f'$ es elegido aleatoriamente entre aquellos que sean diferentes entre el par de agentes, es decir $\sigma_{i,f'} \ne \sigma_{j,f'}$.

3. El atributo elegido se iguala $\sigma_{i,f'} \rightarrow \sigma'_{i,f'} = \sigma_{j,f'}$

Implementación en Python

Paquetes

In [2]:
import numpy as np
import random as rdm
from os import system
from time import sleep

Dinámica

In [3]:
def axelrod(Lx, Ly, f, q, pop, pop1):
    
    ''' 
    Autor: Francisco Jaramillo
    https://www.pakin.lat/
    
    '''

    #    tiempo = 0
    trig = False
    t = 0

    while trig != True:

        xr1 = rdm.randint(0, Lx - 1)
        yr1 = rdm.randint(0, Ly - 1)

        # esquina inferior izquierda
        if xr1 == 0 and yr1 == 0:

            s = rdm.sample([[xr1, yr1 + 1], [xr1 + 1, yr1]], 1)

        # esquina superior derecha
        elif xr1 == Lx - 1 and yr1 == Ly - 1:

            s = rdm.sample([[xr1, yr1 - 1], [xr1 - 1, yr1]], 1)

        # esquina inferior derecha
        elif xr1 == Lx - 1 and yr1 == 0:

            s = rdm.sample([[xr1, yr1 + 1], [xr1 - 1, yr1]], 1)

        # esquina superior izquierda
        elif xr1 == 0 and yr1 == Ly - 1:

            s = rdm.sample([[xr1, yr1 - 1], [xr1 + 1, yr1]], 1)

        # frontera vertical izquierda
        elif xr1 == 0 and (yr1 != 0 or yr1 != Ly - 1):

            s = rdm.sample([[xr1, yr1 + 1], [xr1, yr1 - 1], [xr1 + 1, yr1]], 1)

        # frontera horizontal inferior
        elif yr1 == 0 and (xr1 != 0 or xr1 != Lx - 1):

            s = rdm.sample([[xr1, yr1 + 1], [xr1 - 1, yr1], [xr1 + 1, yr1]], 1)

        # frontera vertical derecha
        elif xr1 == Lx - 1 and (yr1 != 0 or yr1 != Ly - 1):

            s = rdm.sample([[xr1, yr1 + 1], [xr1, yr1 - 1], [xr1 - 1, yr1]], 1)

        # frontera horizontal superior
        elif yr1 == Ly - 1 and (xr1 != 0 or xr1 != Lx - 1):

            s = rdm.sample([[xr1, yr1 - 1], [xr1 - 1, yr1], [xr1 + 1, yr1]], 1)

        # interior
        else:

            s = rdm.sample([[xr1, yr1 - 1], [xr1, yr1 + 1], [xr1 - 1, yr1],
                            [xr1 + 1, yr1]], 1)

        # agente elegido
        p1 = pop1[xr1, yr1]

        # primer vecino elegido
        p2 = pop1[s[0][0], s[0][1]]

        # se elige característica
        ent = rdm.sample(range(0, f), 1)

        # si se comparte característica:
        if p1[0][ent] == p2[0][ent]:

            ind = []

            for i in range(f):
                # etiqueta características
                ind.append(p1[0][i] - p2[0][i])

            # selecciona índice/posición de características no iguales
            dif = [i for i, x in enumerate(ind) if x != 0]

            # si el conjunto es no vacío
            try:

                ent2 = rdm.sample(dif, 1)

                # agente copia característica a vecino
                p1[0][ent2] = p2[0][ent2]

            except:

                #conjunto vacío, pasa
                pass

        #no se comparte ninguna característica, pasa
        else:

            pass

        t += 1
        #        tiempo = t

        visual(pop1, Lx, Ly, f)

        #sleep(0.0555)
        sleep(0.07)
        #sleep(0.01)
        clear()

        if t % (Lx * Ly) == 0:

            trig = pruebaEdo(Lx, Ly, f, pop1)

    return t

Prueba de Estado Absorbente

In [5]:
def pruebaEdo(Lx, Ly, f, pop1):

    ''' 
    Autor: Francisco Jaramillo
    https://www.pakin.lat/
    
    '''
    
    res = []

    for i in range(Lx):
        for j in range(Ly):

            # esquina inferior izquierda
            if i == 0 and j == 0:

                res.append(sum(np.equal(pop1[i, j][0], pop1[i, j + 1][0])))
                res.append(sum(np.equal(pop1[i, j][0], pop1[i + 1, j][0])))

            # esquina superior derecha
            elif i == Lx - 1 and j == Ly - 1:

                res.append(sum(np.equal(pop1[i, j][0], pop1[i, j - 1][0])))
                res.append(sum(np.equal(pop1[i, j][0], pop1[i - 1, j][0])))

            # esquina inferior derecha
            elif i == Lx - 1 and j == 0:

                res.append(sum(np.equal(pop1[i, j][0], pop1[i, j + 1][0])))
                res.append(sum(np.equal(pop1[i, j][0], pop1[i - 1, j][0])))

            # esquina superior izquierda
            elif i == 0 and j == Ly - 1:

                res.append(sum(np.equal(pop1[i, j][0], pop1[i, j - 1][0])))
                res.append(sum(np.equal(pop1[i, j][0], pop1[i + 1, j][0])))

            # frontera vertical izquierda
            elif i == 0 and (j != 0 or j != Ly - 1):

                res.append(sum(np.equal(pop1[i, j][0], pop1[i, j + 1][0])))
                res.append(sum(np.equal(pop1[i, j][0], pop1[i, j - 1][0])))
                res.append(sum(np.equal(pop1[i, j][0], pop1[i + 1, j][0])))

            # frontera horizontal inferior
            elif j == 0 and (i != 0 or i != Lx - 1):

                res.append(sum(np.equal(pop1[i, j][0], pop1[i, j + 1][0])))
                res.append(sum(np.equal(pop1[i, j][0], pop1[i - 1, j][0])))
                res.append(sum(np.equal(pop1[i, j][0], pop1[i + 1, j][0])))

            # frontera vertical derecha
            elif i == Lx - 1 and (j != 0 or j != Ly - 1):

                res.append(sum(np.equal(pop1[i, j][0], pop1[i, j + 1][0])))
                res.append(sum(np.equal(pop1[i, j][0], pop1[i, j - 1][0])))
                res.append(sum(np.equal(pop1[i, j][0], pop1[i - 1, j][0])))

            # frontera horizontal superior
            elif j == Ly - 1 and (i != 0 or i != Lx - 1):

                res.append(sum(np.equal(pop1[i, j][0], pop1[i, j - 1][0])))
                res.append(sum(np.equal(pop1[i, j][0], pop1[i - 1, j][0])))
                res.append(sum(np.equal(pop1[i, j][0], pop1[i + 1, j][0])))

            # interior
            else:

                res.append(sum(np.equal(pop1[i, j][0], pop1[i, j - 1][0])))
                res.append(sum(np.equal(pop1[i, j][0], pop1[i, j + 1][0])))
                res.append(sum(np.equal(pop1[i, j][0], pop1[i - 1, j][0])))
                res.append(sum(np.equal(pop1[i, j][0], pop1[i + 1, j][0])))

    # validación de posibles estados absorbentes
    trig = list(set(res)) == [f] or \
            list(set(res)) == [0] or \
            list(set(res)) == [0,f]

    return trig

Visualización

In [6]:
def clear():

    _ = system('clear')


def visual(conj, Lx, Ly, f):

    ''' 
    Autor: Francisco Jaramillo
    https://www.pakin.lat/
    
    '''
    
    z = conj.reshape(Lx, Ly, 1, f).transpose(0, 3, 2, 1).reshape(Lx, f, Ly)

    def foo(num):
        return "{:02d}".format(num)

    z = np.frompyfunc(foo, 1, 1)(z)

    for x in z:

        for y in x:
            print(np.array2string(y, separator='  |  ').replace('\'', ''),
                  end='')
            print(end='\n')
        print()

Función Principal

In [7]:
def main():
    
    ''' 
    Autor: Francisco Jaramillo
    https://www.pakin.lat/
    
    '''

    Lx = 7
    Ly = 7
    f = 4
    q = 16

    pop = np.random.randint(q, size=(Lx, Ly, 1, f))
    pop1 = pop.copy()

    axelrod(Lx, Ly, f, q, pop, pop1)
    print('Estado Inicial')
    visual(pop, Lx, Ly, f)
    print('Estado Absorbente')
    visual(pop1, Lx, Ly, f)

    return Lx, Ly, f, q, pop, pop1


#main()

Dinámica en Tiempo Real

Estado Absorbente
Formación de Dominios Culturales

Pakin Axelrod Estado Absorbente

Regresar al inicio


Esperamos con gusto tus comentarios y sugerencias: