From 8ea2a0c1a4abaa9329ab5d9d865cfe6c24d54f8c Mon Sep 17 00:00:00 2001 From: Vineet Tiruvadi Date: Fri, 5 Aug 2022 00:52:44 +0000 Subject: [PATCH 1/6] initial implement of legendre --- .devcontainer/devcontainer.json | 12 ++++-------- setup.py | 2 +- src/autodyn/core/lifters/__init__.py | 0 src/autodyn/core/lifters/legendre.py | 24 ++++++++++++++++++++++++ 4 files changed, 29 insertions(+), 9 deletions(-) create mode 100644 src/autodyn/core/lifters/__init__.py create mode 100644 src/autodyn/core/lifters/legendre.py diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index dfa4130..054f6f0 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -5,7 +5,7 @@ "build": { "dockerfile": "Dockerfile", "context": "..", - "args": { + "args": { // Update 'VARIANT' to pick a Python version: 3, 3.10, 3.9, 3.8, 3.7, 3.6 // Append -bullseye or -buster to pin to an OS version. // Use -bullseye variants on local on arm64/Apple Silicon. @@ -14,16 +14,16 @@ "NODE_VERSION": "lts/*" } }, - // Configure tool-specific properties. "customizations": { // Configure properties specific to VS Code. "vscode": { // Set *default* container specific settings.json values on container create. - "settings": { + "settings": { "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", @@ -34,7 +34,6 @@ "python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle", "python.linting.pylintPath": "/usr/local/py-utils/bin/pylint" }, - // Add the IDs of extensions you want installed when the container is created. "extensions": [ "ms-python.python", @@ -42,13 +41,10 @@ ] } }, - // Use 'forwardPorts' to make a list of ports inside the container available locally. // "forwardPorts": [], - // Use 'postCreateCommand' to run commands after the container is created. // "postCreateCommand": "pip3 install --user -r requirements.txt", - // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. "remoteUser": "vscode" -} +} \ No newline at end of file diff --git a/setup.py b/setup.py index 538107a..c9f397c 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup( name="autodyn", version="0.1.4", - description="A module for differentiable dynamical systems", + description="A library for differentiable dynamical systems", author="Vineet Tiruvadi", author_email="virati@gmail.com", package_dir={"": "src"}, 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) From fb11a21f46135d330f9b9cea25f51818d435a7c6 Mon Sep 17 00:00:00 2001 From: Vineet Tiruvadi Date: Fri, 5 Aug 2022 04:00:52 +0000 Subject: [PATCH 2/6] whole bunch of control, with builder submodule --- scripts/basics/builder_dynamics.py | 6 ++++ scripts/wilson-cowan/basic_setup.py | 1 - scripts/wilson-cowan/neurotransmitter.py | 35 ++++++++++++++++++++++++ src/autodyn/builder/__init__.py | 0 src/autodyn/builder/macro.py | 0 src/autodyn/builder/micro.py | 22 +++++++++++++++ src/autodyn/core/control.py | 3 ++ src/autodyn/core/dynamical.py | 20 +++++++++++++- src/autodyn/models/neuro/jr.py | 0 src/autodyn/models/neuro/kuramoto.py | 13 +++++++++ 10 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 scripts/basics/builder_dynamics.py create mode 100644 scripts/wilson-cowan/neurotransmitter.py create mode 100644 src/autodyn/builder/__init__.py create mode 100644 src/autodyn/builder/macro.py create mode 100644 src/autodyn/builder/micro.py create mode 100644 src/autodyn/core/control.py create mode 100644 src/autodyn/models/neuro/jr.py create mode 100644 src/autodyn/models/neuro/kuramoto.py diff --git a/scripts/basics/builder_dynamics.py b/scripts/basics/builder_dynamics.py new file mode 100644 index 0000000..980f752 --- /dev/null +++ b/scripts/basics/builder_dynamics.py @@ -0,0 +1,6 @@ +from autodyn.builder import microstruct, macrostruct + +neural_mass = microstruct() +brain_network = macrostruct(L=brain_graph) + +main_system = microstruct + macrostruct diff --git a/scripts/wilson-cowan/basic_setup.py b/scripts/wilson-cowan/basic_setup.py index dbce6a6..f06f42b 100644 --- a/scripts/wilson-cowan/basic_setup.py +++ b/scripts/wilson-cowan/basic_setup.py @@ -1,7 +1,6 @@ #%% from autodyn.core import dynamical as dyn from autodyn.models.neuro.wc import wc_drift, wc_input -from autodyn.core.network import connectivity #%% wilson_cowan = dyn.system(wc_drift, D=2) 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 8d2d1e1..d02d0f5 100755 --- a/src/autodyn/core/dynamical.py +++ b/src/autodyn/core/dynamical.py @@ -7,11 +7,13 @@ 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 +23,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 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/kuramoto.py b/src/autodyn/models/neuro/kuramoto.py new file mode 100644 index 0000000..87b4a47 --- /dev/null +++ b/src/autodyn/models/neuro/kuramoto.py @@ -0,0 +1,13 @@ +from numpy import cos + + +def kuramoto(x, g, u, **kwargs): + D = kwargs["D"] + + new_x = -D @ cos(D.T @ x) + + return new_x + + +def delay_kuramoto(x, g, u, **kwargs): + raise NotImplemented From 8ebba4b712e5ef2730b118c0452370bf879c6063 Mon Sep 17 00:00:00 2001 From: Vineet Tiruvadi Date: Fri, 5 Aug 2022 04:19:21 +0000 Subject: [PATCH 3/6] first attempt at kuramoto, looks funny --- scripts/basics/kuramoto_eg.py | 11 +++++++++++ src/autodyn/core/dynamical.py | 9 +++++++-- src/autodyn/models/neuro/kuramoto.py | 13 ------------- src/autodyn/models/neuro/scalar.py | 14 ++++++++++++++ 4 files changed, 32 insertions(+), 15 deletions(-) create mode 100644 scripts/basics/kuramoto_eg.py delete mode 100644 src/autodyn/models/neuro/kuramoto.py create mode 100644 src/autodyn/models/neuro/scalar.py diff --git a/scripts/basics/kuramoto_eg.py b/scripts/basics/kuramoto_eg.py new file mode 100644 index 0000000..78f5592 --- /dev/null +++ b/scripts/basics/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/src/autodyn/core/dynamical.py b/src/autodyn/core/dynamical.py index d02d0f5..cf52d0b 100755 --- a/src/autodyn/core/dynamical.py +++ b/src/autodyn/core/dynamical.py @@ -101,7 +101,12 @@ def plot_raster(self): plt.plot(self.raster) plt.show() - def plot_phase(self): + def plot_phase(self, d1, d2): + 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.gca(projection="3d") @@ -121,4 +126,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/models/neuro/kuramoto.py b/src/autodyn/models/neuro/kuramoto.py deleted file mode 100644 index 87b4a47..0000000 --- a/src/autodyn/models/neuro/kuramoto.py +++ /dev/null @@ -1,13 +0,0 @@ -from numpy import cos - - -def kuramoto(x, g, u, **kwargs): - D = kwargs["D"] - - new_x = -D @ cos(D.T @ x) - - return new_x - - -def delay_kuramoto(x, g, u, **kwargs): - raise NotImplemented 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 From 0bb706ac1e7adf3c362456f957af6906ab066c47 Mon Sep 17 00:00:00 2001 From: Vineet Tiruvadi Date: Tue, 25 Mar 2025 19:52:20 +0000 Subject: [PATCH 4/6] fixed basic setup --- scripts/basics/basic_setup.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/scripts/basics/basic_setup.py b/scripts/basics/basic_setup.py index 0108097..e19c480 100644 --- a/scripts/basics/basic_setup.py +++ b/scripts/basics/basic_setup.py @@ -11,7 +11,7 @@ #%% 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() +lorenz_sys.plot_phase(d1=0,d2=1) #%% @@ -19,8 +19,7 @@ brain_net = connectivity(10, proportion=0.4) test_sys.simulate(T=100, dt=0.1, D=brain_net.D.T) - -test_sys.plot_phase() +test_sys.plot_phase(d1=0,d2=2) brain_net.plot_incidence() brain_net.plot_spectrum() @@ -47,6 +46,4 @@ stim=stim, keep_positive=True, ) -hopf_single.plot_polar() - -# %% +hopf_single.plot_polar() \ No newline at end of file From 3831e25e3bfe9d036ed831bb88c49941f6eb0e6d Mon Sep 17 00:00:00 2001 From: Vineet Tiruvadi Date: Tue, 25 Mar 2025 19:55:49 +0000 Subject: [PATCH 5/6] fixes to get plotting working --- scripts/wilson-cowan/basic_setup.py | 3 +-- src/autodyn/core/dynamical.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/scripts/wilson-cowan/basic_setup.py b/scripts/wilson-cowan/basic_setup.py index f06f42b..ed340ac 100644 --- a/scripts/wilson-cowan/basic_setup.py +++ b/scripts/wilson-cowan/basic_setup.py @@ -1,6 +1,7 @@ #%% from autodyn.core import dynamical as dyn from autodyn.models.neuro.wc import wc_drift, wc_input +import numpy as np #%% wilson_cowan = dyn.system(wc_drift, D=2) @@ -21,8 +22,6 @@ T = 100 dt = 0.1 tpts = int(T // dt) + 1 -import numpy as np - tvect = np.linspace(0, T, tpts) u = np.zeros_like(tvect) diff --git a/src/autodyn/core/dynamical.py b/src/autodyn/core/dynamical.py index cf52d0b..187137f 100755 --- a/src/autodyn/core/dynamical.py +++ b/src/autodyn/core/dynamical.py @@ -101,7 +101,7 @@ def plot_raster(self): plt.plot(self.raster) plt.show() - def plot_phase(self, d1, d2): + def plot_phase(self, d1=0, d2=1): fig = plt.figure() plt.plot(self.raster[:, d1], self.raster[:, d2]) plt.title("Phase") From cd4d256c6db67055337cfe93c5ab12dd3ff43d58 Mon Sep 17 00:00:00 2001 From: Vineet Tiruvadi Date: Tue, 25 Mar 2025 20:53:15 +0000 Subject: [PATCH 6/6] new split out examples --- .devcontainer/devcontainer.json | 3 +- scripts/basics/basic_setup.py | 48 ++----------------- scripts/basics/builder_dynamics.py | 2 + scripts/basics/sys_types/consensus.py | 13 +++++ scripts/basics/sys_types/controlled_hopf.py | 27 +++++++++++ scripts/basics/{ => sys_types}/kuramoto_eg.py | 4 +- src/autodyn/core/dynamical.py | 9 ---- 7 files changed, 50 insertions(+), 56 deletions(-) create mode 100644 scripts/basics/sys_types/consensus.py create mode 100644 scripts/basics/sys_types/controlled_hopf.py rename scripts/basics/{ => sys_types}/kuramoto_eg.py (96%) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index fac5ec4..ccc2f9a 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -39,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 d96d771..ae8c67a 100644 --- a/scripts/basics/basic_setup.py +++ b/scripts/basics/basic_setup.py @@ -1,50 +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(d1=0,d2=1) - - -#%% -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() -#%% -# 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() \ No newline at end of file +lorenz_sys.plot_phase(d1=0, d2=1) diff --git a/scripts/basics/builder_dynamics.py b/scripts/basics/builder_dynamics.py index 980f752..2003b4d 100644 --- a/scripts/basics/builder_dynamics.py +++ b/scripts/basics/builder_dynamics.py @@ -1,5 +1,7 @@ from autodyn.builder import microstruct, macrostruct +import networkx as nx +# %% neural_mass = microstruct() brain_network = macrostruct(L=brain_graph) 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/kuramoto_eg.py b/scripts/basics/sys_types/kuramoto_eg.py similarity index 96% rename from scripts/basics/kuramoto_eg.py rename to scripts/basics/sys_types/kuramoto_eg.py index 78f5592..9e0dc48 100644 --- a/scripts/basics/kuramoto_eg.py +++ b/scripts/basics/sys_types/kuramoto_eg.py @@ -1,9 +1,9 @@ -#%% +# %% 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) diff --git a/src/autodyn/core/dynamical.py b/src/autodyn/core/dynamical.py index 0a01f2b..a7b259b 100755 --- a/src/autodyn/core/dynamical.py +++ b/src/autodyn/core/dynamical.py @@ -1,12 +1,3 @@ -#!/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