pynamicalsys 1.3.0__py3-none-any.whl → 1.4.0__py3-none-any.whl
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.
- pynamicalsys/__init__.py +2 -0
- pynamicalsys/__version__.py +2 -2
- pynamicalsys/common/recurrence_quantification_analysis.py +1 -1
- pynamicalsys/common/time_series_metrics.py +85 -0
- pynamicalsys/common/utils.py +3 -3
- pynamicalsys/continuous_time/chaotic_indicators.py +306 -8
- pynamicalsys/continuous_time/models.py +25 -0
- pynamicalsys/continuous_time/numerical_integrators.py +7 -7
- pynamicalsys/continuous_time/trajectory_analysis.py +460 -13
- pynamicalsys/core/continuous_dynamical_systems.py +933 -35
- pynamicalsys/core/discrete_dynamical_systems.py +20 -9
- pynamicalsys/core/hamiltonian_systems.py +1193 -0
- pynamicalsys/core/time_series_metrics.py +65 -0
- pynamicalsys/discrete_time/dynamical_indicators.py +13 -102
- pynamicalsys/discrete_time/models.py +2 -2
- pynamicalsys/discrete_time/trajectory_analysis.py +10 -10
- pynamicalsys/discrete_time/transport.py +1 -1
- pynamicalsys/hamiltonian_systems/__init__.py +16 -0
- pynamicalsys/hamiltonian_systems/chaotic_indicators.py +638 -0
- pynamicalsys/hamiltonian_systems/models.py +68 -0
- pynamicalsys/hamiltonian_systems/numerical_integrators.py +248 -0
- pynamicalsys/hamiltonian_systems/trajectory_analysis.py +293 -0
- pynamicalsys/hamiltonian_systems/validators.py +114 -0
- {pynamicalsys-1.3.0.dist-info → pynamicalsys-1.4.0.dist-info}/METADATA +37 -8
- pynamicalsys-1.4.0.dist-info/RECORD +36 -0
- pynamicalsys-1.3.0.dist-info/RECORD +0 -28
- {pynamicalsys-1.3.0.dist-info → pynamicalsys-1.4.0.dist-info}/WHEEL +0 -0
- {pynamicalsys-1.3.0.dist-info → pynamicalsys-1.4.0.dist-info}/top_level.txt +0 -0
pynamicalsys/__init__.py
CHANGED
@@ -17,6 +17,7 @@
|
|
17
17
|
|
18
18
|
from pynamicalsys.core.discrete_dynamical_systems import DiscreteDynamicalSystem
|
19
19
|
from pynamicalsys.core.continuous_dynamical_systems import ContinuousDynamicalSystem
|
20
|
+
from pynamicalsys.core.hamiltonian_systems import HamiltonianSystem
|
20
21
|
from pynamicalsys.core.basin_metrics import BasinMetrics
|
21
22
|
from pynamicalsys.core.plot_styler import PlotStyler
|
22
23
|
from pynamicalsys.core.time_series_metrics import TimeSeriesMetrics
|
@@ -25,6 +26,7 @@ from .__version__ import __version__
|
|
25
26
|
__all__ = [
|
26
27
|
"DiscreteDynamicalSystem",
|
27
28
|
"ContinuousDynamicalSystem",
|
29
|
+
"HamiltonianSystem",
|
28
30
|
"PlotStyler",
|
29
31
|
"TimeSeriesMetrics",
|
30
32
|
"BasinMetrics",
|
pynamicalsys/__version__.py
CHANGED
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
28
28
|
commit_id: COMMIT_ID
|
29
29
|
__commit_id__: COMMIT_ID
|
30
30
|
|
31
|
-
__version__ = version = '1.
|
32
|
-
__version_tuple__ = version_tuple = (1,
|
31
|
+
__version__ = version = '1.4.0'
|
32
|
+
__version_tuple__ = version_tuple = (1, 4, 0)
|
33
33
|
|
34
34
|
__commit_id__ = commit_id = None
|
@@ -0,0 +1,85 @@
|
|
1
|
+
from typing import Optional, Tuple, Union, Callable
|
2
|
+
from numpy.typing import NDArray
|
3
|
+
import numpy as np
|
4
|
+
from numba import njit, prange
|
5
|
+
|
6
|
+
from pynamicalsys.common.utils import fit_poly
|
7
|
+
|
8
|
+
|
9
|
+
@njit(parallel=True)
|
10
|
+
def hurst_exponent(
|
11
|
+
time_series: NDArray[np.float64],
|
12
|
+
wmin: int = 2,
|
13
|
+
) -> NDArray[np.float64]:
|
14
|
+
|
15
|
+
time_series = time_series.copy()
|
16
|
+
|
17
|
+
sample_size, neq = time_series.shape
|
18
|
+
|
19
|
+
H = np.zeros(neq)
|
20
|
+
|
21
|
+
ells = np.arange(wmin, sample_size // 2)
|
22
|
+
log_ells = np.log(ells)
|
23
|
+
RS = np.empty((ells.shape[0], neq))
|
24
|
+
|
25
|
+
for j in prange(neq):
|
26
|
+
series = time_series[:, j]
|
27
|
+
|
28
|
+
# Precompute cumulative sums and cumulative sums of squares
|
29
|
+
cum_sum = np.zeros(sample_size)
|
30
|
+
cum_sum_sq = np.zeros(sample_size)
|
31
|
+
cum_sum[0] = series[0]
|
32
|
+
cum_sum_sq[0] = series[0] ** 2
|
33
|
+
for t in range(1, sample_size):
|
34
|
+
cum_sum[t] = cum_sum[t - 1] + series[t]
|
35
|
+
cum_sum_sq[t] = cum_sum_sq[t - 1] + series[t] ** 2
|
36
|
+
|
37
|
+
for i, ell in enumerate(ells):
|
38
|
+
num_blocks = sample_size // ell
|
39
|
+
R_over_S = np.zeros(num_blocks)
|
40
|
+
|
41
|
+
for block in range(num_blocks):
|
42
|
+
start = block * ell
|
43
|
+
end = start + ell
|
44
|
+
|
45
|
+
# Mean using cumulative sums
|
46
|
+
block_sum = cum_sum[end - 1] - (cum_sum[start - 1] if start > 0 else 0)
|
47
|
+
block_mean = block_sum / ell
|
48
|
+
|
49
|
+
# Variance using cumulative sums of squares
|
50
|
+
block_sum_sq = cum_sum_sq[end - 1] - (
|
51
|
+
cum_sum_sq[start - 1] if start > 0 else 0
|
52
|
+
)
|
53
|
+
var = block_sum_sq / ell - block_mean**2
|
54
|
+
S = np.sqrt(var) if var > 0 else 0
|
55
|
+
|
56
|
+
# Cumulative sum of mean-adjusted series for range
|
57
|
+
max_Z = 0.0
|
58
|
+
min_Z = 0.0
|
59
|
+
cumsum = 0.0
|
60
|
+
for k in range(start, end):
|
61
|
+
cumsum += series[k] - block_mean
|
62
|
+
if cumsum > max_Z:
|
63
|
+
max_Z = cumsum
|
64
|
+
if cumsum < min_Z:
|
65
|
+
min_Z = cumsum
|
66
|
+
R = max_Z - min_Z
|
67
|
+
|
68
|
+
R_over_S[block] = R / S if S > 0 else 0.0
|
69
|
+
|
70
|
+
positive_mask = R_over_S > 0
|
71
|
+
RS[i, j] = (
|
72
|
+
np.mean(R_over_S[positive_mask]) if np.any(positive_mask) else 0.0
|
73
|
+
)
|
74
|
+
|
75
|
+
# Linear regression in log-log space
|
76
|
+
positive_inds = np.where(RS[:, j] > 0)[0]
|
77
|
+
if positive_inds.size == 0:
|
78
|
+
H[j] = 0.0
|
79
|
+
else:
|
80
|
+
x_fit = log_ells[positive_inds]
|
81
|
+
y_fit = np.log(RS[positive_inds, j])
|
82
|
+
fitting = fit_poly(x_fit, y_fit, 1)
|
83
|
+
H[j] = fitting[0]
|
84
|
+
|
85
|
+
return H
|
pynamicalsys/common/utils.py
CHANGED
@@ -21,7 +21,7 @@ from numpy.typing import NDArray
|
|
21
21
|
from numba import njit
|
22
22
|
|
23
23
|
|
24
|
-
@njit
|
24
|
+
@njit
|
25
25
|
def qr(M: NDArray[np.float64]) -> Tuple[NDArray[np.float64], NDArray[np.float64]]:
|
26
26
|
"""
|
27
27
|
Perform numerically stable QR decomposition using modified Gram-Schmidt with reorthogonalization.
|
@@ -92,7 +92,7 @@ def qr(M: NDArray[np.float64]) -> Tuple[NDArray[np.float64], NDArray[np.float64]
|
|
92
92
|
return Q, R
|
93
93
|
|
94
94
|
|
95
|
-
@njit
|
95
|
+
@njit
|
96
96
|
def householder_qr(
|
97
97
|
M: NDArray[np.float64],
|
98
98
|
) -> Tuple[NDArray[np.float64], NDArray[np.float64]]:
|
@@ -184,7 +184,7 @@ def householder_qr(
|
|
184
184
|
return Q, R
|
185
185
|
|
186
186
|
|
187
|
-
@njit
|
187
|
+
@njit
|
188
188
|
def finite_difference_jacobian(
|
189
189
|
u: NDArray[np.float64],
|
190
190
|
parameters: NDArray[np.float64],
|
@@ -15,17 +15,33 @@
|
|
15
15
|
# You should have received a copy of the GNU General Public License
|
16
16
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
17
17
|
|
18
|
-
from typing import Optional, Callable,
|
18
|
+
from typing import Optional, Callable, Tuple
|
19
19
|
from numpy.typing import NDArray
|
20
20
|
import numpy as np
|
21
21
|
from numba import njit
|
22
22
|
|
23
|
-
from pynamicalsys.common.utils import qr, wedge_norm
|
24
|
-
|
23
|
+
from pynamicalsys.common.utils import qr, wedge_norm, fit_poly
|
24
|
+
|
25
|
+
from pynamicalsys.continuous_time.trajectory_analysis import (
|
26
|
+
generate_maxima_map,
|
27
|
+
step,
|
28
|
+
evolve_system,
|
29
|
+
generate_poincare_section,
|
30
|
+
generate_stroboscopic_map,
|
31
|
+
)
|
32
|
+
|
25
33
|
from pynamicalsys.continuous_time.numerical_integrators import rk4_step_wrapped
|
26
34
|
|
35
|
+
from pynamicalsys.common.recurrence_quantification_analysis import (
|
36
|
+
RTEConfig,
|
37
|
+
recurrence_matrix,
|
38
|
+
white_vertline_distr,
|
39
|
+
)
|
40
|
+
|
41
|
+
from pynamicalsys.common.time_series_metrics import hurst_exponent
|
27
42
|
|
28
|
-
|
43
|
+
|
44
|
+
@njit
|
29
45
|
def lyapunov_exponents(
|
30
46
|
u: NDArray[np.float64],
|
31
47
|
parameters: NDArray[np.float64],
|
@@ -44,7 +60,6 @@ def lyapunov_exponents(
|
|
44
60
|
integrator=rk4_step_wrapped,
|
45
61
|
return_history: bool = False,
|
46
62
|
seed: int = 13,
|
47
|
-
log_base: float = np.e,
|
48
63
|
QR: Callable[
|
49
64
|
[NDArray[np.float64]], Tuple[NDArray[np.float64], NDArray[np.float64]]
|
50
65
|
] = qr,
|
@@ -110,7 +125,7 @@ def lyapunov_exponents(
|
|
110
125
|
# Perform the QR decomposition
|
111
126
|
v, R = QR(v)
|
112
127
|
# Accumulate the log
|
113
|
-
exponents += np.log(np.abs(np.diag(R)))
|
128
|
+
exponents += np.log(np.abs(np.diag(R)))
|
114
129
|
|
115
130
|
if return_history:
|
116
131
|
result = [time]
|
@@ -136,7 +151,103 @@ def lyapunov_exponents(
|
|
136
151
|
return [result]
|
137
152
|
|
138
153
|
|
139
|
-
@njit
|
154
|
+
@njit
|
155
|
+
def maximum_lyapunov_exponent(
|
156
|
+
u: NDArray[np.float64],
|
157
|
+
parameters: NDArray[np.float64],
|
158
|
+
total_time: float,
|
159
|
+
equations_of_motion: Callable[
|
160
|
+
[np.float64, NDArray[np.float64], NDArray[np.float64]], NDArray[np.float64]
|
161
|
+
],
|
162
|
+
jacobian: Callable[
|
163
|
+
[np.float64, NDArray[np.float64], NDArray[np.float64]], NDArray[np.float64]
|
164
|
+
],
|
165
|
+
transient_time: Optional[float] = None,
|
166
|
+
time_step: float = 0.01,
|
167
|
+
atol: float = 1e-6,
|
168
|
+
rtol: float = 1e-3,
|
169
|
+
integrator=rk4_step_wrapped,
|
170
|
+
return_history: bool = False,
|
171
|
+
seed: int = 13,
|
172
|
+
) -> NDArray[np.float64]:
|
173
|
+
|
174
|
+
neq = len(u) # Number of equations of the system
|
175
|
+
nt = neq + neq # system + variational equations
|
176
|
+
|
177
|
+
u = u.copy()
|
178
|
+
|
179
|
+
# Handle transient time
|
180
|
+
if transient_time is not None:
|
181
|
+
u = evolve_system(
|
182
|
+
u,
|
183
|
+
parameters,
|
184
|
+
transient_time,
|
185
|
+
equations_of_motion,
|
186
|
+
time_step=time_step,
|
187
|
+
atol=atol,
|
188
|
+
rtol=rtol,
|
189
|
+
integrator=integrator,
|
190
|
+
)
|
191
|
+
sample_time = total_time - transient_time
|
192
|
+
time = transient_time
|
193
|
+
else:
|
194
|
+
sample_time = total_time
|
195
|
+
time = 0
|
196
|
+
|
197
|
+
# State + deviation vectors
|
198
|
+
uv = np.zeros(nt)
|
199
|
+
uv[:neq] = u.copy()
|
200
|
+
|
201
|
+
# Randomly define the deviation vectors and orthonormalize them
|
202
|
+
np.random.seed(seed)
|
203
|
+
uv[neq:] = -1 + 2 * np.random.rand(nt - neq)
|
204
|
+
norm = np.linalg.norm(uv[neq:])
|
205
|
+
uv[neq:] /= norm
|
206
|
+
|
207
|
+
exponent = 0.0
|
208
|
+
history = []
|
209
|
+
|
210
|
+
while time < total_time:
|
211
|
+
if time + time_step > total_time:
|
212
|
+
time_step = total_time - time
|
213
|
+
|
214
|
+
uv, time, time_step = step(
|
215
|
+
time,
|
216
|
+
uv,
|
217
|
+
parameters,
|
218
|
+
equations_of_motion,
|
219
|
+
jacobian=jacobian,
|
220
|
+
time_step=time_step,
|
221
|
+
atol=atol,
|
222
|
+
rtol=rtol,
|
223
|
+
integrator=integrator,
|
224
|
+
number_of_deviation_vectors=1,
|
225
|
+
)
|
226
|
+
|
227
|
+
norm = np.linalg.norm(uv[neq:])
|
228
|
+
|
229
|
+
exponent += np.log(np.abs(norm))
|
230
|
+
|
231
|
+
uv[neq:] /= norm
|
232
|
+
|
233
|
+
if return_history:
|
234
|
+
result = [time]
|
235
|
+
result.append(
|
236
|
+
exponent
|
237
|
+
/ (time - (transient_time if transient_time is not None else 0))
|
238
|
+
)
|
239
|
+
history.append(result)
|
240
|
+
|
241
|
+
if return_history:
|
242
|
+
return history
|
243
|
+
else:
|
244
|
+
result = [
|
245
|
+
exponent / (time - (transient_time if transient_time is not None else 0))
|
246
|
+
]
|
247
|
+
return [result]
|
248
|
+
|
249
|
+
|
250
|
+
@njit
|
140
251
|
def SALI(
|
141
252
|
u: NDArray[np.float64],
|
142
253
|
parameters: NDArray[np.float64],
|
@@ -238,7 +349,6 @@ def SALI(
|
|
238
349
|
return [[time, sali]]
|
239
350
|
|
240
351
|
|
241
|
-
# @njit(cache=True)
|
242
352
|
def LDI(
|
243
353
|
u: NDArray[np.float64],
|
244
354
|
parameters: NDArray[np.float64],
|
@@ -441,3 +551,191 @@ def GALI(
|
|
441
551
|
return history
|
442
552
|
else:
|
443
553
|
return [[time, gali]]
|
554
|
+
|
555
|
+
|
556
|
+
def recurrence_time_entropy(
|
557
|
+
u,
|
558
|
+
parameters,
|
559
|
+
num_points,
|
560
|
+
transient_time,
|
561
|
+
equations_of_motion,
|
562
|
+
time_step,
|
563
|
+
atol,
|
564
|
+
rtol,
|
565
|
+
integrator,
|
566
|
+
map_type,
|
567
|
+
section_index,
|
568
|
+
section_value,
|
569
|
+
crossing,
|
570
|
+
sampling_time,
|
571
|
+
maxima_index,
|
572
|
+
**kwargs,
|
573
|
+
):
|
574
|
+
|
575
|
+
# Configuration handling
|
576
|
+
config = RTEConfig(**kwargs)
|
577
|
+
|
578
|
+
# Metric setup
|
579
|
+
metric_map = {"supremum": np.inf, "euclidean": 2, "manhattan": 1}
|
580
|
+
|
581
|
+
try:
|
582
|
+
ord = metric_map[config.std_metric.lower()]
|
583
|
+
except KeyError:
|
584
|
+
raise ValueError(
|
585
|
+
f"Invalid std_metric: {config.std_metric}. Must be {list(metric_map.keys())}"
|
586
|
+
)
|
587
|
+
|
588
|
+
# Generate the Poincaré section or stroboscopic map
|
589
|
+
if map_type == "PS":
|
590
|
+
points = generate_poincare_section(
|
591
|
+
u,
|
592
|
+
parameters,
|
593
|
+
num_points,
|
594
|
+
equations_of_motion,
|
595
|
+
transient_time,
|
596
|
+
time_step,
|
597
|
+
atol,
|
598
|
+
rtol,
|
599
|
+
integrator,
|
600
|
+
section_index,
|
601
|
+
section_value,
|
602
|
+
crossing,
|
603
|
+
)
|
604
|
+
data = points[:, 1:] # Remove time
|
605
|
+
data = np.delete(data, section_index, axis=1)
|
606
|
+
elif map_type == "SM":
|
607
|
+
points = generate_stroboscopic_map(
|
608
|
+
u,
|
609
|
+
parameters,
|
610
|
+
num_points,
|
611
|
+
sampling_time,
|
612
|
+
equations_of_motion,
|
613
|
+
transient_time,
|
614
|
+
time_step,
|
615
|
+
atol,
|
616
|
+
rtol,
|
617
|
+
integrator,
|
618
|
+
)
|
619
|
+
|
620
|
+
data = points[:, 1:] # Remove time
|
621
|
+
else:
|
622
|
+
points = generate_maxima_map(
|
623
|
+
u,
|
624
|
+
parameters,
|
625
|
+
num_points,
|
626
|
+
maxima_index,
|
627
|
+
equations_of_motion,
|
628
|
+
transient_time,
|
629
|
+
time_step,
|
630
|
+
atol,
|
631
|
+
rtol,
|
632
|
+
integrator,
|
633
|
+
)
|
634
|
+
|
635
|
+
data = points[:, 1:] # Remove time
|
636
|
+
|
637
|
+
# Threshold calculation
|
638
|
+
if config.threshold_std:
|
639
|
+
std = np.std(data, axis=0)
|
640
|
+
eps = config.threshold * np.linalg.norm(std, ord=ord)
|
641
|
+
if eps <= 0:
|
642
|
+
eps = 0.1
|
643
|
+
else:
|
644
|
+
eps = config.threshold
|
645
|
+
|
646
|
+
# Recurrence matrix calculation
|
647
|
+
recmat = recurrence_matrix(data, float(eps), metric=config.metric)
|
648
|
+
|
649
|
+
# White line distribution
|
650
|
+
P = white_vertline_distr(recmat)[config.lmin :]
|
651
|
+
P = P[P > 0] # Remove zeros
|
652
|
+
P /= P.sum() # Normalize
|
653
|
+
|
654
|
+
# Entropy calculation
|
655
|
+
rte = -np.sum(P * np.log(P))
|
656
|
+
|
657
|
+
# Prepare output
|
658
|
+
result = [rte]
|
659
|
+
if config.return_final_state:
|
660
|
+
result.append(points[-1])
|
661
|
+
if config.return_recmat:
|
662
|
+
result.append(recmat)
|
663
|
+
if config.return_p:
|
664
|
+
result.append(P)
|
665
|
+
|
666
|
+
return result[0] if len(result) == 1 else tuple(result)
|
667
|
+
|
668
|
+
|
669
|
+
def hurst_exponent_wrapped(
|
670
|
+
u: NDArray[np.float64],
|
671
|
+
parameters: NDArray[np.float64],
|
672
|
+
num_points: int,
|
673
|
+
equations_of_motion: Callable,
|
674
|
+
time_step: float,
|
675
|
+
atol: float,
|
676
|
+
rtol: float,
|
677
|
+
integrator: Callable,
|
678
|
+
map_type: str,
|
679
|
+
section_index: int,
|
680
|
+
section_value: float,
|
681
|
+
crossing: int,
|
682
|
+
sampling_time: float,
|
683
|
+
maxima_index: int,
|
684
|
+
wmin: int = 2,
|
685
|
+
transient_time: Optional[int] = None,
|
686
|
+
) -> NDArray[np.float64]:
|
687
|
+
|
688
|
+
u = u.copy()
|
689
|
+
neq = len(u)
|
690
|
+
H = np.zeros(neq)
|
691
|
+
|
692
|
+
# Generate the Poincaré section or stroboscopic map
|
693
|
+
if map_type == "PS":
|
694
|
+
points = generate_poincare_section(
|
695
|
+
u,
|
696
|
+
parameters,
|
697
|
+
num_points,
|
698
|
+
equations_of_motion,
|
699
|
+
transient_time,
|
700
|
+
time_step,
|
701
|
+
atol,
|
702
|
+
rtol,
|
703
|
+
integrator,
|
704
|
+
section_index,
|
705
|
+
section_value,
|
706
|
+
crossing,
|
707
|
+
)
|
708
|
+
data = points[:, 1:] # Remove time
|
709
|
+
data = np.delete(data, section_index, axis=1)
|
710
|
+
elif map_type == "SM":
|
711
|
+
points = generate_stroboscopic_map(
|
712
|
+
u,
|
713
|
+
parameters,
|
714
|
+
num_points,
|
715
|
+
sampling_time,
|
716
|
+
equations_of_motion,
|
717
|
+
transient_time,
|
718
|
+
time_step,
|
719
|
+
atol,
|
720
|
+
rtol,
|
721
|
+
integrator,
|
722
|
+
)
|
723
|
+
|
724
|
+
data = points[:, 1:] # Remove time
|
725
|
+
else:
|
726
|
+
points = generate_maxima_map(
|
727
|
+
u,
|
728
|
+
parameters,
|
729
|
+
num_points,
|
730
|
+
maxima_index,
|
731
|
+
equations_of_motion,
|
732
|
+
transient_time,
|
733
|
+
time_step,
|
734
|
+
atol,
|
735
|
+
rtol,
|
736
|
+
integrator,
|
737
|
+
)
|
738
|
+
|
739
|
+
data = points[:, 1:] # Remove time
|
740
|
+
|
741
|
+
return hurst_exponent(data, wmin=wmin)
|
@@ -196,6 +196,31 @@ def rossler_system_4D_jacobian(
|
|
196
196
|
return J
|
197
197
|
|
198
198
|
|
199
|
+
@njit
|
200
|
+
def duffing(time, u, parameters):
|
201
|
+
delta, alpha, beta, gamma, omega = parameters
|
202
|
+
dudt = np.zeros_like(u)
|
203
|
+
dudt[0] = u[1]
|
204
|
+
dudt[1] = (
|
205
|
+
-delta * u[1] + alpha * u[0] - beta * u[0] ** 3 + gamma * np.cos(omega * time)
|
206
|
+
)
|
207
|
+
|
208
|
+
return dudt
|
209
|
+
|
210
|
+
|
211
|
+
@njit
|
212
|
+
def duffing_jacobian(time, u, parameters):
|
213
|
+
delta, alpha, beta, gamma, omega = parameters
|
214
|
+
neq = len(u)
|
215
|
+
J = np.zeros((neq, neq), dtype=np.float64)
|
216
|
+
|
217
|
+
J[0, 0] = 0
|
218
|
+
J[0, 1] = 1
|
219
|
+
J[1, 0] = alpha - 3 * beta * u[0] ** 2
|
220
|
+
J[1, 1] = -delta
|
221
|
+
return J
|
222
|
+
|
223
|
+
|
199
224
|
@njit
|
200
225
|
def variational_equations(
|
201
226
|
time: float,
|
@@ -23,7 +23,7 @@ from numba import njit, prange
|
|
23
23
|
from pynamicalsys.continuous_time.models import variational_equations
|
24
24
|
|
25
25
|
|
26
|
-
@njit
|
26
|
+
@njit
|
27
27
|
def rk4_step(
|
28
28
|
t: float,
|
29
29
|
u: NDArray[np.float64],
|
@@ -43,7 +43,7 @@ def rk4_step(
|
|
43
43
|
return u_next
|
44
44
|
|
45
45
|
|
46
|
-
@njit
|
46
|
+
@njit
|
47
47
|
def variational_rk4_step(
|
48
48
|
t: float,
|
49
49
|
u: NDArray[np.float64],
|
@@ -115,7 +115,7 @@ RK45_B4 = np.array(
|
|
115
115
|
)
|
116
116
|
|
117
117
|
|
118
|
-
@njit
|
118
|
+
@njit
|
119
119
|
def rk45_step(t, u, parameters, equations_of_motion, time_step, atol=1e-6, rtol=1e-3):
|
120
120
|
"""Single adaptive step of RK45 (Dormand-Prince).
|
121
121
|
|
@@ -163,7 +163,7 @@ def rk45_step(t, u, parameters, equations_of_motion, time_step, atol=1e-6, rtol=
|
|
163
163
|
return u5, t + time_step, time_step_new, accept
|
164
164
|
|
165
165
|
|
166
|
-
@njit
|
166
|
+
@njit
|
167
167
|
def variational_rk45_step(
|
168
168
|
t,
|
169
169
|
u,
|
@@ -227,7 +227,7 @@ def variational_rk45_step(
|
|
227
227
|
return u5, t + time_step, time_step_new, accept
|
228
228
|
|
229
229
|
|
230
|
-
@njit
|
230
|
+
@njit
|
231
231
|
def rk4_step_wrapped(
|
232
232
|
t: float,
|
233
233
|
u: NDArray[np.float64],
|
@@ -265,7 +265,7 @@ def rk4_step_wrapped(
|
|
265
265
|
return u_next, t_next, h_next, accept
|
266
266
|
|
267
267
|
|
268
|
-
@njit
|
268
|
+
@njit
|
269
269
|
def rk45_step_wrapped(
|
270
270
|
t: float,
|
271
271
|
u: NDArray[np.float64],
|
@@ -302,7 +302,7 @@ def rk45_step_wrapped(
|
|
302
302
|
)
|
303
303
|
|
304
304
|
|
305
|
-
@njit
|
305
|
+
@njit
|
306
306
|
def estimate_initial_step(
|
307
307
|
t0: float,
|
308
308
|
u0: np.ndarray,
|