mirror of
https://github.com/bics-rug/felice-models.git
synced 2026-09-30 21:03:06 +02:00
Initial commit
Co-authored-by: Aradhana Dube <a.dube@rug.nl> Co-authored-by: Renzo I. Barraza Altamirano <r.i.barraza.altamirano@rug.nl> Co-authored-by: Paolo Gibertini <p.gibertini@rug.nl> Co-authored-by: Luca D. Fehlings <l.d.fehlings@rug.nl>
This commit is contained in:
commit
9fabbdefc0
75 files changed
+447515
No files matched your search
@@ -0,0 +1,240 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "b433e05f",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import diffrax as dfx\n",
|
||||
"import jax\n",
|
||||
"import jax.numpy as jnp\n",
|
||||
"import jax.random as jrand\n",
|
||||
"import matplotlib as mpl\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"\n",
|
||||
"from felice.neuron_models import FHNRS"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 23,
|
||||
"id": "6abc4178",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"key = jrand.key(0)\n",
|
||||
"max_time = 200\n",
|
||||
"\n",
|
||||
"neuron_model = FHNRS(\n",
|
||||
" gmax_pasive=2.0,\n",
|
||||
" Erev_pasive=0.0,\n",
|
||||
" a_fast=-2.0,\n",
|
||||
" voff_fast=0.0,\n",
|
||||
" tau_fast=0.0,\n",
|
||||
" a_slow=0.5,\n",
|
||||
" voff_slow=1.0,\n",
|
||||
" tau_slow=50.0,\n",
|
||||
" vthr=jnp.inf,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def state_at_t(comp_times):\n",
|
||||
" sol = dfx.diffeqsolve(\n",
|
||||
" terms=dfx.ODETerm(neuron_model.dynamics),\n",
|
||||
" solver=dfx.Tsit5(),\n",
|
||||
" t0=0.0,\n",
|
||||
" t1=max_time,\n",
|
||||
" dt0=1e-3,\n",
|
||||
" y0=neuron_model.init_state(1)\n",
|
||||
" + jrand.uniform(key, shape=(1, 3), minval=0.1, maxval=0.5),\n",
|
||||
" saveat=dfx.SaveAt(ts=comp_times),\n",
|
||||
" max_steps=200000,\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" return sol.ts, sol.ys"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 24,
|
||||
"id": "9d185d60",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"image/png": "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 truncated
|
||||
"text/plain": [
|
||||
"<Figure size 1380x460 with 3 Axes>"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"v_range = jnp.arange(-3.1, 3, 0.1)\n",
|
||||
"VI_inst = jax.vmap(neuron_model.IV_inst)(v_range)\n",
|
||||
"VI_fast = jax.vmap(neuron_model.IV_fast)(v_range)\n",
|
||||
"VI_slow = jax.vmap(neuron_model.IV_slow)(v_range)\n",
|
||||
"\n",
|
||||
"with mpl.style.context(\"boilerplot.ieeetran\"):\n",
|
||||
" fig, ax = plt.subplots(1, 3, figsize=(6.9, 2.3), dpi=200.0, sharey=True)\n",
|
||||
" ax[0].plot(v_range, VI_inst)\n",
|
||||
" ax[1].plot(v_range, VI_fast)\n",
|
||||
" ax[2].plot(v_range, VI_slow)\n",
|
||||
" plt.show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 25,
|
||||
"id": "d98ace6e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"comp_times = jnp.linspace(0.0, max_time, 500)\n",
|
||||
"_, state = state_at_t(comp_times)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 26,
|
||||
"id": "06ae6a94",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def compute_nullclines(neuron_model, u_range, v_range, resolution=200):\n",
|
||||
" \"\"\"\n",
|
||||
" Compute nullclines\n",
|
||||
" du/dt = 0 (u-nullcline)\n",
|
||||
" dv/dt = 0 (v-nullcline)\n",
|
||||
" \"\"\"\n",
|
||||
" u_vals = jnp.linspace(u_range[0], u_range[1], resolution)\n",
|
||||
" v_vals = jnp.linspace(v_range[0], v_range[1], resolution)\n",
|
||||
" U, V = jnp.meshgrid(u_vals, v_vals)\n",
|
||||
"\n",
|
||||
" UV = jnp.stack(\n",
|
||||
" [U.reshape(-1), V.reshape(-1), jnp.zeros((resolution * resolution,))], axis=1\n",
|
||||
" )\n",
|
||||
" dS = neuron_model.dynamics(0, UV, {})\n",
|
||||
" dU = dS[:, 0].reshape(U.shape)\n",
|
||||
" dV = dS[:, 1].reshape(V.shape)\n",
|
||||
"\n",
|
||||
" return U, V, dU, dV\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def plot_vf(ax, neuron_model, u_range, v_range):\n",
|
||||
" import numpy as np\n",
|
||||
"\n",
|
||||
" u_sparse = jnp.linspace(u_range[0], u_range[1], 30)\n",
|
||||
" v_sparse = jnp.linspace(v_range[0], v_range[1], 30)\n",
|
||||
"\n",
|
||||
" Us, Vs = jnp.meshgrid(u_sparse, v_sparse)\n",
|
||||
"\n",
|
||||
" U, V, dU, dV = compute_nullclines(neuron_model, u_range, v_range, 200)\n",
|
||||
"\n",
|
||||
" UVs = jnp.stack([Us.reshape(-1), Vs.reshape(-1), jnp.ones((30 * 30,))], axis=1)\n",
|
||||
" dS = neuron_model.dynamics(0, UVs, {})\n",
|
||||
" dUs = dS[:, 0].reshape(Us.shape)\n",
|
||||
" dVs = dS[:, 1].reshape(Vs.shape)\n",
|
||||
"\n",
|
||||
" # Normalize for visualization\n",
|
||||
" magnitude = np.sqrt(dUs**2 + dVs**2)\n",
|
||||
" magnitude[magnitude == 0] = 1\n",
|
||||
" dUs_norm = dUs / magnitude\n",
|
||||
" dVs_norm = dVs / magnitude\n",
|
||||
"\n",
|
||||
" # Nullclines\n",
|
||||
" ax.contour(U, V, dU, levels=[0], colors=\"blue\", linewidths=1, linestyles=\"-\")\n",
|
||||
" ax.contour(U, V, dV, levels=[0], colors=\"red\", linewidths=1, linestyles=\"-\")\n",
|
||||
"\n",
|
||||
" # Vector field\n",
|
||||
" ax.quiver(Us, Vs, dUs_norm, dVs_norm, magnitude, cmap=\"viridis\", alpha=0.6)\n",
|
||||
"\n",
|
||||
" ax.set_xlabel(\"v\")\n",
|
||||
" ax.set_ylabel(\"w\")\n",
|
||||
" ax.legend([\"u-nullcline (du/dt=0)\", \"v-nullcline (dv/dt=0)\"], loc=\"upper right\")\n",
|
||||
" ax.set_xlim(u_range)\n",
|
||||
" ax.set_ylim(v_range)\n",
|
||||
" ax.axhline(y=0, color=\"gray\", linestyle=\"--\", alpha=0.3)\n",
|
||||
" ax.axvline(x=0, color=\"gray\", linestyle=\"--\", alpha=0.3)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 27,
|
||||
"id": "b73fb06d",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"image/png": "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 truncated
|
||||
"text/plain": [
|
||||
"<Figure size 1380x520 with 2 Axes>"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"with mpl.style.context(\"boilerplot.ieeetran\"):\n",
|
||||
" fig, ax = plt.subplots(1, 2, figsize=(6.9, 2.6), dpi=200)\n",
|
||||
" ax[0].plot(comp_times, state[:, 0, 0])\n",
|
||||
" ax[0].plot(comp_times, state[:, 0, 1], \"--\")\n",
|
||||
" # ax[0].plot(comp_times, state[0, :, 2], \"-.\")\n",
|
||||
" ax[0].set_xlabel(\"Time (ms)\")\n",
|
||||
" ax[0].legend([\"v\", \"vslow\", \"syn\"])\n",
|
||||
"\n",
|
||||
" plot_vf(ax[1], neuron_model, [-2, 2], [-2, 2])\n",
|
||||
"\n",
|
||||
" ax[1].plot(state[:, 0, 0], state[:, 0, 1])\n",
|
||||
" ax[1].plot(state[0, 0, 0], state[0, 0, 1], \".\", label=\"start\")\n",
|
||||
" ax[1].plot(state[0, -1, 0], state[0, -1, 1], \".\", label=\"end\")\n",
|
||||
" ax[1].set_xlabel(\"v\")\n",
|
||||
" ax[1].set_ylabel(\"v fast\")\n",
|
||||
" ax[1].legend()\n",
|
||||
" plt.show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "5a6f0b9f",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "daf213b5",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "felice",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.13.5"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
import marimo
|
||||
|
||||
__generated_with = "0.19.4"
|
||||
app = marimo.App(width="medium")
|
||||
|
||||
|
||||
@app.cell
|
||||
def _():
|
||||
import diffrax as dfx
|
||||
import jax.numpy as jnp
|
||||
import marimo as mo
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
from jax import jit
|
||||
|
||||
return dfx, jit, jnp, mo, np, plt
|
||||
|
||||
|
||||
@app.cell
|
||||
def _(dfx, jnp):
|
||||
def vector_field(t, y, args):
|
||||
v, vslow = y
|
||||
|
||||
ipasive = args["gmax"] * (v - args["Erev"])
|
||||
ifast = args["af"] * jnp.tanh(v - args["Ef"])
|
||||
islow = args["as"] * jnp.tanh(vslow - args["Es"])
|
||||
|
||||
dv = (-ipasive - ifast - islow) / args["C"]
|
||||
dvs = (v - vslow) / args["ts"]
|
||||
|
||||
return jnp.array([dv, dvs])
|
||||
|
||||
term = dfx.ODETerm(vector_field)
|
||||
return term, vector_field
|
||||
|
||||
|
||||
@app.cell
|
||||
def _(mo):
|
||||
p1 = mo.ui.slider(0.0, 5.0, value=1.0, step=0.1, label="gmax")
|
||||
p2 = mo.ui.slider(-1.0, 1.0, value=0.0, step=0.1, label="Erev")
|
||||
p3 = mo.ui.slider(-5.0, 5.0, value=-2.0, step=0.05, label="af")
|
||||
p4 = mo.ui.slider(-1.0, 1.0, value=0.0, step=0.05, label="Ef")
|
||||
p5 = mo.ui.slider(-5.0, 5.0, value=2.0, step=0.05, label="as")
|
||||
p6 = mo.ui.slider(-1.0, 1.0, value=0.0, step=0.05, label="Es")
|
||||
p7 = mo.ui.slider(1.0, 100.0, value=50.0, step=0.1, label="ts")
|
||||
p8 = mo.ui.slider(0.0, 1.0, value=1.0, step=0.01, label="C")
|
||||
|
||||
mo.hstack(
|
||||
[
|
||||
mo.vstack([p1, p2, p3, p4], justify="start", gap=1),
|
||||
mo.vstack([p5, p6, p7, p8], justify="start", gap=1),
|
||||
]
|
||||
)
|
||||
return p1, p2, p3, p4, p5, p6, p7, p8
|
||||
|
||||
|
||||
@app.cell
|
||||
def _(mo):
|
||||
mo.md("""
|
||||
### Initial Conditions & Simulation
|
||||
""")
|
||||
return
|
||||
|
||||
|
||||
@app.cell
|
||||
def _(mo):
|
||||
x0 = mo.ui.slider(-5.0, 5.0, value=2.0, step=0.1, label="x₀")
|
||||
y0 = mo.ui.slider(-5.0, 5.0, value=0.0, step=0.1, label="y₀")
|
||||
t_max = mo.ui.slider(10, 100, value=30, step=5, label="t_max")
|
||||
|
||||
mo.hstack([x0, y0, t_max], justify="start", gap=2)
|
||||
return t_max, x0, y0
|
||||
|
||||
|
||||
@app.cell
|
||||
def _(dfx, jit, jnp, p1, p2, p3, p4, p5, p6, p7, p8, t_max, term, x0, y0):
|
||||
@jit
|
||||
def solve_ode(y_init, args, t_end):
|
||||
solver = dfx.Tsit5()
|
||||
saveat = dfx.SaveAt(ts=jnp.linspace(0, t_end, 2000))
|
||||
sol = dfx.diffeqsolve(
|
||||
term,
|
||||
solver,
|
||||
t0=0,
|
||||
t1=t_end,
|
||||
dt0=0.01,
|
||||
y0=y_init,
|
||||
args=args,
|
||||
saveat=saveat,
|
||||
max_steps=100000,
|
||||
)
|
||||
return sol.ts, sol.ys
|
||||
|
||||
args = {
|
||||
"gmax": p1.value,
|
||||
"Erev": p2.value,
|
||||
"af": p3.value,
|
||||
"Ef": p4.value,
|
||||
"as": p5.value,
|
||||
"Es": p6.value,
|
||||
"ts": p7.value,
|
||||
"C": p8.value,
|
||||
}
|
||||
y_init = jnp.array([x0.value, y0.value])
|
||||
|
||||
t, ys = solve_ode(y_init, args, float(t_max.value))
|
||||
x_sol = ys[:, 0]
|
||||
y_sol = ys[:, 1]
|
||||
return args, t, x_sol, y_sol
|
||||
|
||||
|
||||
@app.cell
|
||||
def _(args, jnp, np, plt, t, vector_field, x0, x_sol, y0, y_sol):
|
||||
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5))
|
||||
|
||||
# Time series
|
||||
|
||||
ax1.plot(t, x_sol, "b-", lw=1.5, label="x(t)")
|
||||
ax1.plot(t, y_sol, "r-", lw=1.5, label="y(t)")
|
||||
ax1.set_xlabel("Time t")
|
||||
ax1.set_ylabel("State")
|
||||
ax1.set_title("Transient Response")
|
||||
ax1.legend()
|
||||
ax1.grid(True, alpha=0.3)
|
||||
|
||||
# Phase plane bounds
|
||||
# pad = 1.0
|
||||
xmin, xmax = -4, 4
|
||||
ymin, ymax = -2.5, 2.5
|
||||
|
||||
# Vector field
|
||||
X, Y = jnp.meshgrid(jnp.linspace(xmin, xmax, 20), jnp.linspace(ymin, ymax, 20))
|
||||
U, V = jnp.zeros_like(X), np.zeros_like(Y)
|
||||
|
||||
state = jnp.stack([X, Y], axis=0)
|
||||
deriv = vector_field(0.0, state, args)
|
||||
dx, dy = deriv[0], deriv[1]
|
||||
mag = jnp.sqrt(dx**2 + dy**2)
|
||||
U = jnp.where(mag > 0, dx / mag, U)
|
||||
V = jnp.where(mag > 0, dy / mag, V)
|
||||
|
||||
ax2.quiver(X, Y, U, V, alpha=0.4, color="gray", scale=25)
|
||||
|
||||
# Nullclines
|
||||
Xf, Yf = jnp.meshgrid(jnp.linspace(xmin, xmax, 150), jnp.linspace(ymin, ymax, 150))
|
||||
DX, DY = jnp.zeros_like(Xf), jnp.zeros_like(Yf)
|
||||
state = jnp.stack([Xf, Yf], axis=0)
|
||||
deriv = vector_field(0.0, state, args)
|
||||
DX, DY = deriv[0], deriv[1]
|
||||
ax2.contour(
|
||||
Xf,
|
||||
Yf,
|
||||
DX,
|
||||
levels=[0],
|
||||
colors="blue",
|
||||
linestyles="--",
|
||||
linewidths=1.5,
|
||||
alpha=0.7,
|
||||
)
|
||||
ax2.contour(
|
||||
Xf, Yf, DY, levels=[0], colors="red", linestyles="--", linewidths=1.5, alpha=0.7
|
||||
)
|
||||
|
||||
# Trajectory
|
||||
ax2.plot(x_sol, y_sol, "b-", lw=2)
|
||||
ax2.plot(x0.value, y0.value, "go", ms=10, label="Start")
|
||||
ax2.plot(x_sol[-1], y_sol[-1], "r*", ms=12, label="End")
|
||||
|
||||
ax2.set_xlabel("x")
|
||||
ax2.set_ylabel("y")
|
||||
ax2.set_title("Phase Plane")
|
||||
ax2.legend()
|
||||
ax2.grid(True, alpha=0.3)
|
||||
ax2.set_xlim(xmin, xmax)
|
||||
ax2.set_ylim(ymin, ymax)
|
||||
|
||||
plt.tight_layout()
|
||||
fig
|
||||
return
|
||||
|
||||
|
||||
@app.cell
|
||||
def _():
|
||||
return
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
||||
Reference in new issue
Block a user