edmkit 0.0.13__tar.gz → 0.0.14__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {edmkit-0.0.13 → edmkit-0.0.14}/PKG-INFO +1 -1
- {edmkit-0.0.13 → edmkit-0.0.14}/pyproject.toml +2 -1
- {edmkit-0.0.13 → edmkit-0.0.14}/pyproject.toml.orig +2 -1
- edmkit-0.0.14/src/edmkit/generate/__init__.py +10 -0
- edmkit-0.0.14/src/edmkit/generate/ctrnn.py +25 -0
- {edmkit-0.0.13 → edmkit-0.0.14}/src/edmkit/generate/double_pendulum.py +6 -30
- edmkit-0.0.14/src/edmkit/generate/duffing.py +31 -0
- edmkit-0.0.14/src/edmkit/generate/lorenz.py +27 -0
- edmkit-0.0.14/src/edmkit/generate/lorenz96.py +25 -0
- edmkit-0.0.14/src/edmkit/generate/rk45.py +85 -0
- edmkit-0.0.14/src/edmkit/generate/rossler.py +27 -0
- edmkit-0.0.13/src/edmkit/generate/__init__.py +0 -5
- edmkit-0.0.13/src/edmkit/generate/lorenz.py +0 -49
- {edmkit-0.0.13 → edmkit-0.0.14}/README.md +0 -0
- {edmkit-0.0.13 → edmkit-0.0.14}/src/edmkit/ccm.py +0 -0
- {edmkit-0.0.13 → edmkit-0.0.14}/src/edmkit/embedding.py +0 -0
- {edmkit-0.0.13 → edmkit-0.0.14}/src/edmkit/generate/mackey_glass.py +0 -0
- {edmkit-0.0.13 → edmkit-0.0.14}/src/edmkit/metrics/__init__.py +0 -0
- {edmkit-0.0.13 → edmkit-0.0.14}/src/edmkit/metrics/mae.py +0 -0
- {edmkit-0.0.13 → edmkit-0.0.14}/src/edmkit/metrics/pearson_correlation.py +0 -0
- {edmkit-0.0.13 → edmkit-0.0.14}/src/edmkit/metrics/rmse.py +0 -0
- {edmkit-0.0.13 → edmkit-0.0.14}/src/edmkit/simplex_projection/__init__.py +0 -0
- {edmkit-0.0.13 → edmkit-0.0.14}/src/edmkit/simplex_projection/knn.py +0 -0
- {edmkit-0.0.13 → edmkit-0.0.14}/src/edmkit/simplex_projection/simplex_projection.py +0 -0
- {edmkit-0.0.13 → edmkit-0.0.14}/src/edmkit/simplex_projection/soft.py +0 -0
- {edmkit-0.0.13 → edmkit-0.0.14}/src/edmkit/smap.py +0 -0
- {edmkit-0.0.13 → edmkit-0.0.14}/src/edmkit/splits.py +0 -0
- {edmkit-0.0.13 → edmkit-0.0.14}/src/edmkit/theiler_window.py +0 -0
- {edmkit-0.0.13 → edmkit-0.0.14}/src/edmkit/types.py +0 -0
- {edmkit-0.0.13 → edmkit-0.0.14}/src/edmkit/util.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "edmkit"
|
|
3
|
-
version = "0.0.
|
|
3
|
+
version = "0.0.14"
|
|
4
4
|
description = "Simple EDM (Empirical Dynamic Modeling) library"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">= 3.13"
|
|
@@ -20,6 +20,7 @@ email = "tenma.x0@gmail.com"
|
|
|
20
20
|
dev = [
|
|
21
21
|
"griffe2md>=1.5.0",
|
|
22
22
|
"hypothesis>=6.151.9",
|
|
23
|
+
"matplotlib>=3.11.2",
|
|
23
24
|
"pytest>=9.1.1",
|
|
24
25
|
"ruff>=0.15.20",
|
|
25
26
|
"ty>=0.0.56",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "edmkit"
|
|
3
|
-
version = "0.0.
|
|
3
|
+
version = "0.0.14"
|
|
4
4
|
description = "Simple EDM (Empirical Dynamic Modeling) library"
|
|
5
5
|
authors = [{ name = "FUJISHIGE TEMMA", email = "tenma.x0@gmail.com" }]
|
|
6
6
|
readme = "README.md"
|
|
@@ -17,6 +17,7 @@ dependencies = [
|
|
|
17
17
|
dev = [
|
|
18
18
|
"griffe2md>=1.5.0",
|
|
19
19
|
"hypothesis>=6.151.9",
|
|
20
|
+
"matplotlib>=3.11.2",
|
|
20
21
|
"pytest>=9.1.1",
|
|
21
22
|
"ruff>=0.15.20",
|
|
22
23
|
"ty>=0.0.56",
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from .ctrnn import ctrnn
|
|
2
|
+
from .double_pendulum import double_pendulum, to_xy
|
|
3
|
+
from .duffing import duffing
|
|
4
|
+
from .lorenz import lorenz
|
|
5
|
+
from .lorenz96 import lorenz96
|
|
6
|
+
from .mackey_glass import mackey_glass
|
|
7
|
+
from .rk45 import rk45
|
|
8
|
+
from .rossler import rossler
|
|
9
|
+
|
|
10
|
+
__all__ = ["ctrnn", "double_pendulum", "duffing", "lorenz", "lorenz96", "mackey_glass", "rk45", "rossler", "to_xy"]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
__all__ = ["ctrnn"]
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def ctrnn(g: float, J: np.ndarray):
|
|
7
|
+
"""Vector field of a continuous-time recurrent neural network ``dx/dt = -x + g J tanh(x)``.
|
|
8
|
+
|
|
9
|
+
Parameters
|
|
10
|
+
----------
|
|
11
|
+
g : float
|
|
12
|
+
Gain (chaos for ``g > 1`` when ``J`` is large and random with variance ``1 / N``).
|
|
13
|
+
J : np.ndarray
|
|
14
|
+
Connectivity of shape ``(N, N)``; ``J[i, j]`` is the weight with which unit ``j`` drives unit ``i``.
|
|
15
|
+
|
|
16
|
+
Returns
|
|
17
|
+
-------
|
|
18
|
+
f : Callable[[float, np.ndarray], np.ndarray]
|
|
19
|
+
Right-hand side ``f(t, x)`` for a state of shape ``(N,)``.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def f(t: float, x: np.ndarray):
|
|
23
|
+
return -x + g * J @ np.tanh(x)
|
|
24
|
+
|
|
25
|
+
return f
|
|
@@ -3,17 +3,8 @@ import numpy as np
|
|
|
3
3
|
__all__ = ["double_pendulum", "to_xy"]
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
def double_pendulum(
|
|
7
|
-
|
|
8
|
-
m2: float,
|
|
9
|
-
L1: float,
|
|
10
|
-
L2: float,
|
|
11
|
-
g: float,
|
|
12
|
-
X0: np.ndarray,
|
|
13
|
-
dt: float,
|
|
14
|
-
t_max: int,
|
|
15
|
-
):
|
|
16
|
-
"""Generate double pendulum dynamics via forward Euler integration.
|
|
6
|
+
def double_pendulum(m1: float, m2: float, L1: float, L2: float, g: float):
|
|
7
|
+
"""Vector field of the double pendulum.
|
|
17
8
|
|
|
18
9
|
Parameters
|
|
19
10
|
----------
|
|
@@ -27,22 +18,14 @@ def double_pendulum(
|
|
|
27
18
|
Length of second pendulum.
|
|
28
19
|
g : float
|
|
29
20
|
Gravitational acceleration.
|
|
30
|
-
X0 : np.ndarray
|
|
31
|
-
Initial state ``(theta1, theta2, omega1, omega2)`` of shape ``(4,)``.
|
|
32
|
-
dt : float
|
|
33
|
-
Integration time step.
|
|
34
|
-
t_max : int
|
|
35
|
-
Maximum time.
|
|
36
21
|
|
|
37
22
|
Returns
|
|
38
23
|
-------
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
X : np.ndarray
|
|
42
|
-
State trajectory of shape ``(N, 4)``.
|
|
24
|
+
f : Callable[[float, np.ndarray], np.ndarray]
|
|
25
|
+
Right-hand side ``f(t, x)`` for a state ``(theta1, theta2, omega1, omega2)`` of shape ``(4,)``.
|
|
43
26
|
"""
|
|
44
27
|
|
|
45
|
-
def f(x: np.ndarray):
|
|
28
|
+
def f(t: float, x: np.ndarray):
|
|
46
29
|
theta1, theta2, omega1, omega2 = x
|
|
47
30
|
delta = theta1 - theta2
|
|
48
31
|
|
|
@@ -63,14 +46,7 @@ def double_pendulum(
|
|
|
63
46
|
|
|
64
47
|
return np.array([dtheta1_dt, dtheta2_dt, domega1_dt, domega2_dt])
|
|
65
48
|
|
|
66
|
-
|
|
67
|
-
X = np.zeros((len(t), 4))
|
|
68
|
-
X[0] = X0
|
|
69
|
-
|
|
70
|
-
for i in range(1, len(t)):
|
|
71
|
-
X[i] = X[i - 1] + dt * f(X[i - 1])
|
|
72
|
-
|
|
73
|
-
return t, X
|
|
49
|
+
return f
|
|
74
50
|
|
|
75
51
|
|
|
76
52
|
def to_xy(L1: float, L2: float, theta1: np.ndarray, theta2: np.ndarray):
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
__all__ = ["duffing"]
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def duffing(alpha: float, beta: float, delta: float, gamma: float, omega: float):
|
|
7
|
+
"""Vector field of the forced Duffing oscillator.
|
|
8
|
+
|
|
9
|
+
Parameters
|
|
10
|
+
----------
|
|
11
|
+
alpha : float
|
|
12
|
+
Linear stiffness (typical: -1).
|
|
13
|
+
beta : float
|
|
14
|
+
Cubic stiffness (typical: 1).
|
|
15
|
+
delta : float
|
|
16
|
+
Damping coefficient (typical: 0.3).
|
|
17
|
+
gamma : float
|
|
18
|
+
Forcing amplitude (typical: 0.5 for chaos).
|
|
19
|
+
omega : float
|
|
20
|
+
Forcing angular frequency (typical: 1.2).
|
|
21
|
+
|
|
22
|
+
Returns
|
|
23
|
+
-------
|
|
24
|
+
f : Callable[[float, np.ndarray], np.ndarray]
|
|
25
|
+
Right-hand side ``f(t, x)`` for a state ``(x, v)`` of shape ``(2,)``.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def f(t: float, x: np.ndarray):
|
|
29
|
+
return np.array([[0, 1], [-alpha - beta * x[0] ** 2, -delta]]) @ x + np.array([0, gamma * np.cos(omega * t)])
|
|
30
|
+
|
|
31
|
+
return f
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
__all__ = ["lorenz"]
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def lorenz(sigma: float, rho: float, beta: float):
|
|
7
|
+
"""Vector field of the Lorenz system.
|
|
8
|
+
|
|
9
|
+
Parameters
|
|
10
|
+
----------
|
|
11
|
+
sigma : float
|
|
12
|
+
Prandtl number (typical: 10).
|
|
13
|
+
rho : float
|
|
14
|
+
Rayleigh number (typical: 28).
|
|
15
|
+
beta : float
|
|
16
|
+
Geometric factor (typical: 8/3).
|
|
17
|
+
|
|
18
|
+
Returns
|
|
19
|
+
-------
|
|
20
|
+
f : Callable[[float, np.ndarray], np.ndarray]
|
|
21
|
+
Right-hand side ``f(t, x)`` for a state ``(x, y, z)`` of shape ``(3,)``.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def f(t: float, x: np.ndarray):
|
|
25
|
+
return np.array([[-sigma, sigma, 0], [rho, -1, -x[0]], [0, x[0], -beta]]) @ x
|
|
26
|
+
|
|
27
|
+
return f
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
__all__ = ["lorenz96"]
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def lorenz96(F: float):
|
|
7
|
+
"""Vector field of the Lorenz 96 system.
|
|
8
|
+
|
|
9
|
+
Parameters
|
|
10
|
+
----------
|
|
11
|
+
F : float
|
|
12
|
+
Forcing constant (typical: 8 for chaos).
|
|
13
|
+
|
|
14
|
+
Returns
|
|
15
|
+
-------
|
|
16
|
+
f : Callable[[float, np.ndarray], np.ndarray]
|
|
17
|
+
Right-hand side ``f(t, x)`` for a state of shape ``(D,)`` with ``D >= 4``.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def f(t: float, x: np.ndarray):
|
|
21
|
+
# p[i + 2] is x[i] on the ring, so this is (x[i + 1] - x[i - 2]) * x[i - 1] - x[i] + F
|
|
22
|
+
p = np.concatenate([x[-2:], x, x[:1]])
|
|
23
|
+
return (p[3:] - p[:-3]) * p[1:-2] - x + F
|
|
24
|
+
|
|
25
|
+
return f
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
from collections.abc import Callable
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
__all__ = ["rk45"]
|
|
6
|
+
|
|
7
|
+
# Dormand-Prince 5(4)
|
|
8
|
+
A = np.array(
|
|
9
|
+
[
|
|
10
|
+
[0, 0, 0, 0, 0, 0],
|
|
11
|
+
[1 / 5, 0, 0, 0, 0, 0],
|
|
12
|
+
[3 / 40, 9 / 40, 0, 0, 0, 0],
|
|
13
|
+
[44 / 45, -56 / 15, 32 / 9, 0, 0, 0],
|
|
14
|
+
[19372 / 6561, -25360 / 2187, 64448 / 6561, -212 / 729, 0, 0],
|
|
15
|
+
[9017 / 3168, -355 / 33, 46732 / 5247, 49 / 176, -5103 / 18656, 0],
|
|
16
|
+
[35 / 384, 0, 500 / 1113, 125 / 192, -2187 / 6784, 11 / 84],
|
|
17
|
+
]
|
|
18
|
+
)
|
|
19
|
+
C = A.sum(axis=1)
|
|
20
|
+
E = np.array([71 / 57600, 0, -71 / 16695, 71 / 1920, -17253 / 339200, 22 / 525, -1 / 40])
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def rk45(
|
|
24
|
+
f: Callable[[float, np.ndarray], np.ndarray],
|
|
25
|
+
X0: np.ndarray,
|
|
26
|
+
dt: float,
|
|
27
|
+
t_max: int,
|
|
28
|
+
rtol: float = 1e-6,
|
|
29
|
+
atol: float = 1e-9,
|
|
30
|
+
):
|
|
31
|
+
"""Integrate ``dx/dt = f(t, x)`` via the adaptive Dormand-Prince RK45 method.
|
|
32
|
+
|
|
33
|
+
The step size adapts between samples, while the output stays on the uniform grid of spacing ``dt``.
|
|
34
|
+
|
|
35
|
+
Parameters
|
|
36
|
+
----------
|
|
37
|
+
f : Callable[[float, np.ndarray], np.ndarray]
|
|
38
|
+
Right-hand side of the system.
|
|
39
|
+
X0 : np.ndarray
|
|
40
|
+
Initial condition of shape ``(D,)``.
|
|
41
|
+
dt : float
|
|
42
|
+
Sampling time step.
|
|
43
|
+
t_max : int
|
|
44
|
+
Maximum time.
|
|
45
|
+
rtol : float
|
|
46
|
+
Relative tolerance of the local error.
|
|
47
|
+
atol : float
|
|
48
|
+
Absolute tolerance of the local error.
|
|
49
|
+
|
|
50
|
+
Returns
|
|
51
|
+
-------
|
|
52
|
+
t : np.ndarray
|
|
53
|
+
Time array.
|
|
54
|
+
X : np.ndarray
|
|
55
|
+
Trajectory of shape ``(N, D)``.
|
|
56
|
+
"""
|
|
57
|
+
t = np.arange(0, t_max, dt)
|
|
58
|
+
X = np.zeros((len(t), len(X0)))
|
|
59
|
+
X[0] = X0
|
|
60
|
+
|
|
61
|
+
K = np.zeros((7, len(X0)))
|
|
62
|
+
h = dt
|
|
63
|
+
|
|
64
|
+
for i in range(1, len(t)):
|
|
65
|
+
s, x = t[i - 1], X[i - 1]
|
|
66
|
+
|
|
67
|
+
while s < t[i]:
|
|
68
|
+
last = h >= t[i] - s
|
|
69
|
+
step = t[i] - s if last else h
|
|
70
|
+
|
|
71
|
+
for j in range(7):
|
|
72
|
+
K[j] = f(s + C[j] * step, x + step * (A[j, :j] @ K[:j]))
|
|
73
|
+
|
|
74
|
+
x_new = x + step * (A[6] @ K[:6])
|
|
75
|
+
error = np.max(np.abs(step * (E @ K)) / (atol + rtol * np.maximum(np.abs(x), np.abs(x_new))))
|
|
76
|
+
accepted = error <= 1
|
|
77
|
+
|
|
78
|
+
if accepted:
|
|
79
|
+
s, x = t[i] if last else s + step, x_new
|
|
80
|
+
if not (accepted and last):
|
|
81
|
+
h = step * min(10, max(0.2, 0.9 * error**-0.2)) if error > 0 else step * 10
|
|
82
|
+
|
|
83
|
+
X[i] = x
|
|
84
|
+
|
|
85
|
+
return t, X
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
__all__ = ["rossler"]
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def rossler(a: float, b: float, c: float):
|
|
7
|
+
"""Vector field of the Rössler system.
|
|
8
|
+
|
|
9
|
+
Parameters
|
|
10
|
+
----------
|
|
11
|
+
a : float
|
|
12
|
+
Spiral growth rate (typical: 0.2).
|
|
13
|
+
b : float
|
|
14
|
+
Constant injection into z (typical: 0.2).
|
|
15
|
+
c : float
|
|
16
|
+
Folding threshold (typical: 5.7).
|
|
17
|
+
|
|
18
|
+
Returns
|
|
19
|
+
-------
|
|
20
|
+
f : Callable[[float, np.ndarray], np.ndarray]
|
|
21
|
+
Right-hand side ``f(t, x)`` for a state ``(x, y, z)`` of shape ``(3,)``.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def f(t: float, x: np.ndarray):
|
|
25
|
+
return np.array([[0, -1, -1], [1, a, 0], [0, 0, x[0] - c]]) @ x + np.array([0, 0, b])
|
|
26
|
+
|
|
27
|
+
return f
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import numpy as np
|
|
2
|
-
|
|
3
|
-
__all__ = ["lorenz"]
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def lorenz(
|
|
7
|
-
sigma: float,
|
|
8
|
-
rho: float,
|
|
9
|
-
beta: float,
|
|
10
|
-
X0: np.ndarray,
|
|
11
|
-
dt: float,
|
|
12
|
-
t_max: int,
|
|
13
|
-
):
|
|
14
|
-
"""Generate a Lorenz system trajectory via forward Euler integration.
|
|
15
|
-
|
|
16
|
-
Parameters
|
|
17
|
-
----------
|
|
18
|
-
sigma : float
|
|
19
|
-
Prandtl number (typical: 10).
|
|
20
|
-
rho : float
|
|
21
|
-
Rayleigh number (typical: 28).
|
|
22
|
-
beta : float
|
|
23
|
-
Geometric factor (typical: 8/3).
|
|
24
|
-
X0 : np.ndarray
|
|
25
|
-
Initial condition of shape ``(3,)``.
|
|
26
|
-
dt : float
|
|
27
|
-
Integration time step.
|
|
28
|
-
t_max : int
|
|
29
|
-
Maximum time.
|
|
30
|
-
|
|
31
|
-
Returns
|
|
32
|
-
-------
|
|
33
|
-
t : np.ndarray
|
|
34
|
-
Time array.
|
|
35
|
-
X : np.ndarray
|
|
36
|
-
Trajectory of shape ``(N, 3)`` for ``(x, y, z)``.
|
|
37
|
-
"""
|
|
38
|
-
|
|
39
|
-
def f(x: np.ndarray):
|
|
40
|
-
return np.array([[-sigma, sigma, 0], [rho, -1, -x[0]], [0, x[0], -beta]]) @ x
|
|
41
|
-
|
|
42
|
-
t = np.arange(0, t_max, dt)
|
|
43
|
-
X = np.zeros((len(t), 3))
|
|
44
|
-
X[0] = X0
|
|
45
|
-
|
|
46
|
-
for i in range(1, len(t)):
|
|
47
|
-
X[i] = X[i - 1] + dt * f(X[i - 1])
|
|
48
|
-
|
|
49
|
-
return t, X
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|