diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 11c21c6..ccc2f9a 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -23,6 +23,7 @@ "python.defaultInterpreterPath": "/usr/local/bin/python", "python.linting.enabled": true, "python.linting.pylintEnabled": true, + "python.formatting.provider": "black", "python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8", "python.formatting.blackPath": "/usr/local/py-utils/bin/black", "python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf", @@ -38,7 +39,8 @@ "ms-python.python", "ms-python.vscode-pylance", "ms-azuretools.vscode-docker", - "ms-toolsai.jupyter" + "ms-toolsai.jupyter", + "charliermarsh.ruff" ] } }, diff --git a/scripts/basics/basic_setup.py b/scripts/basics/basic_setup.py index 77d7e65..ae8c67a 100644 --- a/scripts/basics/basic_setup.py +++ b/scripts/basics/basic_setup.py @@ -1,53 +1,10 @@ -#%% +# %% from autodyn.core import dynamical as dyn -from autodyn.models.canonical.standard import ( - lorenz, - consensus, - single_hopf, - controlled_hopf, -) +from autodyn.models.canonical.standard import lorenz from autodyn.core.network import connectivity import numpy as np -#%% +# %% lorenz_sys = dyn.system(lorenz, D=3) lorenz_sys.simulate(T=50, dt=0.01, sigma=10, rho=28, beta=8 / 3) -lorenz_sys.plot_phase() - - -#%% -test_sys = dyn.system(consensus, D=10) -brain_net = connectivity(10, proportion=0.4) - -test_sys.simulate(T=100, dt=0.1, D=brain_net.D.T) - -test_sys.plot_phase() - -brain_net.plot_incidence() -brain_net.plot_spectrum() -#%% -# Design our stimulation waveform here - - -times = 50 -dt = 0.01 -stim = np.zeros((int(times // dt) + 1, 1)) -stim[stim.shape[0] // 2 :: 10] = 40 - -# Setup and run our dynamics -hopf_single = dyn.system(controlled_hopf, D=2) -hopf_single.simulate( - T=times, - dt=dt, - sigma=10, - rho=28, - beta=8 / 3, - c=3, - w=1, - g=1, - stim=stim, - keep_positive=True, -) -hopf_single.plot_polar() - -# %% +lorenz_sys.plot_phase(d1=0, d2=1) diff --git a/scripts/basics/builder_dynamics.py b/scripts/basics/builder_dynamics.py new file mode 100644 index 0000000..2003b4d --- /dev/null +++ b/scripts/basics/builder_dynamics.py @@ -0,0 +1,8 @@ +from autodyn.builder import microstruct, macrostruct +import networkx as nx + +# %% +neural_mass = microstruct() +brain_network = macrostruct(L=brain_graph) + +main_system = microstruct + macrostruct diff --git a/scripts/basics/sys_types/consensus.py b/scripts/basics/sys_types/consensus.py new file mode 100644 index 0000000..922340a --- /dev/null +++ b/scripts/basics/sys_types/consensus.py @@ -0,0 +1,13 @@ +from autodyn.core import dynamical as dyn +from autodyn.models.canonical.standard import consensus +from autodyn.core.network import connectivity + +# %% +test_sys = dyn.system(consensus, D=10) +brain_net = connectivity(10, proportion=0.4) + +test_sys.simulate(T=100, dt=0.1, D=brain_net.D.T) +test_sys.plot_phase(d1=0, d2=2) + +brain_net.plot_incidence() +brain_net.plot_spectrum() diff --git a/scripts/basics/sys_types/controlled_hopf.py b/scripts/basics/sys_types/controlled_hopf.py new file mode 100644 index 0000000..a854566 --- /dev/null +++ b/scripts/basics/sys_types/controlled_hopf.py @@ -0,0 +1,27 @@ +# %% +import numpy as np +from autodyn.core import dynamical as dyn +from autodyn.models.canonical.standard import controlled_hopf + +# %% +# Design our stimulation waveform here +times = 50 +dt = 0.01 +stim = np.zeros((int(times // dt) + 1, 1)) +stim[stim.shape[0] // 2 :: 10] = 40 + +# Setup and run our dynamics +hopf_single = dyn.system(controlled_hopf, D=2) +hopf_single.simulate( + T=times, + dt=dt, + sigma=10, + rho=28, + beta=8 / 3, + c=3, + w=1, + g=1, + stim=stim, + keep_positive=True, +) +hopf_single.plot_polar() diff --git a/scripts/basics/sys_types/kuramoto_eg.py b/scripts/basics/sys_types/kuramoto_eg.py new file mode 100644 index 0000000..9e0dc48 --- /dev/null +++ b/scripts/basics/sys_types/kuramoto_eg.py @@ -0,0 +1,11 @@ +# %% +import numpy as np +from autodyn.core import dynamical as dyn +from autodyn.models.neuro.scalar import kuramoto + +# %% +kmo = dyn.system(kuramoto, D=3) +param_set = {"D": 1 * np.eye(3), "w": np.pi / 2} +kmo.simulate(T=100, dt=0.1, params=param_set) +kmo.plot_phase(0, 1) +kmo.plot_polar() diff --git a/scripts/wilson-cowan/basic_setup.py b/scripts/wilson-cowan/basic_setup.py index 985c3d7..d4a9c1d 100644 --- a/scripts/wilson-cowan/basic_setup.py +++ b/scripts/wilson-cowan/basic_setup.py @@ -23,7 +23,6 @@ T = 100 dt = 0.1 tpts = int(T // dt) + 1 - tvect = np.linspace(0, T, tpts) u = np.zeros_like(tvect) diff --git a/scripts/wilson-cowan/neurotransmitter.py b/scripts/wilson-cowan/neurotransmitter.py new file mode 100644 index 0000000..6460d57 --- /dev/null +++ b/scripts/wilson-cowan/neurotransmitter.py @@ -0,0 +1,35 @@ +#%% +from autodyn.core import dynamical as dyn +from autodyn.models.neuro.wc import wc_drift, wc_input +from autodyn.core import control + +#%% +wilson_cowan = dyn.system(wc_drift, D=2) +param_set = { + "T_e": 5, + "T_i": 5, + "beta": {"e": -1, "i": -1}, + "w": {"ee": 10, "ii": 3, "ei": 12, "ie": 8}, + "alpha": 0.1, + "thresh": {"e": 0.2, "i": 4}, + "tau": 0, + "net_k": 1 / 10, +} +wilson_cowan.simulate(T=100, dt=0.1, params=param_set) +wilson_cowan.plot_phase() + +#%% +def step_u(x): + _, t = x.shape + u = np.zero_like(x) + u[t // 2 :: 2] = 1 + + return u + + +controller = control(u=step_u) + + +wilson_cowan = dyn.system(wc_input, D=2) +wilson_cowan.simulate(T=T, dt=dt, params=param_set, stim=u) +wilson_cowan.plot_phase() diff --git a/src/autodyn/builder/__init__.py b/src/autodyn/builder/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/autodyn/builder/macro.py b/src/autodyn/builder/macro.py new file mode 100644 index 0000000..e69de29 diff --git a/src/autodyn/builder/micro.py b/src/autodyn/builder/micro.py new file mode 100644 index 0000000..44c0c4c --- /dev/null +++ b/src/autodyn/builder/micro.py @@ -0,0 +1,22 @@ +from dataclasses import dataclass +from typing import Optional, Callable +import numpy as np +from autodyn.core.network import connectivity +import networkx as nx +from autodyn.utils.functions import unity + + +class microstruct: + def __init__(self): + pass + + @property + def dynamics(self): + if not self._dynamics: + self._dynamics = unity + + return self._dynamics + + @dynamics.setter + def dynamics(self, f: Callable): + self._dynamics = f diff --git a/src/autodyn/core/control.py b/src/autodyn/core/control.py new file mode 100644 index 0000000..f6b89c7 --- /dev/null +++ b/src/autodyn/core/control.py @@ -0,0 +1,3 @@ +class control: + def __init__(self): + pass diff --git a/src/autodyn/core/dynamical.py b/src/autodyn/core/dynamical.py index ec910cd..a7b259b 100755 --- a/src/autodyn/core/dynamical.py +++ b/src/autodyn/core/dynamical.py @@ -1,17 +1,10 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Tue Feb 11 17:21:34 2020 - -@author: virati -Barebones class for dynamical systems -""" +from typing import Optional import numpy as np import matplotlib.pyplot as plt import networkx as nx -import scipy.signal as sig from autodyn.utils.functions import unity +from autodyn.core import control from autodyn.core.integrators.runge_kutta import rk_integrator @@ -21,10 +14,26 @@ def __init__(self, f, D: int = 3, net_graph: nx.Graph = None): self.x = np.zeros((D, 1)) self.D = D self.f = f + self.u_func: Optional[callable] = None self.gen_connectivity() self.post_step = unity + @property + def u(self): + self._u: control + if self.u_func is None: + return np.zeros_like(self.x) + + if not self._u: + self._u = control(self.u_func) + return self._u + + @u.setter + def u(self, value: np.ndarray): + self._u = control() + self._u.raw = value + def set_post_step(self, func: callable): self.post_step = func @@ -83,7 +92,12 @@ def plot_raster(self): plt.plot(self.raster) plt.show() - def plot_phase(self): + def plot_phase(self, d1=0, d2=1): + fig = plt.figure() + plt.plot(self.raster[:, d1], self.raster[:, d2]) + plt.title("Phase") + + def plot_phase_full(self): if self.D == 3: fig = plt.figure() ax = fig.add_subplot(projection="3d") @@ -103,4 +117,4 @@ def plot_measure(self): def plot_polar(self): plt.figure() plt.plot(np.real(self.raster[:, 0] * np.exp(1j * self.raster[:, 1]))) - plt.title("Measured Trajectories in Time") + plt.title("Polar Trajectories in Time") diff --git a/src/autodyn/core/lifters/__init__.py b/src/autodyn/core/lifters/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/autodyn/core/lifters/legendre.py b/src/autodyn/core/lifters/legendre.py new file mode 100644 index 0000000..f334cbb --- /dev/null +++ b/src/autodyn/core/lifters/legendre.py @@ -0,0 +1,24 @@ +from typing import Optional +import jax.numpy as np +import numpy as nnp + + +def L_lift(x): + + a * x + a * x**3 + + +def lift( + trajectory: np.ndarray, + M: int, + U: Optional[np.ndarray] = None, +): + if trajectory.ndim != 2: + raise NotImplemented + + N, T = trajectory.shape + if U is None: + U = nnp.random.multivariate_normal(nnp.zeros(M), nnp.eye(M, N), size=(M, N)) + + # need an array of callables + U @ f(trajectory) diff --git a/src/autodyn/models/neuro/jr.py b/src/autodyn/models/neuro/jr.py new file mode 100644 index 0000000..e69de29 diff --git a/src/autodyn/models/neuro/scalar.py b/src/autodyn/models/neuro/scalar.py new file mode 100644 index 0000000..2d2c241 --- /dev/null +++ b/src/autodyn/models/neuro/scalar.py @@ -0,0 +1,14 @@ +from numpy import cos + + +def kuramoto(x, g=None, u=0, **kwargs): + D = kwargs["params"]["D"] + w = kwargs["params"]["w"] + + new_x = w - D @ cos(D.T @ x) + + return new_x + + +def delay_kuramoto(x, g=None, u=0, **kwargs): + raise NotImplemented