Simular um neurônio modelo

16

Um neurônio Izhikevich é um modelo simples, mas bastante eficaz, de um neurônio biológico, projetado para uso em uma simulação discreta do tempo. Neste desafio de golfe, você estará implementando este modelo.

Parâmetros

Este modelo envolve apenas 7 variáveis ​​organizadas em 2 equações diferenciais, em comparação com as dezenas de parâmetros de um modelo fisiologicamente preciso.

  • ve usão as duas variáveis ​​de estado do neurônio. Aqui, vé a variável "rápida" que representa o potencial da célula ao longo do tempo e ué a variável "lenta" que representa certas propriedades da membrana. A vvariável é a mais importante, pois esta é a saída da simulação.
  • a, b, c, E dsão constantes que descrevem as propriedades do neurónio fixo. Diferentes tipos de neurônios têm constantes diferentes, dependendo do comportamento desejado. Notavelmente, cé o potencial de redefinição, que é o potencial de membrana ao qual a célula retorna após o pico.
  • Irepresenta a corrente de entrada para o neurônio. Nas simulações de rede, isso mudará com o tempo, mas, para nossos propósitos, trataremos Icomo uma constante fixa.

O Modelo

Este modelo possui um pseudocódigo muito simples. Primeiro, pegamos os valores constantes de abcde os usamos para inicializar ve u:

v = c
u = b * c

Em seguida, percorreremos o código de simulação quantas vezes desejar. Cada iteração representa 1 milissegundo de tempo.

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

Certas implementações do mundo real incluem etapas adicionais para precisão numérica, mas não as incluímos aqui.

Entrada

Como entrada, a sua função / programa deve ter os valores de a, b, c, d, I, e t(o número de passos de tempo para simular). Uma vez definido, nenhum desses parâmetros será alterado durante nossa simulação simples. A ordem de entrada não importa: você pode especificar a ordem em que seu programa aceita esses parâmetros.

Resultado

A saída será uma lista de números que representam o potencial de membrana da célula (fornecido por variável v) ao longo da simulação. A lista pode estar em qualquer formato apropriado.

Você tem a opção de incluir o 0º valor da simulação (a configuração inicial antes de qualquer momento) em sua saída. Por exemplo, para uma entrada de 0.02 0.2 -50 2 10 6(for a b c d I t), uma saída 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

é aceitável.

Seus valores não precisam ser exatamente os mesmos que os acima, dependendo de como seu idioma lida com flutuações.

Implementação de referência

Aqui está uma implementação de TIO que escrevi no Perl para demonstrar o modelo. Os parâmetros são os de um neurônio "tagarelado" do artigo acima, e isso serve como uma demonstração de como esse modelo é capaz de recriar algumas das propriedades mais complexas dos neurônios, como alternar entre estados de alta e baixa atividade. Se você observar a saída, poderá ver onde o neurônio dispara imediatamente várias vezes, mas depois espera um pouco antes de disparar várias vezes (apesar da tensão de entrada da célula Iser constante o tempo todo).

PhiNotPi
fonte
Vai tser sempre negativo?
precisa saber é o seguinte
1
@ kamoroso94 Não, você não pode simular um tempo negativo.
PhiNotPi

Respostas:

6

R , 110 99 bytes

Função anônima que recebe 6 argumentos. Nada extravagante, apenas uma porta direta da implementação de referência. A atualização de u, ve a impressão de vtodas foram combinadas em uma única linha, graças ao fato de R's printretornar o valor que está sendo impresso, para que você possa usá-lo na atribuição. Muito obrigado a Giuseppe por salvar 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}})

Experimente online!

rturnbull
fonte
2
Isso é ótimo, +1. Embora você esteja rotulando explicitamente os argumentos, não há economia de bytes entre pryr::f()e function(). No entanto, você pode, depois de algumas experiências, mover ve u's declarações no corpo da função, preservando a ordem dos argumentos, para salvar uma dúzia Bytes: Experimente online!
Giuseppe
desde que vnão necessariamente inteiros valores, você precisa fazer v>=30, embora
Giuseppe
@ Giuseppe Obrigado, essas melhorias são fantásticas. Por alguma razão eu não tinha considerado não rotular explicitamente os argumentos ...
rturnbull
4

Limpo , 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)

Experimente online!

Define a função $ :: Real Real Real Real Real Int -> [Real], implementando o algoritmo conforme descrito no OP, começando no 0º termo.

Furioso
fonte
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

Experimente online!

Economizou 2 bytes graças a user71546 .

Mr. Xcoder
fonte
Ops, você está certo. Deve ser corrigido agora.
Sr. Xcoder 8/02
Virar 0.04*v*vpara v*v/25.deve economizar 1 byte. Se forem sempre fornecidos carros alegóricos, centão será v*v/25suficiente para -2 bytes.
Shieru Asakoto
@ceilingcat Se você der uma olhada no meu histórico de revisões, perceberá que eu tinha v>29na minha versão inicial. No entanto, isso é inválido porque vnão é necessariamente um número inteiro.
Mr. Xcoder
3

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

Contribuição de @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))

Experimente online!

Abordagem original: 105 103 bytes. -1 byte Obrigado Arnauld e -2 bytes Obrigado @ 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)}}

Experimente online!

Ou se os alertas popping estiverem OK, 101 ... 99 97 bytes (-1 byte Obrigado Arnauld, -2 bytes Obrigado @ 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
fonte
v>29não é equivalente a v>=30para carros alegóricos. Você provavelmente quer fazer isso v<30?0:(v=c,u+=d), ou melhor ainda, v<30||(v=c,u+=d)que economiza um byte.
Arnauld
@ Arnauld Ah, sim, quando olhei para a resposta em Python, percebi que não otimizava isso, mas também não percebi que estava processando carros alegóricos.; P Corrigido.
Shieru Asakoto
2
Você pode salvar dois bytes mudando t-->0para simplesmente t--.
Kamoroso94
1
Você pode obter este para baixo a 101 por refatoração do forcircuito em uma mapoperação em uma matriz de comprimento 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)). A função retorna uma matriz em vez de registrar valores, que parece satisfazer a especificação. Não supera a alertsolução, no entanto.
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}}

Experimente online!

Outra porta simples da implementação de referência, um lambda que aceita 6 argumentos.

benj2240
fonte
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))

Experimente online!

Não gera o caso zero. Supõe que cnunca, >=30já que isso não faria sentido.

Nunca pensei que eu teria que usar uma wherecláusula em um código de golfe, mas existem variáveis ​​demais.

EDIT: Obrigado @Lynn por tirar um byte! Esqueci que você pode colocar letdeclarações em guardas. Claro que mata a legibilidade embora

user1472751
fonte
1
Você pode substituir o wherepela f x|let g a=b=ysintaxe estranha para salvar um 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:]]

Experimente online! , Página Esolangs

Explicação:

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

Esta parte do programa recebe entrada. Ele armazena constantes a, b, d, e Iem variáveis. A entrada para cnunca é armazenada em uma variável, mas permanece na pilha principal durante a execução. Três cópias são feitas: uma no topo para inicializar u, uma no meio para servir como inicial ve uma na parte inferior para servir como constante c. A entrada para té lançada imediatamente na pilha de controle para servir como base do loop FOR (o [...]) ao redor do restante do programa.

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

Esta parte do programa pega o valor atual ve calcula o novo valor e, em seguida, vsão feitas quatro cópias do novo valor.

\
.`

A primeira cópia de vtem uma nova linha anexada e é impressa.

30<!

A segunda cópia de vé usada para testar se o neurônio disparou. O resultado desse teste é colocado na pilha de controle para uso posterior.

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

Esta parte calcula o "delta u", significando a quantidade a ser adicionada u.

[d~+]

Este bloco IF adiciona dà soma acima se o neurônio estiver disparando. Isso combina o que normalmente seriam duas atribuições em uma única atribuição.

u;

Isso armazena o valor atualizado de u.

[#2:]

Este bloco IF é uma continuação do bloco IF acima. Se o neurônio estiver disparando, exclua o valor atual de v(que agora está no topo da pilha principal) e substitua-o por uma duplicata de c(que esteve na parte inferior da pilha principal esse tempo todo).

E isso é basicamente tudo o que existe. Uma observação secundária é que essa coisa vaza memória: é preciso um extra "#para excluir a parte superior da pilha de controle (a condição IF avaliada) após cada iteração do loop.

Embora eu não diria que Element é a linguagem de golfe mais elegante, esse desafio me permite mostrar um recurso interessante: devido à divisão entre a pilha principal e a pilha de controle, posso pegar uma instrução IF e dividir a condição e o corpo em vários partes, entrelaçadas com código incondicional.

PhiNotPi
fonte
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

Implementação bastante simples, provavelmente pode ser melhorada ainda mais.

Chris Loonam
fonte