Wolf et al. (1985) outlined an algorithm that estimates the Lyapunov spectra of systems whose equations are known using local Jacobian matrices and Gram-Schmidt orthonormalization. Python code is available for Wolf’s algorithm and discrete maps and their inverted counterparts. I have adapted this code to estimate Lyapunov spectra for continuous-time systems like the Lorenz attractor and Rossler attractor. Additionally, Python code is available to generate time series for ordinary differential equations. Lyapunov spectrum code is also available on Clint Sprott’s website.
Source Code:
import math, operator, random h = 0.01 cmax = 1000 # number of iterations to perform choice = input("Which system would you like?\n (1) Rossler \n (2) Lorenz\n") while str(choice) != "1" and str(choice) != "2": print("Please select 1 or 2") choice = input("Which system would you like?\n (1) Rossler \n (2) Lorenz\n")
print "\n", a = 0.2 b = 0.2 c = 5.7 def derivs(x, xnew, n): if choice == 1: return Rossler(x, xnew, n) else: return Lorenz(x, xnew, n) def Rossler(x, xnew, n): # Nonlinear Rossler Equations xnew[1] = -x[2]-x[3] xnew[2] = x[1] + a * x[2] xnew[3] = b + x[3] * (x[1] - c) # Linearized Rossler Equations xnew[4] = -1*x[7]-x[10] xnew[5] = -1*x[8]-x[11] xnew[6] = -1*x[9]-x[12] xnew[7] = x[4] + a*x[7] xnew[8] = x[5] + a*x[8] xnew[9] = x[6] + a*x[9] xnew[10] = x[3]*x[4] + x[1]*x[10] - c*x[10] xnew[11] = x[3]*x[5] + x[1]*x[11] - c*x[11] xnew[12] = x[3]*x[6] + x[1]*x[12] - c*x[12] return [x, xnew] def Lorenz(x, xnew, n): # Nonlinear Lorenz Equations xnew[1] = 10 * (x[2] - x[1]) xnew[2] = -1*x[1] * x[3] + 28 * x[1] - x[2] xnew[3] = x[1] * x[2] - 8/3.0 * x[3] # Linearized Lorenz Equations xnew[4] = -10 * x[4] + 10 * x[7] xnew[5] = -10 * x[5] + 10 * x[8] xnew[6] = -10 * x[6] + 10 * x[9] xnew[7] = 28*x[4]-x[3]*x[4] - x[7] - x[1]*x[10] xnew[8] = 28*x[5]-x[3]*x[5] - x[8] - x[1]*x[11] xnew[9] = 28*x[6]-x[3]*x[6] - x[9] - x[1]*x[12] xnew[10] = x[2]*x[4] + x[1]*x[7] - 8/3.0 * x[10] xnew[11] = x[2]*x[5] + x[1]*x[8] - 8/3.0 * x[11] xnew[12] = x[2]*x[6] + x[1]*x[9] - 8/3.0 * x[12] return [x, xnew] def timeseries(cmax): X0 = [] Y0 = [] Z0 = [] xList = [] yList = [] zList = [] changeInTime = h # Initial conditions if choice == 1: # Rossler X0.append(0.01) Y0.append(0.01) Z0.append(0.01) else: # Lorenz X0.append(0) Y0.append(1) Z0.append(0) t = 0 while len(xList) <= cmax: [x,y,z] = Rk4o(X0, Y0, Z0, h, len(X0)) X0.append(x) Y0.append(y) Z0.append(z) if 200 < t: xList.append(x) yList.append(y) zList.append(z) changeInTime += h t = t + 1 return [xList, yList, zList] def f(x,y,z): if choice == 1: dxdt = -y-z else: dxdt = 10 * (y - x) return dxdt def g(x,y,z): if choice == 1: dydt = x + a * y else: dydt = 28 * x - y - x*z return dydt def e(x,y,z): if choice == 1: dzdt = b + z * (x - c) else: dzdt = x * y - 8/3.0 * z return dzdt def Rk4o(xList, yList, zList, h, t): k1x = h*f(xList[t-1],yList[t-1], zList[t-1]) k1y = h*g(xList[t-1],yList[t-1], zList[t-1]) k1z = h*e(xList[t-1],yList[t-1], zList[t-1]) k2x = h*f(xList[t-1] + k1x/2,yList[t-1] + k1y/2, zList[t-1] + k1y/2) k2y = h*g(xList[t-1] + k1x/2,yList[t-1] + k1y/2, zList[t-1] + k1y/2) k2z = h*e(xList[t-1] + k1x/2,yList[t-1] + k1y/2, zList[t-1] + k1y/2) k3x = h*f(xList[t-1] + k2x/2,yList[t-1] + k2y/2, zList[t-1] + k2y/2) k3y = h*g(xList[t-1] + k2x/2,yList[t-1] + k2y/2, zList[t-1] + k2y/2) k3z = h*e(xList[t-1] + k2x/2,yList[t-1] + k2y/2, zList[t-1] + k2y/2) k4x = h*f(xList[t-1] + k3x/2,yList[t-1] + k3y/2, zList[t-1] + k3y/2) k4y = h*g(xList[t-1] + k3x/2,yList[t-1] + k3y/2, zList[t-1] + k3y/2) k4z = h*e(xList[t-1] + k3x/2,yList[t-1] + k3y/2, zList[t-1] + k3y/2) x = xList[t-1] + k1x/6 + k2x/3 + k3x/3 + k4x/6 y = yList[t-1] + k1y/6 + k2y/3 + k3y/3 + k4y/6 z = zList[t-1] + k1z/6 + k2z/3 + k3z/3 + k4z/6 return [x,y,z] n = 3 # number of variables in nonlinear system nn=n*(n+1) # total number of variables (nonlinear + linear) m = 0 x = [] xnew = [] v = [] ltot = [] znorm = [] gsc = [] A = [] B = [] C = [] D = [] i = 0 while i <= nn: x.append(0) xnew.append(0) v.append(0) A.append(0) B.append(0) C.append(0) D.append(0) i = i + 1 i = 0 while i <= n: ltot.append(0) znorm.append(0) gsc.append(0) i = i + 1 irate=10 # integration steps per reorthonormalization io= 100 # number of iterations between normalization # initial conditions for nonlinear maps # must be within the basin of attraction # Generate a random transient before starting the initial conditions i = 1 while i <= n: v[i] = 0.001 i = i + 1 transient = random.randint(n,100000) # Generate the initial conditions for the system [tempx,tempy,tempz] = timeseries(transient) v[1] = tempx[len(tempx)-1] v[2] = tempy[len(tempy)-1] v[3] = tempz[len(tempz)-1] i = n+1 while i <= nn: # initial conditions for linearized maps v[i]=0 # Don't mess with these; they are problem independent! i = i + 1 i = 1 while i <= n: v[(n+1)*i]=1 ltot[i]=0 i = i + 1 #print "v = ",v t=0 w = 0 while (w < cmax): j = 1 while j <= irate: i = 1 while i <= nn: x[i]=v[i] i = i + 1 [x, xnew] = derivs(x, xnew, n) i = 1 while i <= nn: A[i] = xnew[i] x[i] = v[i] + (h*A[i]) / 2.0 i = i + 1 [x, xnew] = derivs(x, xnew, n) i = 1 while i <= nn: B[i] = xnew[i] x[i] = v[i] + (h*B[i]) / 2.0 i = i + 1 [x, xnew] = derivs(x, xnew, n) i = 1 while i <= nn: C[i] = xnew[i] x[i] = v[i] + h*C[i] i = i + 1 [x, xnew] = derivs(x, xnew, n) i = 1 while i <= nn: D[i] = xnew[i] v[i] = v[i] + h*(A[i] + D[i] + 2*(B[i] + C[i]))/6.0 i = i + 1 t = t + h j = j + 1 #construct new orthonormal basis by gram-schmidt: znorm[1]=0 #normalize first vector j = 1 while j <= n: znorm[1]=znorm[1]+v[n*j+1]**2 j = j + 1 znorm[1] = math.sqrt(znorm[1]) j = 1 while j <= n: v[n*j+1]=v[n*j+1]/znorm[1] j = j + 1 #generate new orthonormal set: j = 2 while j <= n: k = 1 while k <= j-1: gsc[k]=0 l = 1 while l <= n: gsc[k]=gsc[k]+v[n*l+j]*v[n*l+k] l = l + 1 k = k + 1 k = 1 while k <= n: #construct a new vector l = 1 while l <= j-1: v[n*k+j]=v[n*k+j]-gsc[l]*v[n*k+l] l = l + 1 k = k + 1 znorm[j]=0 #calculate the vector's norm k = 1 while k <= n: #construct a new vector znorm[j]=znorm[j]+v[n*k+j]**2 k = k + 1 znorm[j]=math.sqrt(znorm[j]) k = 1 while k <= n: #normalize the new vector v[n*k+j] = v[n*k+j] / znorm[j] k = k + 1 j = j + 1 k = 1 while k <= n: #update running vector magnitudes if znorm[k] > 0: ltot[k] = ltot[k] + math.log(znorm[k]) k = k + 1 m = m + 1 if m % io == 0 or w == cmax-1: #normalize exponent and print every io iterations lsum=0 kmax=0 k = 1 while k <= n: le = ltot[k] / t lsum = lsum + le if lsum > 0: lsum0 = lsum kmax = k k = k + 1 w = w + 1 if choice == 1: print "Rossler:" else: print "Lorenz:" print n, "LEs = " lsum=0 kmax=0 k = 1 while k <= n: le = ltot[k] / t lsum = lsum + le if lsum > 0: lsum0 = lsum kmax = k print le k = k + 1
Comments
One response to “Estimating Lyapunov Spectra of ODEs using Python”