Symuluj model neuronu

16

Neuron Izhikevicha to prosty, ale dość skuteczny model neuronu biologicznego, zaprojektowany do zastosowania w dyskretnej symulacji z krokami w czasie. W tym wyzwaniu golfowym wdrożysz ten model.

Parametry

Model ten obejmuje tylko 7 zmiennych zorganizowanych w 2 równania różniczkowe, w porównaniu do kilkudziesięciu parametrów fizjologicznie dokładnego modelu.

  • vi usą dwiema zmiennymi stanu neuronu. Tutaj vjest „szybką” zmienną reprezentującą potencjał komórki w czasie i ujest „wolną” zmienną reprezentującą pewne właściwości błony. vZmienna jest najważniejsza, ponieważ jest to wyjście symulacji.
  • a, b, c, I dsą przymocowane stałe, które opisują właściwości neuronu. Różne typy neuronów mają różne stałe, w zależności od pożądanego zachowania. W szczególności cchodzi o potencjał resetowania, czyli potencjał błonowy, do którego komórka powraca po wzbogaceniu.
  • Ireprezentuje prąd wejściowy do neuronu. W symulacjach sieciowych zmieni się to z czasem, ale dla naszych celów będziemy traktować Ijako stałą.

Model

Ten model ma bardzo prosty pseudokod. Najpierw bierzemy stałe wartości abcdi używamy ich do inicjalizacji voraz u:

v = c
u = b * c

Następnie przeglądamy kod symulacyjny tyle razy, ile potrzeba. Każda iteracja odpowiada 1 milisekundie czasu.

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

Niektóre rzeczywiste implementacje zawierają dodatkowe kroki dla dokładności numerycznej, ale nie uwzględniamy ich tutaj.

Wejście

Jako dane wejściowe, program / funkcja powinna przyjąć wartości a, b, c, d, I, i t(liczba kroków czasowych do symulacji). Po ustawieniu żaden z tych parametrów nie będzie się zmieniał podczas naszej prostej symulacji. Kolejność wprowadzania nie ma znaczenia: możesz określić kolejność, w jakiej program pobiera te parametry.

Wynik

Wyjściem będzie lista liczb reprezentujących potencjał błonowy komórki (podana przez zmienną v) w trakcie symulacji. Lista może mieć dowolny odpowiedni format.

Masz wybór, czy dołączyć do wyjścia wartość 0 symulacji (wstępna konfiguracja przed upływem czasu). Na przykład dla wejścia 0.02 0.2 -50 2 10 6(for a b c d I t) wyjście dowolnego z nich

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

lub

-40
-16.04
73.876224
-42.667044096
-25.8262335380956
29.0355029192068

jest do zaakceptowania.

Twoje wartości nie muszą być dokładnie takie same jak powyższe, w zależności od tego, jak Twój język obsługuje zmiennoprzecinkowe.

Wdrożenie referencyjne

Oto implementacja TIO, którą napisałem w Perlu, aby zademonstrować model. Parametry są takie jak „gadający” neuron z powyższego artykułu, co służy jako demonstracja tego, jak ten model jest w stanie odtworzyć niektóre z bardziej złożonych właściwości neuronów, takich jak naprzemienne zmienianie stanów wysokiej i niskiej aktywności. Jeśli spojrzysz na wynik, możesz zobaczyć, gdzie neuron natychmiast gwałtownie podnosi kilka razy, ale potem czeka jeszcze chwilę, zanim przyspieszy jeszcze kilka razy (mimo że napięcie wejściowe komórki Ijest stałe przez cały czas).

PhiNotPi
źródło
Czy tkiedykolwiek będzie negatywny?
kamoroso94
1
@ kamoroso94 Nie, nie można symulować czasu ujemnego.
PhiNotPi

Odpowiedzi:

6

R , 110 99 bajtów

Anonimowa funkcja, która pobiera 6 argumentów. Nic szczególnego, tylko prosty port referencyjnej implementacji. Uaktualnianiem u, voraz drukowanie vwszystkie zostały połączone w jedną linię, dzięki temu, że R jest printZwraca wartość, która jest drukowana, więc można go używać w zadania. Wielkie podziękowania dla Giuseppe za uratowanie 11 bajtów!

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

Wypróbuj online!

rturnbull
źródło
2
To świetnie, +1. Chociaż, ponieważ wyraźnie określasz argumenty, nie ma zapisywania bajtów między pryr::f()a function(). Można jednak, po pewnym eksperymentowania, przesuń vi u„s deklaracji w ciele funkcji zachowując kolejność argumentów, aby zaoszczędzić kilkanaście bajtów: Spróbuj online!
Giuseppe
ponieważ vniekoniecznie przyjmuje wartości całkowite, potrzebujesz v>=30jednak
Giuseppe
@Giuseppe Dzięki, te ulepszenia są fantastyczne. Z jakiegoś powodu nie zastanawiałem się nad jawnym oznaczeniem argumentów ...
rturnbull
4

Czysty , 150 145 140 138 bajtów

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)

Wypróbuj online!

Definiuje funkcję $ :: Real Real Real Real Real Int -> [Real], implementując algorytm opisany w OP, zaczynając od 0-tego terminu.

Obrzydliwe
źródło
3

Python 2 , 100 bajtów

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

Wypróbuj online!

Zaoszczędzono 2 bajty dzięki user71546 .

Pan Xcoder
źródło
@ovs Ups, masz rację. Powinien zostać teraz naprawiony.
Pan Xcoder,
Przejście 0.04*v*vdo v*v/25.powinno zaoszczędzić 1 bajt. Jeśli pływaki są zawsze podane na cpotem v*v/25wystarczy do -2 bajtów.
Shieru Asakoto,
@ceilingcat Jeśli spojrzysz na moją historię zmian, zauważysz, że miałem ją v>29w mojej początkowej wersji. Jest to jednak nieprawidłowe, ponieważ vniekoniecznie jest liczbą całkowitą.
Pan Xcoder,
3

JavaScript (Node.js) , 107 ... 103 101 bajtów

Wkład @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))

Wypróbuj online!

Podejście oryginalne: 105 103 bajtów. -1 bajty Dzięki Arnauld i -2 bajty Dzięki @ 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)}}

Wypróbuj online!

Lub jeśli wyskakujące alerty są w porządku, to 101 ... 99 97 bajtów (-1 bajtów Dzięki Arnauld, -2 bajtów Dzięki @ 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
źródło
v>29nie jest równoważne z v>=30pływakami. Prawdopodobnie chcesz to zrobić v<30?0:(v=c,u+=d)zamiast tego lub lepiej, v<30||(v=c,u+=d)co oszczędza bajt.
Arnauld
@Arnauld O tak, kiedy spojrzałem na odpowiedź Pythona, zdałem sobie sprawę, że nie zoptymalizowałem tego, ale nie zdawałem sobie sprawy, że również przetwarzam zmiennoprzecinkowe.; P Naprawiono.
Shieru Asakoto,
2
Możesz zapisać dwa bajty, zmieniając t-->0na po prostu t--.
kamoroso94
1
Można dostać to w dół do 101 przez refactoring forpętlę do mappracy na tablicy długości 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)). Funkcja zwraca tablicę zamiast rejestrowania wartości, które wydają się spełniać specyfikację. alertJednak nie pokonuje rozwiązania.
apsillers
2

Ruby , 94 bajty

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

Wypróbuj online!

Kolejny prosty port implementacji referencyjnej, lambda akceptująca 6 argumentów.

benj2240
źródło
2

Haskell , 112 111 bajtów

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

Wypróbuj online!

Nie generuje przypadku zerowego. Zakłada, że cnigdy tak nie jest, >=30ponieważ nie miałoby to sensu.

Nigdy nie myślałem, że będę musiał użyć whereklauzuli w golfie kodowym, ale jest po prostu zbyt wiele zmiennych.

EDYCJA: Dzięki @Lynn za start bajtu! Zapomniałem, że możesz umieścić letoświadczenia w straży. Pewnie zabija jednak czytelność

użytkownik 1472751
źródło
1
Możesz zastąpić tę wheredziwną f x|let g a=b=yskładnią, aby zapisać bajt:(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

Element , 81 bajtów

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

Wypróbuj online! , Strona Esolangs

Wyjaśnienie:

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

Ta część programu wymaga danych wejściowych. Przechowuje ona stałe a, b, d, i Ido zmiennych. Dane wejściowe dla cnigdy nie są przechowywane w zmiennej, ale pozostają na głównym stosie podczas wykonywania. Wykonywane są trzy kopie: jedna u góry do inicjalizacji u, jedna w środku, która służy jako inicjał v, a druga u dołu, aby służyła jako stała c. Dane wejściowe for tsą rzucane natychmiast na stos sterujący, aby służyć jako podstawa pętli FOR [...]otaczającej resztę programu.

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

Ta część programu przyjmuje bieżącą wartość vi oblicza nową wartość, a następnie wykonuje się cztery kopie nowej vwartości.

\
.`

Pierwszy egzemplarz vma dołączony znak nowej linii i jest drukowany.

30<!

Druga kopia vsłuży do testowania, czy neuron przyspieszył. Wynik tego testu jest umieszczany na stosie kontrolnym do późniejszego wykorzystania.

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

Ta część oblicza „deltę u”, co oznacza kwotę do dodania u.

[d~+]

Ten blok IF dodaje dsię do powyższej sumy, jeśli neuron ulega skoku. Łączy to zwykle dwa zadania w jedno zadanie.

u;

Przechowuje zaktualizowaną wartość u.

[#2:]

Ten blok IF jest kontynuacją powyższego bloku IF. Jeśli neuron przebija, usuń bieżącą wartość v(która jest teraz na górze głównego stosu) i zastąp ją duplikatem c(który znajdował się na dole głównego stosu przez cały czas).

I to w zasadzie wszystko. Jedna drobna uwaga jest taka, że ​​ta rzecz przecieka pamięć: potrzeba więcej "#tam, aby usunąć górę stosu kontrolnego (oceniany warunek JEŻELI) po każdej iteracji pętli.

Chociaż nie nazwałbym Elementu najbardziej wdzięcznym językiem golfa, to wyzwanie pozwala mi zaprezentować interesującą funkcję: ze względu na podział na główny stos i stos kontrolny, mogę wziąć instrukcję IF i podzielić warunek i ciało na wiele części z przeplotem bezwarunkowym kodem.

PhiNotPi
źródło
0

MATLAB, 111 bajtów

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

Całkiem prosta implementacja, prawdopodobnie można ją jeszcze ulepszyć.

Chris Loonam
źródło