文章目錄
原始論文
M.T. Rosenstein, J.J. Collins, and C.J. De Luca. A practical method for calculating largest Lyapunov exponents from small data sets. Physica D, 65:117-134, 1993.
下載地址:https://www.physionet.org/content/lyapunov/1.0.0/
python 相關代碼
混沌系統的常見指標
區分確定性混沌系統與噪聲已成爲許多不同領域的重要問題。
對於實驗產生的時間序列,可以計算這些混沌系統的指標:
- 相關維數(),
- Kolmogorov 熵
- Lyapunov 特徵指數。
相關維度是對系統複雜程度的估計,熵和特徵指數是對混沌程度的估計。
最大李亞普諾夫指數的含義
LLE 描述了相空間中相近的兩點(初始間距爲)隨時間推移指數分離的速率:
其中表示分離距離,表示初始間距, 爲最大李氏指數。
算法流程圖
python 代碼模塊
最近鄰
import numpy as np
from scipy import stats
from scipy.spatial import cKDTree as KDTree
from scipy.spatial import distance
def neighbors(y, metric='chebyshev', window=0, maxnum=None):
"""Find nearest neighbors of all points in the given array.
Finds the nearest neighbors of all points in the given array using
SciPy's KDTree search.
Parameters
----------
y : ndarray
N-dimensional array containing time-delayed vectors.
metric : string, optional (default = 'chebyshev')
Metric to use for distance computation. Must be one of
"cityblock" (aka the Manhattan metric), "chebyshev" (aka the
maximum norm metric), or "euclidean".
window : int, optional (default = 0)
Minimum temporal separation (Theiler window) that should exist
between near neighbors. This is crucial while computing
Lyapunov exponents and the correlation dimension.
maxnum : int, optional (default = None (optimum))
Maximum number of near neighbors that should be found for each
point. In rare cases, when there are no neighbors that are at a
nonzero distance, this will have to be increased (i.e., beyond
2 * window + 3).
Returns
-------
index : array
Array containing indices of near neighbors.
dist : array
Array containing near neighbor distances.
"""
if metric == 'cityblock':
p = 1
elif metric == 'euclidean':
p = 2
elif metric == 'chebyshev':
p = np.inf
else:
raise ValueError('Unknown metric. Should be one of "cityblock", '
'"euclidean", or "chebyshev".')
tree = KDTree(y)
n = len(y)
if not maxnum:
maxnum = (window + 1) + 1 + (window + 1)
else:
maxnum = max(1, maxnum)
if maxnum >= n:
raise ValueError('maxnum is bigger than array length.')
dists = np.empty(n)
indices = np.empty(n, dtype=int)
for i, x in enumerate(y):
for k in range(2, maxnum + 2):
dist, index = tree.query(x, k=k, p=p)
valid = (np.abs(index - i) > window) & (dist > 0)
if np.count_nonzero(valid):
dists[i] = dist[valid][0]
indices[i] = index[valid][0]
break
if k == (maxnum + 1):
raise Exception('Could not find any near neighbor with a '
'nonzero distance. Try increasing the '
'value of maxnum.')
return np.squeeze(indices), np.squeeze(dists)
maximum Lyapunov exponent
def mle(y, maxt=500, window=10, metric='euclidean', maxnum=None):
"""Estimate the maximum Lyapunov exponent.
Estimates the maximum Lyapunov exponent (MLE) from a
multi-dimensional series using the algorithm described by
Rosenstein et al. (1993).
Parameters
----------
y : ndarray
Multi-dimensional real input array containing points in the
phase space.
maxt : int, optional (default = 500)
Maximum time (iterations) up to which the average divergence
should be computed.
window : int, optional (default = 10)
Minimum temporal separation (Theiler window) that should exist
between near neighbors (see Notes).
maxnum : int, optional (default = None (optimum))
Maximum number of near neighbors that should be found for each
point. In rare cases, when there are no neighbors that are at a
nonzero distance, this will have to be increased (i.e., beyond
2 * window + 3).
Returns
-------
d : array
Average divergence for each time up to maxt.
Notes
-----
This function does not directly estimate the MLE. The MLE should be
estimated by linearly fitting the average divergence (i.e., the
average of the logarithms of near-neighbor distances) with time.
It is also important to choose an appropriate Theiler window so that
the near neighbors do not lie on the same trajectory, in which case
the estimated MLE will always be close to zero.
"""
index, dist = utils.neighbors(y, metric=metric, window=window,
maxnum=maxnum)
m = len(y)
maxt = min(m - window - 1, maxt)
d = np.empty(maxt)
d[0] = np.mean(np.log(dist))
for t in range(1, maxt):
t1 = np.arange(t, m)
t2 = index[:-t] + t
# Sometimes the nearest point would be farther than (m - maxt)
# in time. Such trajectories needs to be omitted.
valid = t2 < m
t1, t2 = t1[valid], t2[valid]
d[t] = np.mean(np.log(utils.dist(y[t1], y[t2], metric=metric)))
return d
RANSAC 擬合曲線
需要先安裝 sklearn 庫
def poly_fit(x, y, degree, fit="RANSAC"):
# check if we can use RANSAC
if fit == "RANSAC":
try:
# ignore ImportWarnings in sklearn
with warnings.catch_warnings():
warnings.simplefilter("ignore", ImportWarning)
import sklearn.linear_model as sklin
import sklearn.preprocessing as skpre
except ImportError:
warnings.warn(
"fitting mode 'RANSAC' requires the package sklearn, using"
+ " 'poly' instead",
RuntimeWarning)
fit = "poly"
if fit == "poly":
return np.polyfit(x, y, degree)
elif fit == "RANSAC":
model = sklin.RANSACRegressor(sklin.LinearRegression(fit_intercept=False))
xdat = np.asarray(x)
if len(xdat.shape) == 1:
# interpret 1d-array as list of len(x) samples instead of
# one sample of length len(x)
xdat = xdat.reshape(-1, 1)
polydat = skpre.PolynomialFeatures(degree).fit_transform(xdat)
try:
model.fit(polydat, y)
coef = model.estimator_.coef_[::-1]
except ValueError:
warnings.warn(
"RANSAC did not reach consensus, "
+ "using numpy's polyfit",
RuntimeWarning)
coef = np.polyfit(x, y, degree)
return coef
else:
raise ValueError("invalid fitting mode ({})".format(fit))
例子:計算洛倫茲系統的最大李雅普諾夫指數
import warnings
from nolitsa import data, lyapunov
import numpy as np
import matplotlib.pyplot as plt
dt = 0.01
x0 = [0.62225717, -0.08232857, 30.60845379]
x = data.lorenz(length=4000, sample=dt, x0=x0,
sigma=16.0, beta=4.0, rho=45.92)[1]
plt.plot(range(len(x)),x)
plt.show()
# Choose appropriate Theiler window.
meanperiod = 30
maxt = 250
d = lyapunov.mle(x, maxt=maxt, window=meanperiod)
t = np.arange(maxt) *dt
coefs = poly_fit(t, d, 1)
print('LLE = ', coefs[0])
plt.title('Maximum Lyapunov exponent for the Lorenz system')
plt.xlabel(r'Time $t$')
plt.ylabel(r'Average divergence $\langle d_i(t) \rangle$')
plt.plot(t, d, label='divergence')
plt.plot(t, t * 1.50, '--', label='slope=1.5')
plt.plot(t, coefs[1] +coefs[0]* t, '--', label='RANSAC')
plt.legend()
plt.show()