pynamicalsys 1.2.2__tar.gz → 1.3.0__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.
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/CHANGELOG.md +27 -4
- pynamicalsys-1.3.0/CONTRIBUTING.md +47 -0
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/PKG-INFO +3 -3
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/README.md +2 -2
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/src/pynamicalsys/__version__.py +16 -3
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/src/pynamicalsys/common/utils.py +20 -21
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/src/pynamicalsys/continuous_time/chaotic_indicators.py +174 -78
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/src/pynamicalsys/continuous_time/numerical_integrators.py +1 -1
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/src/pynamicalsys/continuous_time/trajectory_analysis.py +58 -22
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/src/pynamicalsys/core/continuous_dynamical_systems.py +184 -11
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/src/pynamicalsys/core/discrete_dynamical_systems.py +264 -16
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/src/pynamicalsys/discrete_time/dynamical_indicators.py +407 -163
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/src/pynamicalsys/discrete_time/transport.py +74 -112
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/src/pynamicalsys/discrete_time/validators.py +1 -1
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/src/pynamicalsys.egg-info/PKG-INFO +3 -3
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/src/pynamicalsys.egg-info/SOURCES.txt +5 -1
- pynamicalsys-1.3.0/tests/continuous_time/test_SALI_and_LDI.ipynb +259 -0
- pynamicalsys-1.3.0/tests/continuous_time/test_lyapunov.ipynb +438 -0
- pynamicalsys-1.3.0/tests/continuous_time/test_trajectory.ipynb +443 -0
- pynamicalsys-1.3.0/tests/discrete-time/test_FTLE.ipynb +164 -0
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/tests/discrete-time/test_RTE.ipynb +2 -2
- pynamicalsys-1.3.0/tests/discrete-time/test_SALI.ipynb +153 -0
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/tests/discrete-time/test_bif_diagram.ipynb +3 -3
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/tests/discrete-time/test_chaotic_saddle.ipynb +3 -3
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/tests/discrete-time/test_dig.ipynb +2 -2
- pynamicalsys-1.3.0/tests/discrete-time/test_ensemble_statistics.ipynb +562 -0
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/tests/discrete-time/test_escape.ipynb +2 -2
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/tests/discrete-time/test_fractal_dimension.ipynb +3 -3
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/tests/discrete-time/test_hurst_exponent.ipynb +2 -2
- pynamicalsys-1.3.0/tests/discrete-time/test_lyapunov.ipynb +472 -0
- pynamicalsys-1.3.0/tests/discrete-time/test_lyapunov_exponents.ipynb +319 -0
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/tests/discrete-time/test_manifold.ipynb +2 -2
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/tests/discrete-time/test_manifolds.ipynb +3 -3
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/tests/discrete-time/test_period_counter.ipynb +2 -2
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/tests/discrete-time/test_phase_space.ipynb +2 -2
- pynamicalsys-1.3.0/tests/discrete-time/test_step.py +31 -0
- pynamicalsys-1.2.2/CONTRIBUTING.md +0 -47
- pynamicalsys-1.2.2/tests/continuous_time/test_SALI_and_LDI.ipynb +0 -237
- pynamicalsys-1.2.2/tests/continuous_time/test_lyapunov.ipynb +0 -332
- pynamicalsys-1.2.2/tests/continuous_time/test_trajectory.ipynb +0 -443
- pynamicalsys-1.2.2/tests/discrete-time/test_FTLE.ipynb +0 -101
- pynamicalsys-1.2.2/tests/discrete-time/test_lyapunov_exponents.ipynb +0 -473
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/.gitignore +0 -0
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/CITATION.cff +0 -0
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/LICENSE +0 -0
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/MANIFEST.in +0 -0
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/pyproject.toml +0 -0
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/setup.cfg +0 -0
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/src/pynamicalsys/__init__.py +0 -0
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/src/pynamicalsys/common/__init__.py +0 -0
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/src/pynamicalsys/common/basin_analysis.py +0 -0
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/src/pynamicalsys/common/recurrence_quantification_analysis.py +0 -0
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/src/pynamicalsys/continuous_time/__init__.py +0 -0
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/src/pynamicalsys/continuous_time/models.py +0 -0
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/src/pynamicalsys/continuous_time/validators.py +0 -0
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/src/pynamicalsys/core/__init__.py +0 -0
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/src/pynamicalsys/core/basin_metrics.py +0 -0
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/src/pynamicalsys/core/plot_styler.py +0 -0
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/src/pynamicalsys/core/time_series_metrics.py +0 -0
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/src/pynamicalsys/discrete_time/__init__.py +0 -0
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/src/pynamicalsys/discrete_time/models.py +0 -0
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/src/pynamicalsys/discrete_time/trajectory_analysis.py +0 -0
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/src/pynamicalsys.egg-info/dependency_links.txt +0 -0
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/src/pynamicalsys.egg-info/requires.txt +0 -0
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/src/pynamicalsys.egg-info/top_level.txt +0 -0
- {pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/tests/discrete-time/test_SALI.py +0 -0
@@ -5,6 +5,29 @@ All notable changes to this project will be documented in this file.
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
7
|
|
8
|
+
## [v1.3.0] 2025-08-23
|
9
|
+
|
10
|
+
### Added
|
11
|
+
|
12
|
+
- `DiscreteDynamicalSystem` class:
|
13
|
+
- `step` method: returns the next state of the system.
|
14
|
+
- `GALI` method: computes the generalized alignment index (GALI).
|
15
|
+
|
16
|
+
- `ContinuousDynamicalSystem` class:
|
17
|
+
- `GALI` method that computes the generalized alignment index (GALI).
|
18
|
+
|
19
|
+
### Modified
|
20
|
+
|
21
|
+
- `DiscreteDynamicalSystem` class:
|
22
|
+
- Improved performance when checking sampling points by avoiding repeated searches in sample_times.
|
23
|
+
- Refactored the `lyapunov` method to allow computing only a subset of the Lyapunov spectrum.
|
24
|
+
|
25
|
+
- `ContinuousDynamicalSystem` class:
|
26
|
+
- Unified integration step logic (previously duplicated across methods like trajectory and lyapunov_exponents) into a single step function.
|
27
|
+
- Refactored the `lyapunov` method to allow computing only a subset of the Lyapunov spectrum.
|
28
|
+
|
29
|
+
[v1.3.0]: https://github.com/mrolims/pynamicalsys/compare/v1.2.2...v1.3.0
|
30
|
+
|
8
31
|
## [v1.2.2] - 2025-06-29
|
9
32
|
|
10
33
|
### Added
|
@@ -16,11 +39,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
16
39
|
- Lyapunov exponents calculation.
|
17
40
|
- The smallest aligment index (SALI) and linear dependence index (LDI) for chaos detection.
|
18
41
|
|
19
|
-
|
42
|
+
[v1.2.2]: https://github.com/mrolims/pynamicalsys/compare/v1.0.0...v1.2.2
|
43
|
+
|
44
|
+
## v1.0.0 - 2025-06-16
|
20
45
|
|
21
46
|
### Added
|
22
47
|
|
23
|
-
- `
|
48
|
+
- `DiscreteDynamicalSystem` class for simulating and analyzing discrete nonlinear dynamical systems:
|
24
49
|
- Trajectory computation.
|
25
50
|
- Chaotic indicators.
|
26
51
|
- Fixed points, periodic orbits, and manifolds.
|
@@ -36,8 +61,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
36
61
|
|
37
62
|
- `PlotStyler` utility class to globally configure and apply consistent styling for Matplotlib plots.
|
38
63
|
|
39
|
-
---
|
40
|
-
|
41
64
|
<!-- Dummy heading to avoid ending on a transition -->
|
42
65
|
|
43
66
|
##
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# Contributing to pynamicalsys
|
2
|
+
|
3
|
+
We appreciate your interest in contributing to pynamicalsys! Whether you're fixing a bug, suggesting an improvement, or adding a new feature, your help is welcome.
|
4
|
+
|
5
|
+
## How to Contribute
|
6
|
+
|
7
|
+
1. **Fork the Repository**
|
8
|
+
Create a personal copy of this repository.
|
9
|
+
|
10
|
+
2. **Create a Feature Branch**
|
11
|
+
Use a descriptive branch name:
|
12
|
+
|
13
|
+
```bash
|
14
|
+
$ git checkout -b fix-typo-in-readme
|
15
|
+
```
|
16
|
+
|
17
|
+
3. **Make Changes**
|
18
|
+
|
19
|
+
Follow the coding style and ensure existing functionality it not broken. Add tests if relevant.
|
20
|
+
|
21
|
+
4. **Submit a Pull Request**
|
22
|
+
|
23
|
+
Push your branch and open a pull request on GitHub. Include a clear description of your changes.
|
24
|
+
|
25
|
+
5. **Wait for Review**
|
26
|
+
|
27
|
+
We'll review your PR and may request changes or offer suggestions.
|
28
|
+
|
29
|
+
## Coding Style
|
30
|
+
|
31
|
+
- Follow **PEP8** standards for Pycode code.
|
32
|
+
- Use **docstrings** (PEP257) for public classes, functions, and modules.
|
33
|
+
- Include **type hints** where appropriate.
|
34
|
+
- Keep commits clean and descriptive.
|
35
|
+
|
36
|
+
## Bug Reports & Feature Requests
|
37
|
+
|
38
|
+
- Submit an [issue](https://github.com/mrolims/pynamicalsys/issues) to report bugs or suggest new features.
|
39
|
+
- Include steps to reproduce, error messages, or use cases when possible.
|
40
|
+
|
41
|
+
## Code of Conduct
|
42
|
+
|
43
|
+
We strive to maintain a respectful and inclusive community. Please follow our [code of conduct](https://pynamicalsys.readthedocs.io/en/latest/) when interacting.
|
44
|
+
|
45
|
+
## Thank You
|
46
|
+
|
47
|
+
Your contributions help improve the project and support the community — thank you!
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: pynamicalsys
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.3.0
|
4
4
|
Summary: A Python toolkit for the analysis of dynamical systems
|
5
5
|
Author-email: Matheus Rolim Sales <rolim.sales.m@gmail.com>
|
6
6
|
License: GNU GENERAL PUBLIC LICENSE
|
@@ -733,7 +733,7 @@ $ pip install pynamicalsys
|
|
733
733
|
To upgrade your current version of **pynamicalsys** to the latest stable release, run in your command line:
|
734
734
|
|
735
735
|
```bash
|
736
|
-
$ pip install
|
736
|
+
$ pip install pynamicalsys --upgrade
|
737
737
|
```
|
738
738
|
|
739
739
|
### Install from source
|
@@ -781,7 +781,7 @@ Currently, our research paper is under review, but in the mean time, if you use
|
|
781
781
|
|
782
782
|
## Contributing
|
783
783
|
|
784
|
-
We welcome contributions from the community! To get started, please see our Contributing Guidelines.
|
784
|
+
We welcome contributions from the community! To get started, please see our [Contributing Guidelines](https://pynamicalsys.readthedocs.io/en/latest/contributing.html).
|
785
785
|
|
786
786
|
## License
|
787
787
|
|
@@ -40,7 +40,7 @@ $ pip install pynamicalsys
|
|
40
40
|
To upgrade your current version of **pynamicalsys** to the latest stable release, run in your command line:
|
41
41
|
|
42
42
|
```bash
|
43
|
-
$ pip install
|
43
|
+
$ pip install pynamicalsys --upgrade
|
44
44
|
```
|
45
45
|
|
46
46
|
### Install from source
|
@@ -88,7 +88,7 @@ Currently, our research paper is under review, but in the mean time, if you use
|
|
88
88
|
|
89
89
|
## Contributing
|
90
90
|
|
91
|
-
We welcome contributions from the community! To get started, please see our Contributing Guidelines.
|
91
|
+
We welcome contributions from the community! To get started, please see our [Contributing Guidelines](https://pynamicalsys.readthedocs.io/en/latest/contributing.html).
|
92
92
|
|
93
93
|
## License
|
94
94
|
|
@@ -1,7 +1,14 @@
|
|
1
1
|
# file generated by setuptools-scm
|
2
2
|
# don't change, don't track in version control
|
3
3
|
|
4
|
-
__all__ = [
|
4
|
+
__all__ = [
|
5
|
+
"__version__",
|
6
|
+
"__version_tuple__",
|
7
|
+
"version",
|
8
|
+
"version_tuple",
|
9
|
+
"__commit_id__",
|
10
|
+
"commit_id",
|
11
|
+
]
|
5
12
|
|
6
13
|
TYPE_CHECKING = False
|
7
14
|
if TYPE_CHECKING:
|
@@ -9,13 +16,19 @@ if TYPE_CHECKING:
|
|
9
16
|
from typing import Union
|
10
17
|
|
11
18
|
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
19
|
+
COMMIT_ID = Union[str, None]
|
12
20
|
else:
|
13
21
|
VERSION_TUPLE = object
|
22
|
+
COMMIT_ID = object
|
14
23
|
|
15
24
|
version: str
|
16
25
|
__version__: str
|
17
26
|
__version_tuple__: VERSION_TUPLE
|
18
27
|
version_tuple: VERSION_TUPLE
|
28
|
+
commit_id: COMMIT_ID
|
29
|
+
__commit_id__: COMMIT_ID
|
19
30
|
|
20
|
-
__version__ = version = '1.
|
21
|
-
__version_tuple__ = version_tuple = (1,
|
31
|
+
__version__ = version = '1.3.0'
|
32
|
+
__version_tuple__ = version_tuple = (1, 3, 0)
|
33
|
+
|
34
|
+
__commit_id__ = commit_id = 'g138b5d22a'
|
@@ -257,7 +257,7 @@ def finite_difference_jacobian(
|
|
257
257
|
|
258
258
|
|
259
259
|
@njit
|
260
|
-
def
|
260
|
+
def wedge_norm_2(vectors: NDArray[np.float64]) -> float:
|
261
261
|
"""
|
262
262
|
Computes the norm of the wedge product of n m-dimensional vectors using the Gram determinant.
|
263
263
|
|
@@ -295,6 +295,25 @@ def wedge_product_norm(vectors: NDArray[np.float64]) -> float:
|
|
295
295
|
return norm
|
296
296
|
|
297
297
|
|
298
|
+
def wedge_norm(V: NDArray[np.float64]) -> float:
|
299
|
+
"""
|
300
|
+
Computes the norm of the wedge product of k d-dimensional vectors using the Gram determinant.
|
301
|
+
|
302
|
+
Parameters:
|
303
|
+
vectors : NDArray[np.float64]
|
304
|
+
A (d, k) array where d is the dimension and k is the number of vectors.
|
305
|
+
|
306
|
+
Returns:
|
307
|
+
norm : float
|
308
|
+
The norm (magnitude) of the wedge product.
|
309
|
+
"""
|
310
|
+
G = V.T @ V # Gram matrix, shape (k, k)
|
311
|
+
|
312
|
+
det = np.linalg.det(G)
|
313
|
+
|
314
|
+
return 0 if det < 0 else np.sqrt(det)
|
315
|
+
|
316
|
+
|
298
317
|
@njit
|
299
318
|
def _coeff_mat(x: NDArray[np.float64], deg: int) -> NDArray[np.float64]:
|
300
319
|
mat_ = np.zeros(shape=(x.shape[0], deg + 1))
|
@@ -322,23 +341,3 @@ def fit_poly(
|
|
322
341
|
p = _fit_x(a, y)
|
323
342
|
# Reverse order so p[0] is coefficient of highest order
|
324
343
|
return p[::-1]
|
325
|
-
|
326
|
-
|
327
|
-
if __name__ == "__main__":
|
328
|
-
|
329
|
-
v = np.random.rand(2, 2)
|
330
|
-
w = v.copy()
|
331
|
-
|
332
|
-
q, r = qr(v)
|
333
|
-
print("Q:\n", q)
|
334
|
-
print("R:\n", r)
|
335
|
-
print("QR Product:\n", np.dot(q, r))
|
336
|
-
print("Original Matrix:\n", v)
|
337
|
-
|
338
|
-
print()
|
339
|
-
|
340
|
-
q, r = householder_qr(v)
|
341
|
-
print("Q:\n", q)
|
342
|
-
print("R:\n", r)
|
343
|
-
print("QR Product:\n", np.dot(q, r))
|
344
|
-
print("Original Matrix:\n", v)
|
{pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/src/pynamicalsys/continuous_time/chaotic_indicators.py
RENAMED
@@ -18,14 +18,14 @@
|
|
18
18
|
from typing import Optional, Callable, Union, Tuple, Dict, List, Any, Sequence
|
19
19
|
from numpy.typing import NDArray
|
20
20
|
import numpy as np
|
21
|
-
from numba import njit
|
21
|
+
from numba import njit
|
22
22
|
|
23
|
-
from pynamicalsys.common.utils import qr
|
24
|
-
from pynamicalsys.continuous_time.trajectory_analysis import evolve_system
|
23
|
+
from pynamicalsys.common.utils import qr, wedge_norm
|
24
|
+
from pynamicalsys.continuous_time.trajectory_analysis import step, evolve_system
|
25
25
|
from pynamicalsys.continuous_time.numerical_integrators import rk4_step_wrapped
|
26
26
|
|
27
27
|
|
28
|
-
@njit(cache=True)
|
28
|
+
# @njit(cache=True)
|
29
29
|
def lyapunov_exponents(
|
30
30
|
u: NDArray[np.float64],
|
31
31
|
parameters: NDArray[np.float64],
|
@@ -36,6 +36,7 @@ def lyapunov_exponents(
|
|
36
36
|
jacobian: Callable[
|
37
37
|
[np.float64, NDArray[np.float64], NDArray[np.float64]], NDArray[np.float64]
|
38
38
|
],
|
39
|
+
num_exponents: int,
|
39
40
|
transient_time: Optional[float] = None,
|
40
41
|
time_step: float = 0.01,
|
41
42
|
atol: float = 1e-6,
|
@@ -50,7 +51,7 @@ def lyapunov_exponents(
|
|
50
51
|
) -> NDArray[np.float64]:
|
51
52
|
|
52
53
|
neq = len(u) # Number of equations of the system
|
53
|
-
nt = neq + neq
|
54
|
+
nt = neq + neq * num_exponents # system + variational equations
|
54
55
|
|
55
56
|
u = u.copy()
|
56
57
|
|
@@ -79,57 +80,55 @@ def lyapunov_exponents(
|
|
79
80
|
# Randomly define the deviation vectors and orthonormalize them
|
80
81
|
np.random.seed(seed)
|
81
82
|
uv[neq:] = -1 + 2 * np.random.rand(nt - neq)
|
82
|
-
v = uv[neq:].reshape(neq,
|
83
|
+
v = uv[neq:].reshape(neq, num_exponents)
|
83
84
|
v, _ = QR(v)
|
84
|
-
uv[neq:] = v.reshape(neq
|
85
|
+
uv[neq:] = v.reshape(neq * num_exponents)
|
85
86
|
|
86
|
-
exponents = np.zeros(
|
87
|
+
exponents = np.zeros(num_exponents, dtype=np.float64)
|
87
88
|
history = []
|
88
89
|
|
89
90
|
while time < total_time:
|
90
91
|
if time + time_step > total_time:
|
91
92
|
time_step = total_time - time
|
92
93
|
|
93
|
-
|
94
|
+
uv, time, time_step = step(
|
94
95
|
time,
|
95
96
|
uv,
|
96
97
|
parameters,
|
97
98
|
equations_of_motion,
|
98
99
|
jacobian=jacobian,
|
99
100
|
time_step=time_step,
|
101
|
+
atol=atol,
|
102
|
+
rtol=rtol,
|
103
|
+
integrator=integrator,
|
104
|
+
number_of_deviation_vectors=num_exponents,
|
100
105
|
)
|
101
106
|
|
102
|
-
|
103
|
-
|
104
|
-
uv = uv_new.copy()
|
105
|
-
# Reshape the deviation vectors into a neq x neq matrix
|
106
|
-
v = uv[neq:].reshape(neq, neq).copy()
|
107
|
-
|
108
|
-
# Perform the QR decomposition
|
109
|
-
v, R = QR(v)
|
110
|
-
|
111
|
-
# Accumulate the log
|
112
|
-
exponents += np.log(np.abs(np.diag(R))) / np.log(log_base)
|
107
|
+
# Reshape the deviation vectors into a neq x neq matrix
|
108
|
+
v = uv[neq:].reshape(neq, num_exponents).copy()
|
113
109
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
exponents[i]
|
119
|
-
/ (time - (transient_time if transient_time is not None else 0))
|
120
|
-
)
|
121
|
-
history.append(result)
|
110
|
+
# Perform the QR decomposition
|
111
|
+
v, R = QR(v)
|
112
|
+
# Accumulate the log
|
113
|
+
exponents += np.log(np.abs(np.diag(R))) / np.log(log_base)
|
122
114
|
|
123
|
-
|
124
|
-
|
115
|
+
if return_history:
|
116
|
+
result = [time]
|
117
|
+
for i in range(num_exponents):
|
118
|
+
result.append(
|
119
|
+
exponents[i]
|
120
|
+
/ (time - (transient_time if transient_time is not None else 0))
|
121
|
+
)
|
122
|
+
history.append(result)
|
125
123
|
|
126
|
-
|
124
|
+
# Reshape v back to uv
|
125
|
+
uv[neq:] = v.reshape(neq * num_exponents)
|
127
126
|
|
128
127
|
if return_history:
|
129
128
|
return history
|
130
129
|
else:
|
131
130
|
result = []
|
132
|
-
for i in range(
|
131
|
+
for i in range(num_exponents):
|
133
132
|
result.append(
|
134
133
|
exponents[i]
|
135
134
|
/ (time - (transient_time if transient_time is not None else 0))
|
@@ -197,44 +196,41 @@ def SALI(
|
|
197
196
|
if time + time_step > total_time:
|
198
197
|
time_step = total_time - time
|
199
198
|
|
200
|
-
|
199
|
+
uv, time, time_step = step(
|
201
200
|
time,
|
202
201
|
uv,
|
203
202
|
parameters,
|
204
203
|
equations_of_motion,
|
205
204
|
jacobian=jacobian,
|
206
205
|
time_step=time_step,
|
206
|
+
atol=atol,
|
207
|
+
rtol=rtol,
|
208
|
+
integrator=integrator,
|
207
209
|
number_of_deviation_vectors=ndv,
|
208
210
|
)
|
209
211
|
|
210
|
-
|
211
|
-
|
212
|
-
uv = uv_new.copy()
|
212
|
+
# Reshape the deviation vectors into a neq x ndv matrix
|
213
|
+
v = uv[neq:].reshape(neq, ndv)
|
213
214
|
|
214
|
-
|
215
|
-
|
215
|
+
# Normalize the deviation vectors
|
216
|
+
v[:, 0] /= np.linalg.norm(v[:, 0])
|
217
|
+
v[:, 1] /= np.linalg.norm(v[:, 1])
|
216
218
|
|
217
|
-
|
218
|
-
|
219
|
-
|
219
|
+
# Calculate the aligment indexes and SALI
|
220
|
+
PAI = np.linalg.norm(v[:, 0] + v[:, 1])
|
221
|
+
AAI = np.linalg.norm(v[:, 0] - v[:, 1])
|
222
|
+
sali = min(PAI, AAI)
|
220
223
|
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
sali = min(PAI, AAI)
|
224
|
+
if return_history:
|
225
|
+
result = [time, sali]
|
226
|
+
history.append(result)
|
225
227
|
|
226
|
-
|
227
|
-
|
228
|
-
|
228
|
+
# Early termination
|
229
|
+
if sali <= threshold:
|
230
|
+
break
|
229
231
|
|
230
|
-
|
231
|
-
|
232
|
-
break
|
233
|
-
|
234
|
-
# Reshape v back to uv
|
235
|
-
uv[neq:] = v.reshape(neq * ndv)
|
236
|
-
|
237
|
-
time_step = time_step_new
|
232
|
+
# Reshape v back to uv
|
233
|
+
uv[neq:] = v.reshape(neq * ndv)
|
238
234
|
|
239
235
|
if return_history:
|
240
236
|
return history
|
@@ -303,45 +299,145 @@ def LDI(
|
|
303
299
|
if time + time_step > total_time:
|
304
300
|
time_step = total_time - time
|
305
301
|
|
306
|
-
|
302
|
+
uv, time, time_step = step(
|
307
303
|
time,
|
308
304
|
uv,
|
309
305
|
parameters,
|
310
306
|
equations_of_motion,
|
311
307
|
jacobian=jacobian,
|
312
308
|
time_step=time_step,
|
309
|
+
atol=atol,
|
310
|
+
rtol=rtol,
|
311
|
+
integrator=integrator,
|
313
312
|
number_of_deviation_vectors=ndv,
|
314
313
|
)
|
315
314
|
|
316
|
-
|
317
|
-
|
318
|
-
uv = uv_new.copy()
|
315
|
+
# Reshape the deviation vectors into a neq x ndv matrix
|
316
|
+
v = uv[neq:].reshape(neq, ndv)
|
319
317
|
|
320
|
-
|
321
|
-
|
318
|
+
# Normalize the deviation vectors
|
319
|
+
for i in range(ndv):
|
320
|
+
v[:, i] /= np.linalg.norm(v[:, i])
|
322
321
|
|
323
|
-
|
324
|
-
|
325
|
-
|
322
|
+
# Calculate the singular values
|
323
|
+
S = np.linalg.svd(v, full_matrices=False, compute_uv=False)
|
324
|
+
ldi = np.exp(np.sum(np.log(S))) # LDI is the product of all singular values
|
325
|
+
# Instead of computing prod(S) directly, which could lead to underflows
|
326
|
+
# or overflows, we compute the sum_{i=1}^k log(S_i) and then take the
|
327
|
+
# exponential of this sum.
|
326
328
|
|
327
|
-
|
328
|
-
|
329
|
-
|
329
|
+
if return_history:
|
330
|
+
result = [time, ldi]
|
331
|
+
history.append(result)
|
330
332
|
|
331
|
-
|
332
|
-
|
333
|
-
|
333
|
+
# Early termination
|
334
|
+
if ldi <= threshold:
|
335
|
+
break
|
334
336
|
|
335
|
-
|
336
|
-
|
337
|
-
break
|
337
|
+
# Reshape v back to uv
|
338
|
+
uv[neq:] = v.reshape(neq * ndv)
|
338
339
|
|
339
|
-
|
340
|
-
|
340
|
+
if return_history:
|
341
|
+
return history
|
342
|
+
else:
|
343
|
+
return [[time, ldi]]
|
341
344
|
|
342
|
-
|
345
|
+
|
346
|
+
def GALI(
|
347
|
+
u: NDArray[np.float64],
|
348
|
+
parameters: NDArray[np.float64],
|
349
|
+
total_time: float,
|
350
|
+
equations_of_motion: Callable[
|
351
|
+
[np.float64, NDArray[np.float64], NDArray[np.float64]], NDArray[np.float64]
|
352
|
+
],
|
353
|
+
jacobian: Callable[
|
354
|
+
[np.float64, NDArray[np.float64], NDArray[np.float64]], NDArray[np.float64]
|
355
|
+
],
|
356
|
+
number_deviation_vectors: int,
|
357
|
+
transient_time: Optional[float] = None,
|
358
|
+
time_step: float = 0.01,
|
359
|
+
atol: float = 1e-6,
|
360
|
+
rtol: float = 1e-3,
|
361
|
+
integrator=rk4_step_wrapped,
|
362
|
+
return_history: bool = False,
|
363
|
+
seed: int = 13,
|
364
|
+
threshold: float = 1e-16,
|
365
|
+
) -> NDArray[np.float64]:
|
366
|
+
|
367
|
+
neq = len(u) # Number of equations of the system
|
368
|
+
ndv = number_deviation_vectors # Number of deviation vectors
|
369
|
+
nt = neq + neq * ndv # Total number of equations including variational equations
|
370
|
+
|
371
|
+
u = u.copy()
|
372
|
+
|
373
|
+
# Handle transient time
|
374
|
+
if transient_time is not None:
|
375
|
+
u = evolve_system(
|
376
|
+
u,
|
377
|
+
parameters,
|
378
|
+
transient_time,
|
379
|
+
equations_of_motion,
|
380
|
+
time_step=time_step,
|
381
|
+
atol=atol,
|
382
|
+
rtol=rtol,
|
383
|
+
integrator=integrator,
|
384
|
+
)
|
385
|
+
time = transient_time
|
386
|
+
else:
|
387
|
+
time = 0
|
388
|
+
|
389
|
+
# State + deviation vectors
|
390
|
+
uv = np.zeros(nt)
|
391
|
+
uv[:neq] = u.copy()
|
392
|
+
|
393
|
+
# Randomly define the deviation vectors and orthonormalize them
|
394
|
+
np.random.seed(seed)
|
395
|
+
uv[neq:] = -1 + 2 * np.random.rand(nt - neq)
|
396
|
+
v = uv[neq:].reshape(neq, ndv)
|
397
|
+
v, _ = qr(v)
|
398
|
+
uv[neq:] = v.reshape(neq * ndv)
|
399
|
+
|
400
|
+
history = []
|
401
|
+
|
402
|
+
while time < total_time:
|
403
|
+
if time + time_step > total_time:
|
404
|
+
time_step = total_time - time
|
405
|
+
|
406
|
+
uv, time, time_step = step(
|
407
|
+
time,
|
408
|
+
uv,
|
409
|
+
parameters,
|
410
|
+
equations_of_motion,
|
411
|
+
jacobian=jacobian,
|
412
|
+
time_step=time_step,
|
413
|
+
atol=atol,
|
414
|
+
rtol=rtol,
|
415
|
+
integrator=integrator,
|
416
|
+
number_of_deviation_vectors=ndv,
|
417
|
+
)
|
418
|
+
|
419
|
+
# Reshape the deviation vectors into a neq x ndv matrix
|
420
|
+
v = uv[neq:].reshape(neq, ndv)
|
421
|
+
|
422
|
+
# Normalize the deviation vectors
|
423
|
+
for i in range(ndv):
|
424
|
+
v[:, i] /= np.linalg.norm(v[:, i])
|
425
|
+
|
426
|
+
# Calculate GALI
|
427
|
+
gali = wedge_norm(v)
|
428
|
+
|
429
|
+
if return_history:
|
430
|
+
result = [time, gali]
|
431
|
+
history.append(result)
|
432
|
+
|
433
|
+
# Early termination
|
434
|
+
if gali <= threshold:
|
435
|
+
break
|
436
|
+
|
437
|
+
# Reshape v back to uv
|
438
|
+
uv[neq:] = v.reshape(neq * ndv)
|
343
439
|
|
344
440
|
if return_history:
|
345
441
|
return history
|
346
442
|
else:
|
347
|
-
return [[time,
|
443
|
+
return [[time, gali]]
|
{pynamicalsys-1.2.2 → pynamicalsys-1.3.0}/src/pynamicalsys/continuous_time/numerical_integrators.py
RENAMED
@@ -237,7 +237,7 @@ def rk4_step_wrapped(
|
|
237
237
|
],
|
238
238
|
jacobian=None,
|
239
239
|
time_step: float = 0.01,
|
240
|
-
number_of_deviation_vectors=None,
|
240
|
+
number_of_deviation_vectors: Optional[int] = None,
|
241
241
|
atol: float = 1e-6, # unused, just to match signature
|
242
242
|
rtol: float = 1e-3, # unused, just to match signature
|
243
243
|
) -> tuple[NDArray[np.float64], float, float, bool]:
|