Simula una neurona modelo

16

Una neurona Izhikevich es un modelo simple pero bastante efectivo de una neurona biológica, diseñada para su uso en una simulación discreta de tiempo. En este desafío de golf, implementará este modelo.

Parámetros

Este modelo involucra solo 7 variables organizadas en 2 ecuaciones diferenciales, en comparación con las docenas de parámetros de un modelo fisiológicamente preciso.

  • vy uson las dos variables de estado de la neurona. Aquí, vestá la variable "rápida" que representa el potencial celular a lo largo del tiempo, y ues la variable "lenta" que representa ciertas propiedades de la membrana. La vvariable es la más importante, ya que esta es la salida de la simulación.
  • a, b, c, Y dson constantes que describen las propiedades de la neurona fijo. Los diferentes tipos de neuronas tienen diferentes constantes, dependiendo del comportamiento deseado. Notablemente, ces el potencial de reinicio, que es el potencial de membrana al que la célula regresa después del aumento.
  • Irepresenta la corriente de entrada a la neurona. En las simulaciones de red, esto cambiará con el tiempo, pero para nuestros propósitos lo trataremos Icomo una constante fija.

El modelo

Este modelo tiene un pseudocódigo muy simple. Primero, tomamos los valores constantes de abcdy los usamos para inicializar vy u:

v = c
u = b * c

A continuación, recorremos el código de simulación tantas veces como lo desee. Cada iteración representa 1 milisegundo de tiempo.

for 1..t:
  if v >= 30:    # reset after a spike
    v = c
    u = u + d
  v += 0.04*v^2 + 5*v + 140 - u + I
  u += a * (b*v - u)
  print v

Ciertas implementaciones del mundo real incluyen pasos adicionales para la precisión numérica, pero no las incluimos aquí.

Entrada

Como entrada, su programa / función debe tomar los valores de a, b, c, d, I, y t(el número de pasos de tiempo para simular). Una vez establecidos, ninguno de estos parámetros cambiará durante nuestra simulación simple. El orden de entrada no importa: puede especificar el orden en que su programa toma estos parámetros.

Salida

La salida será una lista de números que representan el potencial de membrana de la célula (dada por variable v ) en el transcurso de la simulación. La lista puede estar en cualquier formato apropiado.

Tiene la opción de incluir el valor 0 de la simulación (la configuración inicial antes de que pase cualquier momento) en su salida. Por ejemplo, para una entrada de 0.02 0.2 -50 2 10 6(para a b c d I t), una salida de

-50
-40
-16.04
73.876224
-42.667044096
-25.8262335380956
29.0355029192068

o

-40
-16.04
73.876224
-42.667044096
-25.8262335380956
29.0355029192068

es aceptable.

Sus valores no tienen que ser exactamente los mismos que los anteriores, dependiendo de cómo su idioma maneje los flotantes.

Implementación de referencia

Aquí hay una implementación de TIO que escribí en Perl para demostrar el modelo. Los parámetros son los de una neurona "parloteante" del documento vinculado anteriormente, y esto sirve como una demostración de cómo este modelo puede recrear algunas de las propiedades más complejas de las neuronas, como alternar entre estados de alta y baja actividad. Si observa la salida, puede ver dónde la neurona se dispara inmediatamente varias veces, pero luego espera un tiempo antes de dispararse varias veces más (a pesar de que el voltaje de entrada de la celda Isea ​​constante todo el tiempo).

PhiNotPi
fuente
¿ tAlguna vez será negativo?
kamoroso94
1
@ kamoroso94 No, no puedes simular el tiempo negativo.
PhiNotPi

Respuestas:

6

R , 110 99 bytes

Función anónima que toma 6 argumentos. Nada de lujos, solo un puerto directo de la implementación de referencia. La actualización de u, vy la impresión de vse han combinado en una sola línea, gracias al hecho de que R printdevuelve el valor que se está imprimiendo, por lo que puede usarlo en la asignación. ¡Muchas gracias a Giuseppe por guardar 11 bytes!

pryr::f({v=c;u=b*c;for(i in 1:t){if(v>=30){v=c;u=u+d}
u=a*b*(v=print((.04*v+6)*v+140+I-u))-a*u+u}})

Pruébalo en línea!

rturnbull
fuente
2
Esto es genial, +1. Sin embargo, dado que está etiquetando explícitamente los argumentos, no hay ningún byte guardado entre pryr::f()y function(). Sin embargo, después de experimentar un poco, puede mover vy ulas declaraciones del cuerpo de la función mientras conserva el orden de los argumentos, para guardar una docena de bytes: ¡ Pruébelo en línea!
Giuseppe
ya que vno necesariamente toma valores enteros, sí es necesario v>=30, sin embargo
Giuseppe
@Giuseppe Gracias, esas mejoras son fantásticas. Por alguna razón que no había considerado no etiquetar explícitamente los argumentos ...
rturnbull
4

Limpias , 150 145 140 138 bytes

import StdEnv
$a b c d i t=map snd(iterate(\(u,v)#(w,n)=if(30.0<v)(c,u+d)(v,u)
#y=0.04*w*w+6.0*w+140.0-n+i
=(a*b*y-a*n+n,y))(b*c,c))%(0,t)

Pruébalo en línea!

Define la función $ :: Real Real Real Real Real Int -> [Real], implementando el algoritmo como se describe en el OP, comenzando desde el término 0.

Οurous
fuente
3

Python 2 , 100 bytes

a,b,c,d,I,t=input();v=c;u=b*c
exec"if v>=30:v=c;u+=d\nv=v*v/25+6*v+140-u+I;u+=a*(b*v-u);print v\n"*t

Pruébalo en línea!

Guardado 2 bytes gracias a user71546 .

Sr. Xcoder
fuente
@ovs Oops, tienes razón. Debería arreglarse ahora.
Sr. Xcoder
Pasar 0.04*v*va v*v/25.debería ahorrar 1 byte. Si siempre se dan flotantes, centonces es v*v/25suficiente para -2 bytes.
Shieru Asakoto
@ceilingcat Si miras mi historial de revisiones, notarás que tenía v>29en mi versión inicial. Sin embargo, eso no es válido porque vno es necesariamente un número entero.
Sr. Xcoder
3

JavaScript (Node.js) , 107 ... 103101 bytes

Contribuido por @apsillers

(a,b,c,d,I,t)=>[...Array(t)].map(_=>(v<30||(v=c,u+=d),v=v*(v/25+6)+140-u+I,u+=a*(b*v-u),v),u=b*(v=c))

Pruébalo en línea!

Enfoque Original: 105 103 bytes. -1 byte Gracias Arnauld, y -2 bytes Gracias @ Kamoroso94.

(a,b,c,d,I,t)=>{for(u=b*(v=c);t--;){v<30||(v=c,u+=d);v=v*(v/25+6)+140-u+I;u+=a*(b*v-u);console.log(v)}}

Pruébalo en línea!

O si las alertas emergentes están bien, entonces 101 ... 99 97 bytes (-1 byte Gracias Arnauld, -2 bytes Gracias @ Kamoroso94):

(a,b,c,d,I,t)=>{for(u=b*(v=c);t--;){v<30||(v=c,u+=d);v=v*(v/25+6)+140-u+I;u+=a*(b*v-u);alert(v)}}

var u, v;
var f = 
(a,b,c,d,I,t)=>{for(u=b*(v=c);t--;){v<30||(v=c,u+=d);v=v*(v/25+6)+140-u+I;u+=a*(b*v-u);alert(v)}}

function run() {
 f(...["a", "b", "c", "d", "I", "t"].map(x => document.getElementById(x).value * 1));
}
a = <input id="a" value="0.02"><br>
b = <input id="b" value="0.2"><br>
c = <input id="c" value="-50"><br>
d = <input id="d" value="2"><br>
I = <input id="I" value="10"><br>
t = <input id="t" value="6"><br>
<input type="button" value="Run" onclick="run()">

Shieru Asakoto
fuente
v>29no es equivalente a v>=30para flotadores. Probablemente quieras hacer en su v<30?0:(v=c,u+=d)lugar, o mejor aún, lo v<30||(v=c,u+=d)que guarda un byte.
Arnauld
@Arnauld Oh, sí, cuando miré la respuesta de Python, me di cuenta de que no optimicé eso, pero tampoco me di cuenta de que estaba procesando flotantes; P solucionado.
Shieru Asakoto
2
Puede guardar dos bytes cambiando t-->0a simplemente t--.
kamoroso94
1
Usted puede conseguir esto a 101 por refactorización del forbucle en una mapoperación en una matriz de longitud t: (a,b,c,d,I,t)=>[...Array(t)].map(_=>(v<30||(v=c,u+=d),v=v*(v/25+6)+140-u+I,u+=a*(b*v-u),v),u=b*(v=c)). La función devuelve una matriz en lugar de valores de registro, que parece satisfacer la especificación. Sin alertembargo, no supera la solución.
apsillers
2

Ruby , 94 bytes

->a,b,c,d,i,t{v=c
u=b*c
t.times{v>=30?(v=c;u+=d):0
v+=0.04*v**2+5*v+140-u+i
u+=a*(b*v-u)
p v}}

Pruébalo en línea!

Otro puerto directo de la implementación de referencia, una lambda que acepta 6 argumentos.

benj2240
fuente
2

Haskell , 112 111 bytes

(a#b)c d i t|let r(v,u)|v>=30=r(c,u+d)|p<-0.04*v^2+6*v+140-u+i=(p,u+a*(b*p-u))=fst<$>take t(iterate r$r(c,b*c))

Pruébalo en línea!

No emite el caso cero. Asume que cnunca es así, >=30ya que eso no tendría sentido.

Nunca pensé que tendría que usar una wherecláusula en un código de golf, pero hay demasiadas variables.

EDITAR: ¡Gracias @Lynn por despegar un byte! Olvidé que puedes poner letdeclaraciones en guardias. Claro que mata la legibilidad

usuario1472751
fuente
1
Puede reemplazar el wherepor la extraña f x|let g a=b=ysintaxis para guardar un byte:(a#b)c d i t|let r(v,u)|v>=30=r(c,u+d)|p<-0.04*v^2+6*v+140-u+i=(p,u+a*(b*p-u))=fst<$>take t(iterate r$r(c,b*c))
Lynn
1

Elemento , 81 bytes

_a;_b;_3:b~*u;_d;_I;_'[3:\.04*5+*140+u~-+I~++4:\
.`30<!b~*u~-+a~*u~+[d~+]u;[#2:]]

Pruébalo en línea! , Página de Esolangs

Explicación:

_a;_b;_3:b~*u;_d;_I;_'[ ... ]

Esta parte del programa toma entrada. Almacena constantes a, b, d, y Ien variables. La entrada para cnunca se almacena en una variable, sino que permanece en la pila principal durante la ejecución. Se realizan tres copias: una en la parte superior para inicializar u, una en el medio para servir como inicial vy otra en la parte inferior para servir como constante c. La entrada para tse lanza inmediatamente a la pila de control para servir como base del bucle FOR (the [...]) que rodea al resto del programa.

3:\.04*5+*140+u~-+I~++4:

Esta parte del programa toma el valor actual vy calcula el nuevo valor, y luego se hacen cuatro copias del nuevo vvalor.

\
.`

La primera copia de vtiene una nueva línea adjunta y se imprime.

30<!

La segunda copia de vse usa para probar si la neurona se ha disparado. El resultado de esta prueba se coloca en la pila de control para su uso posterior.

b~*u~-+a~*u~+

Esta parte calcula el "delta u", que significa la cantidad a agregar u.

[d~+]

Este bloque IF se suma da la suma anterior si la neurona se está disparando. Esto combina lo que normalmente serían dos tareas en una sola tarea.

u;

Esto almacena el valor actualizado de u.

[#2:]

Este bloque IF es una continuación del bloque IF anterior. Si la neurona se está disparando, elimine el valor actual de v(que ahora está en la parte superior de la pila principal) y reemplácelo con un duplicado dec (que ha estado en la parte inferior de la pila principal todo este tiempo).

Y eso es básicamente todo lo que hay que hacer. Una nota menor es que esto pierde memoria: se necesita un extra "#para eliminar la parte superior de la pila de control (la condición IF evaluada) después de cada iteración de bucle.

Aunque no llamaría a Element el lenguaje de golf más elegante, este desafío me permite mostrar una característica interesante: debido a la división entre la pila principal y la pila de control, puedo tomar una declaración IF y dividir la condición y el cuerpo en múltiples partes, entrelazadas con código incondicional.

PhiNotPi
fuente
0

MATLAB, 111 bytes

function z(a,b,c,d,I,t)
v=c;u=b*c;for i=1:t if v>=30 v=c;u=u+d;end
v=.04*v^2+6*v+140-u+I
u=u+a*(b*v-u);
end
end

Implementación bastante simple, probablemente se puede mejorar aún más.

Chris Loonam
fuente