pynamicalsys 1.0.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 +24 -0
- pynamicalsys/__version__.py +21 -0
- pynamicalsys/common/__init__.py +16 -0
- pynamicalsys/common/basin_analysis.py +170 -0
- pynamicalsys/common/recurrence_quantification_analysis.py +426 -0
- pynamicalsys/common/utils.py +344 -0
- pynamicalsys/continuous_time/__init__.py +16 -0
- pynamicalsys/core/__init__.py +16 -0
- pynamicalsys/core/basin_metrics.py +206 -0
- pynamicalsys/core/continuous_dynamical_systems.py +18 -0
- pynamicalsys/core/discrete_dynamical_systems.py +3391 -0
- pynamicalsys/core/plot_styler.py +155 -0
- pynamicalsys/core/time_series_metrics.py +139 -0
- pynamicalsys/discrete_time/__init__.py +16 -0
- pynamicalsys/discrete_time/dynamical_indicators.py +1226 -0
- pynamicalsys/discrete_time/models.py +435 -0
- pynamicalsys/discrete_time/trajectory_analysis.py +1459 -0
- pynamicalsys/discrete_time/transport.py +501 -0
- pynamicalsys/discrete_time/validators.py +313 -0
- pynamicalsys-1.0.0.dist-info/METADATA +791 -0
- pynamicalsys-1.0.0.dist-info/RECORD +23 -0
- pynamicalsys-1.0.0.dist-info/WHEEL +5 -0
- pynamicalsys-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,155 @@
|
|
1
|
+
# plot_styler.py
|
2
|
+
|
3
|
+
# Copyright (C) 2025 Matheus Rolim Sales
|
4
|
+
#
|
5
|
+
# This program is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU General Public License
|
16
|
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
17
|
+
|
18
|
+
import matplotlib as mpl
|
19
|
+
import matplotlib.pyplot as plt
|
20
|
+
from typing import Optional
|
21
|
+
|
22
|
+
|
23
|
+
class PlotStyler:
|
24
|
+
"""
|
25
|
+
A utility class to globally configure and apply consistent styling for Matplotlib plots.
|
26
|
+
|
27
|
+
This class sets default plot aesthetics such as font sizes, line widths, marker styles,
|
28
|
+
tick behavior, and more, using Matplotlib's rcParams. It ensures consistent visual style
|
29
|
+
across multiple figures and subplots with minimal repetitive code.
|
30
|
+
|
31
|
+
Parameters
|
32
|
+
----------
|
33
|
+
fontsize : int, default=20
|
34
|
+
Base font size for labels, titles, and text.
|
35
|
+
legend_fontsize : int, default=14
|
36
|
+
Font size for the legend.
|
37
|
+
axes_linewidth : float, default=1.3
|
38
|
+
Width of the axes borders.
|
39
|
+
font_family : str, default="STIXGeneral"
|
40
|
+
Font family used for text and math rendering.
|
41
|
+
line_width : float, default=2.0
|
42
|
+
Default line width for plot lines.
|
43
|
+
markersize : float, default=6.0
|
44
|
+
Default marker size for lines with markers.
|
45
|
+
markeredgewidth : float, default=1.0
|
46
|
+
Edge width for plot markers.
|
47
|
+
markeredgecolor : str, default="black"
|
48
|
+
Edge color for plot markers.
|
49
|
+
tick_direction_in : bool, default=True
|
50
|
+
Whether ticks point inward (`True`) or outward (`False`).
|
51
|
+
minor_ticks_visible : bool, default=True
|
52
|
+
Whether minor ticks are visible.
|
53
|
+
|
54
|
+
Methods
|
55
|
+
-------
|
56
|
+
apply_style()
|
57
|
+
Applies the defined styling parameters globally using Matplotlib's rcParams.
|
58
|
+
set_tick_padding(ax, pad_x=None, pad_y=None)
|
59
|
+
Sets custom padding between axis ticks and labels for a given Axes object.
|
60
|
+
"""
|
61
|
+
|
62
|
+
def __init__(
|
63
|
+
self,
|
64
|
+
fontsize: int = 20,
|
65
|
+
legend_fontsize: int = 14,
|
66
|
+
axes_linewidth: float = 1.3,
|
67
|
+
font_family: str = "STIXGeneral",
|
68
|
+
tick_direction_in: bool = True,
|
69
|
+
minor_ticks_visible: bool = False,
|
70
|
+
linewidth: float = 1.0,
|
71
|
+
markersize: float = 5.0,
|
72
|
+
markeredgewidth: float = 1.0,
|
73
|
+
markeredgecolor: str = "black",
|
74
|
+
ticks_on_all_sides: bool = True,
|
75
|
+
) -> None:
|
76
|
+
self.fontsize = fontsize
|
77
|
+
self.legend_fontsize = legend_fontsize
|
78
|
+
self.axes_linewidth = axes_linewidth
|
79
|
+
self.font_family = font_family
|
80
|
+
self.tick_labelsize = fontsize - 3
|
81
|
+
self.tick_direction_in = (tick_direction_in,)
|
82
|
+
self.minor_ticks_visible = minor_ticks_visible
|
83
|
+
self.linewidth = linewidth
|
84
|
+
self.markersize = markersize
|
85
|
+
self.markeredgewidth = markeredgewidth
|
86
|
+
self.markeredgecolor = markeredgecolor
|
87
|
+
self.ticks_on_all_sides = ticks_on_all_sides
|
88
|
+
|
89
|
+
def apply_style(self) -> None:
|
90
|
+
"""
|
91
|
+
Apply global matplotlib styling.
|
92
|
+
"""
|
93
|
+
self._reset_style() # Reset any previous styles
|
94
|
+
plt.clf()
|
95
|
+
plt.rc("font", size=self.fontsize)
|
96
|
+
plt.rc("xtick", labelsize=self.tick_labelsize)
|
97
|
+
plt.rc("ytick", labelsize=self.tick_labelsize)
|
98
|
+
plt.rc("legend", fontsize=self.legend_fontsize)
|
99
|
+
|
100
|
+
# Font and math rendering
|
101
|
+
plt.rc("font", family=self.font_family)
|
102
|
+
plt.rcParams["mathtext.fontset"] = "stix"
|
103
|
+
|
104
|
+
# Axes linewidth
|
105
|
+
mpl.rcParams["axes.linewidth"] = self.axes_linewidth
|
106
|
+
|
107
|
+
# Global tick settings
|
108
|
+
if self.tick_direction_in:
|
109
|
+
plt.rcParams["xtick.direction"] = "in"
|
110
|
+
plt.rcParams["ytick.direction"] = "in"
|
111
|
+
|
112
|
+
if self.minor_ticks_visible:
|
113
|
+
plt.rcParams["xtick.minor.visible"] = True
|
114
|
+
plt.rcParams["ytick.minor.visible"] = True
|
115
|
+
|
116
|
+
# Ticks on all sides (requires setting this per axis after creation)
|
117
|
+
if self.ticks_on_all_sides:
|
118
|
+
mpl.rcParams["xtick.top"] = True
|
119
|
+
mpl.rcParams["ytick.right"] = True
|
120
|
+
|
121
|
+
# Line and marker styling
|
122
|
+
plt.rcParams["lines.linewidth"] = self.linewidth
|
123
|
+
plt.rcParams["lines.markersize"] = self.markersize
|
124
|
+
plt.rcParams["lines.markeredgewidth"] = self.markeredgewidth
|
125
|
+
plt.rcParams["lines.markeredgecolor"] = self.markeredgecolor
|
126
|
+
|
127
|
+
def set_tick_padding(
|
128
|
+
self, ax: plt.Axes, pad_x: Optional[int] = None, pad_y: Optional[int] = None
|
129
|
+
) -> None:
|
130
|
+
"""
|
131
|
+
Set custom tick padding for a single Axes.
|
132
|
+
|
133
|
+
Parameters
|
134
|
+
----------
|
135
|
+
ax : matplotlib Axes
|
136
|
+
The axes object to modify.
|
137
|
+
pad_x : int, optional
|
138
|
+
Padding for x-axis major ticks.
|
139
|
+
pad_y : int, optional
|
140
|
+
Padding for y-axis major ticks.
|
141
|
+
"""
|
142
|
+
if pad_x is not None:
|
143
|
+
ax.tick_params(axis="x", which="major", pad=pad_x)
|
144
|
+
if pad_y is not None:
|
145
|
+
ax.tick_params(axis="y", which="major", pad=pad_y)
|
146
|
+
|
147
|
+
def _reset_style(self) -> None:
|
148
|
+
"""
|
149
|
+
Reset all matplotlib settings to default values.
|
150
|
+
|
151
|
+
This method undoes any customizations made by `apply_style`
|
152
|
+
or other changes to `matplotlib.rcParams`.
|
153
|
+
"""
|
154
|
+
mpl.rcdefaults()
|
155
|
+
plt.rcdefaults()
|
@@ -0,0 +1,139 @@
|
|
1
|
+
# time_series_metrics.py
|
2
|
+
|
3
|
+
# Copyright (C) 2025 Matheus Rolim Sales
|
4
|
+
#
|
5
|
+
# This program is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU General Public License
|
16
|
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
17
|
+
|
18
|
+
import numpy as np
|
19
|
+
from numpy.typing import NDArray
|
20
|
+
|
21
|
+
from pynamicalsys.common.recurrence_quantification_analysis import (
|
22
|
+
recurrence_matrix,
|
23
|
+
RTEConfig,
|
24
|
+
white_vertline_distr,
|
25
|
+
)
|
26
|
+
|
27
|
+
|
28
|
+
class TimeSeriesMetrics:
|
29
|
+
def __init__(self, time_series: NDArray[np.float64]) -> None:
|
30
|
+
"""
|
31
|
+
Initialize the TimeSeriesMetrics class.
|
32
|
+
|
33
|
+
This class provides methods to compute metrics related to time series analysis,
|
34
|
+
such as survival probability and in a future release, the autocorrelation function.
|
35
|
+
"""
|
36
|
+
self.time_series = time_series
|
37
|
+
|
38
|
+
# The time series can be either 1D (shape(N,)) or 2D (shape(N, dim))
|
39
|
+
if time_series.ndim not in {1, 2}:
|
40
|
+
raise ValueError("time_series must be 1D or 2D array")
|
41
|
+
|
42
|
+
def recurrence_matrix(
|
43
|
+
self, compute_white_vert_distr=False, **kwargs
|
44
|
+
) -> NDArray[np.float64]:
|
45
|
+
"""
|
46
|
+
Compute the recurrence matrix for the time series.
|
47
|
+
|
48
|
+
Parameters
|
49
|
+
----------
|
50
|
+
metric : {'supremum', 'euclidean', 'manhattan'}, default='supremum'
|
51
|
+
Distance metric used for phase space reconstruction.
|
52
|
+
std_metric : {'supremum', 'euclidean', 'manhattan'}, default='supremum'
|
53
|
+
Distance metric used for standard deviation calculation.
|
54
|
+
threshold : float, default=0.1
|
55
|
+
Recurrence threshold (relative to data range).
|
56
|
+
threshold_std : bool, default=True
|
57
|
+
Whether to scale threshold by data standard deviation.
|
58
|
+
|
59
|
+
Returns
|
60
|
+
-------
|
61
|
+
NDArray[np.float64]
|
62
|
+
The recurrence matrix of the time series.
|
63
|
+
"""
|
64
|
+
config = RTEConfig(**kwargs)
|
65
|
+
# Metric setup
|
66
|
+
metric_map = {"supremum": np.inf, "euclidean": 2, "manhattan": 1}
|
67
|
+
try:
|
68
|
+
ord = metric_map[config.std_metric.lower()]
|
69
|
+
except KeyError:
|
70
|
+
raise ValueError(
|
71
|
+
f"Invalid std_metric: {config.std_metric}. Must be {list(metric_map.keys())}"
|
72
|
+
)
|
73
|
+
|
74
|
+
# Threshold calculation
|
75
|
+
if config.threshold_std:
|
76
|
+
std = np.std(self.time_series, axis=0)
|
77
|
+
eps = config.threshold * np.linalg.norm(std, ord=ord)
|
78
|
+
if eps <= 0:
|
79
|
+
eps = 0.1
|
80
|
+
else:
|
81
|
+
eps = config.threshold
|
82
|
+
|
83
|
+
if compute_white_vert_distr:
|
84
|
+
recmat = recurrence_matrix(
|
85
|
+
self.time_series, float(eps), metric=config.metric
|
86
|
+
)
|
87
|
+
P = white_vertline_distr(recmat)[config.lmin :]
|
88
|
+
|
89
|
+
return recmat, P
|
90
|
+
else:
|
91
|
+
return recurrence_matrix(self.time_series, float(eps), metric=config.metric)
|
92
|
+
|
93
|
+
def recurrence_time_entropy(self, **kwargs):
|
94
|
+
"""
|
95
|
+
Compute the recurrence time entropy of the time series.
|
96
|
+
|
97
|
+
Parameters
|
98
|
+
----------
|
99
|
+
metric : {'supremum', 'euclidean', 'manhattan'}, default='supremum'
|
100
|
+
Distance metric used for phase space reconstruction.
|
101
|
+
std_metric : {'supremum', 'euclidean', 'manhattan'}, default='supremum'
|
102
|
+
Distance metric used for standard deviation calculation.
|
103
|
+
lmin : int, default=1
|
104
|
+
Minimum line length to consider in recurrence quantification.
|
105
|
+
threshold : float, default=0.1
|
106
|
+
Recurrence threshold (relative to data range).
|
107
|
+
threshold_std : bool, default=True
|
108
|
+
Whether to scale threshold by data standard deviation.
|
109
|
+
return_recmat : bool, default=False
|
110
|
+
Whether to return the recurrence matrix.
|
111
|
+
return_p : bool, default=False
|
112
|
+
Whether to return white vertical line length distribution.
|
113
|
+
|
114
|
+
Returns
|
115
|
+
-------
|
116
|
+
Tuple[float, float]
|
117
|
+
The recurrence time entropy and its standard deviation.
|
118
|
+
"""
|
119
|
+
|
120
|
+
config = RTEConfig(**kwargs)
|
121
|
+
|
122
|
+
# Compute the recurrence matrix
|
123
|
+
rec_matrix = self.recurrence_matrix(**kwargs)
|
124
|
+
|
125
|
+
# Calculate the white vertical line distribution
|
126
|
+
P = white_vertline_distr(rec_matrix)[config.lmin :]
|
127
|
+
P = P[P > 0] # Filter out zero probabilities
|
128
|
+
P /= P.sum() # Normalize the distribution
|
129
|
+
|
130
|
+
# Calculate the recurrence time entropy
|
131
|
+
rte = -np.sum(P * np.log(P))
|
132
|
+
|
133
|
+
result = [rte]
|
134
|
+
if config.return_recmat:
|
135
|
+
result.append(rec_matrix)
|
136
|
+
if config.return_p:
|
137
|
+
result.append(P)
|
138
|
+
|
139
|
+
return result[0] if len(result) == 1 else tuple(result)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# # __init__.py
|
2
|
+
|
3
|
+
# # Copyright (C) 2025 Matheus Rolim Sales
|
4
|
+
# #
|
5
|
+
# # This program is free software: you can redistribute it and/or modify
|
6
|
+
# # it under the terms of the GNU General Public License as published by
|
7
|
+
# # the Free Software Foundation, either version 3 of the License, or
|
8
|
+
# # (at your option) any later version.
|
9
|
+
# #
|
10
|
+
# # This program is distributed in the hope that it will be useful,
|
11
|
+
# # but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# # GNU General Public License for more details.
|
14
|
+
# #
|
15
|
+
# # You should have received a copy of the GNU General Public License
|
16
|
+
# # along with this program. If not, see <https://www.gnu.org/licenses/>.
|