Simuler un neurone modèle

16

Un neurone Izhikevich est un modèle simple mais assez efficace d'un neurone biologique, conçu pour être utilisé dans une simulation discrète de pas de temps. Dans ce défi de golf, vous implémenterez ce modèle.

Paramètres

Ce modèle ne comporte que 7 variables organisées en 2 équations différentielles, par rapport aux dizaines de paramètres d'un modèle physiologiquement précis.

  • vet usont les deux variables d'état du neurone. Ici, vest la variable "rapide" représentant le potentiel cellulaire au fil du temps, et uest la variable "lente" représentant certaines propriétés de la membrane. La vvariable est la plus importante, car il s'agit de la sortie de la simulation.
  • a, b, c, Et dsont fixées des constantes qui décrivent les propriétés du neurone. Différents types de neurones ont différentes constantes, selon le comportement souhaité. Notamment, cest le potentiel de réinitialisation, qui est le potentiel de membrane vers lequel la cellule revient après le dopage.
  • Ireprésente le courant d'entrée du neurone. Dans les simulations de réseau, cela changera avec le temps, mais pour nos besoins, nous traiterons Icomme une constante fixe.

Le modèle

Ce modèle a un pseudocode très simple. Tout d'abord, nous prenons les valeurs constantes de abcdet les utilisons pour initialiser vet u:

v = c
u = b * c

Ensuite, nous parcourons le code de simulation autant de fois que souhaité. Chaque itération représente 1 milliseconde de temps.

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

Certaines implémentations réelles incluent des étapes supplémentaires pour la précision numérique, mais nous ne les incluons pas ici.

Contribution

Comme entrée, votre programme / fonction doit prendre les valeurs de a, b, c, d, Iet t(le nombre de pas de temps à Simuler). Une fois définis, aucun de ces paramètres ne changera lors de notre simulation simple. L'ordre d'entrée n'a pas d' importance: vous pouvez spécifier l'ordre dans lequel votre programme prend ces paramètres.

Production

La sortie sera une liste de nombres représentant le potentiel de membrane de la cellule (donnée par variable v) au cours de la simulation. La liste peut être dans n'importe quel format approprié.

Vous avez le choix d'inclure ou non la 0ème valeur de la simulation (la configuration initiale avant la fin du temps) dans votre sortie. Par exemple, pour une entrée de 0.02 0.2 -50 2 10 6(pour a b c d I t), une sortie de

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

ou

-40
-16.04
73.876224
-42.667044096
-25.8262335380956
29.0355029192068

est acceptable.

Vos valeurs ne pas doivent être exactement les mêmes que celles ci - dessus, selon la façon dont vos flotteurs poignées de langue.

Implémentation de référence

Voici une implémentation TIO que j'ai écrite en Perl pour démontrer le modèle. Les paramètres sont ceux d'un neurone "bavardant" du document lié ci-dessus, et cela sert de démonstration de la façon dont ce modèle est capable de recréer certaines des propriétés les plus complexes des neurones, telles que l'alternance entre des états d'activité élevée et faible. Si vous regardez la sortie, vous pouvez voir où le neurone pointe immédiatement plusieurs fois, mais attend ensuite un certain temps avant de monter plusieurs fois (malgré la tension d'entrée de la cellule Iconstante tout le temps).

PhiNotPi
la source
Sera tjamais négatif?
kamoroso94
1
@ kamoroso94 Non, vous ne pouvez pas simuler de temps négatif.
PhiNotPi

Réponses:

6

R , 110 99 octets

Fonction anonyme qui prend 6 arguments. Rien d'extraordinaire, juste un port simple de l'implémentation de référence. La mise à jour de u, vet l'impression de vont toutes été combinées en une seule ligne, grâce au fait que R printrenvoie la valeur qui est imprimée, vous pouvez donc l'utiliser dans l'affectation. Un grand merci à Giuseppe pour avoir économisé 11 octets!

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

Essayez-le en ligne!

rturnbull
la source
2
C'est super, +1. Bien que, comme vous étiquetez explicitement les arguments, il n'y ait pas d'enregistrement d'octets entre pryr::f()et function(). Cependant, vous pouvez, après quelques expérimentations, déplacer vet udéclarer les déclarations dans le corps de la fonction tout en préservant l'ordre des arguments, pour économiser une douzaine d'octets: Essayez-le en ligne!
Giuseppe
car vne prend pas nécessairement des valeurs entières, vous en avez besoin v>=30, cependant
Giuseppe
@Giuseppe Merci, ces améliorations sont fantastiques. Pour une raison quelconque, je n'avais pas envisagé de ne pas étiqueter explicitement les arguments ...
rturnbull
4

Clean , 150 145 140 138 octets

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)

Essayez-le en ligne!

Définit la fonction $ :: Real Real Real Real Real Int -> [Real], implémentant l'algorithme comme décrit dans l'OP, à partir du 0ème terme.

Οurous
la source
3

Python 2 , 100 octets

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

Essayez-le en ligne!

Enregistré 2 octets grâce à user71546 .

M. Xcoder
la source
@ovs Oups, vous avez raison. Devrait être corrigé maintenant.
M. Xcoder
Le passage 0.04*v*và v*v/25.devrait enregistrer 1 octet. Si les flotteurs sont toujours donnés pour censuite v*v/25suffit pour -2 octets.
Shieru Asakoto
@ceilingcat Si vous jetez un oeil à mon historique de révision, vous remarquerez que je l'avais v>29dans ma version initiale. Cependant, cela n'est pas valide car ce vn'est pas nécessairement un entier.
M. Xcoder
3

JavaScript (Node.js) , 107 ... 103 101 octets

Contribué par @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))

Essayez-le en ligne!

Approche originale: 105 103 octets. -1 octet Merci Arnauld, et -2 octets Merci @ 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)}}

Essayez-le en ligne!

Ou si les alertes popping sont OK, alors 101 ... 99 97 octets (-1 octet Merci Arnauld, -2 octets Merci @ 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
la source
v>29n'est pas équivalent à v>=30pour les flottants. Vous voudrez probablement le faire à la v<30?0:(v=c,u+=d)place, ou mieux encore, v<30||(v=c,u+=d)ce qui économise un octet.
Arnauld
@Arnauld Oh oui, quand j'ai regardé la réponse Python, j'ai réalisé que je n'optimisais pas cela, mais je ne savais pas non plus que je traitais des flottants.; P Fixe.
Shieru Asakoto
2
Vous pouvez enregistrer deux octets en passant t-->0simplement à t--.
kamoroso94
1
Vous pouvez obtenir cela à 101 par refactoring la forboucle en une mapopération sur un tableau de longueur 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 fonction renvoie un tableau au lieu de consigner des valeurs, ce qui semble satisfaire la spécification. Ne bat pas la alertsolution, cependant.
apsillers
2

Rubis , 94 octets

->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}}

Essayez-le en ligne!

Un autre port simple de l'implémentation de référence, un lambda acceptant 6 arguments.

benj2240
la source
2

Haskell , 112 111 octets

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

Essayez-le en ligne!

Ne produit pas le cas zéro. Suppose que ce cn'est jamais >=30puisque cela n'aurait pas de sens.

Je n'ai jamais pensé que je devrais utiliser une whereclause dans un golf de code, mais il y a tout simplement trop de variables.

EDIT: Merci @Lynn pour avoir décollé un octet! J'ai oublié que vous pouvez mettre des letdéclarations en garde. Bien sûr, tue la lisibilité

user1472751
la source
1
Vous pouvez remplacer le wherepar l'étrange f x|let g a=b=ysyntaxe pour enregistrer un octet:(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

Élément , 81 octets

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

Essayez-le en ligne! , Page Esolangs

Explication:

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

Cette partie du programme prend des informations. Il stocke les constantes a, b, det Ien variables. L'entrée pour cn'est jamais stockée dans une variable, mais reste plutôt sur la pile principale pendant l'exécution. Trois copies sont faites: une en haut pour l'initialisation u, une au milieu pour servir d'initiale vet une en bas pour servir de constante c. L'entrée for test immédiatement lancée sur la pile de contrôle pour servir de base à la boucle FOR (la [...]) entourant le reste du programme.

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

Cette partie du programme prend la valeur actuelle de vet calcule la nouvelle valeur, puis quatre copies de la nouvelle vvaleur sont effectuées.

\
.`

Le premier exemplaire de va une nouvelle ligne en annexe et est imprimé.

30<!

La deuxième copie de vest utilisée pour tester si le neurone a atteint un pic. Le résultat de ce test est placé sur la pile de contrôle pour une utilisation ultérieure.

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

Cette partie calcule le "delta u", c'est-à-dire le montant à ajouter u.

[d~+]

Ce bloc IF s'ajoute dà la somme ci-dessus si le neurone augmente. Cela combine ce qui serait normalement deux affectations en une seule affectation.

u;

Cela stocke la valeur mise à jour de u.

[#2:]

Ce bloc IF est une continuation du bloc IF ci-dessus. Si le neurone augmente, supprimez la valeur actuelle de v(qui se trouve maintenant en haut de la pile principale) et remplacez-la par un doublon dec (qui a été au bas de la pile principale pendant tout ce temps).

Et c'est essentiellement tout ce qu'il y a à faire. Une remarque mineure est que cette chose fuit la mémoire: cela prend un supplément"# pour supprimer le haut de la pile de contrôle (la condition IF évaluée) après chaque itération de boucle.

Bien que je n'appellerais pas Element le langage de golf le plus gracieux, ce défi me permet de présenter une fonctionnalité intéressante: en raison de la division entre la pile principale et la pile de contrôle, je peux prendre une instruction IF et diviser la condition et le corps en plusieurs parties entrelacées avec un code inconditionnel.

PhiNotPi
la source
0

MATLAB, 111 octets

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

Une implémentation assez simple, peut probablement être encore améliorée.

Chris Loonam
la source