It is surprising how much of modern dynamics boils down to an extremely simple formula
This innocuous-looking equation carries such riddles, such surprises, such unintuitive behavior that it can become the object of study for life. This equation is called a vector flow equation, and it can be used to capture the essential physics of economies, neurons, ecosystems, networks, and even orbits of photons around black holes. This equation is to modern dynamics what F = ma was to classical mechanics. It is the starting point for understanding complex systems.
The Magic of Phase Space
The apparent simplicity of the “flow equation” masks the complexity it contains. It is a vector equation because each “dimension” is a variable of a complex system. Many systems of interest may have only a few variables, but ecosystems and economies and social networks may have hundreds or thousands of variables. Expressed in component format, the flow equation is
where the superscript spans the number of variables. But even this masks all that can happen with such an equation. Each of the functions fa can be entirely different from each other, and can be any type of function, whether polynomial, rational, algebraic, transcendental or composite, although they must be single-valued. They are generally nonlinear, and the limitless ways that functions can be nonlinear is where the richness of the flow equation comes from.
The vector flow equation is an ordinary differential equation (ODE) that can be solved for specific trajectories as initial value problems. A single set of initial conditions defines a unique trajectory. For instance, the trajectory for a 4-dimensional example is described as the column vector
which is the single-parameter position vector to a point in phase space, also called state space. The point sweeps through successive configurations as a function of its single parameter—time. This trajectory is also called an orbit. In classical mechanics, the focus has tended to be on the behavior of specific orbits that arise from a specific set of initial conditions. This is the classic “rock thrown from a cliff” problem of introductory physics courses. However, in modern dynamics, the focus shifts away from individual trajectories to encompass the set of all possible trajectories.
Why is Modern Dynamics part of Physics?
If finding the solutions to the “x-dot equals f” vector flow equation is all there is to do, then this would just be a math problem—the solution of ODE’s. There are plenty of gems for mathematicians to look for, and there is an entire of field of study in mathematics called “dynamical systems“, but this would not be “physics”. Physics as a profession is separate and distinct from mathematics, although the two are sometimes confused. Physics uses mathematics as its language and as its toolbox, but physics is not mathematics. Physics is done best when it is done qualitatively—this means with scribbles done on napkins in restaurants or on the back of envelopes while waiting in line. Physics is about recognizing relationships and patterns. Physics is about identifying the limits to scaling properties where the physics changes when scales change. Physics is about the mapping of the simplest possible mathematics onto behavior in the physical world, and recognizing when the simplest possible mathematics is a universal that applies broadly to diverse systems that seem different, but that share the same underlying principles.
So, granted solving ODE’s is not physics, there is still a tremendous amount of good physics that can be done by solving ODE’s. ODE solvers become the modern physicist’s experimental workbench, providing data output from numerical experiments that can test the dependence on parameters in ways that real-world experiments might not be able to access. Physical intuition can be built based on such simulations as the engaged physicist begins to “understand” how the system behaves, able to explain what will happen as the values of parameters are changed.
In the follow sections, three examples of modern dynamics are introduced with a preliminary study, including Python code. These examples are: Galactic dynamics, synchronized networks and ecosystems. Despite their very different natures, their description using dynamical flows share features in common and illustrate the beauty and depth of behavior that can be explored with simple equations.
One example of the power and beauty of the vector flow equation and its set of all solutions in phase space is called the Henon-Heiles model of the motion of a star within a galaxy. Of course, this is a terribly complicated problem that involves tens of billions of stars, but if you average over the gravitational potential of all the other stars, and throw in a couple of conservation laws, the resulting potential can look surprisingly simple. The motion in the plane of this galactic potential takes two configuration coordinates (x, y) with two associated momenta (px, py) for a total of four dimensions. The flow equations in four-dimensional phase space are simply
where the terms in the light blue box describe a two-dimensional simple harmonic oscillator (SHO), which is a linear oscillator, modified by the terms in the magenta box that represent the nonlinear galactic potential. The orbits of this Hamiltonian system are chaotic, and because there is no dissipation in the model, a single orbit will continue forever within certain ranges of phase space governed by energy conservation, but never quite repeating.
(Python code on GitHub.)
#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Hamilton4D.py Created on Wed Apr 18 06:03:32 2018 @author: nolte Derived from: D. D. Nolte, Introduction to Modern Dynamics: Chaos, Networks, Space and Time, 2nd ed. (Oxford,2019) """ import numpy as np import matplotlib as mpl from mpl_toolkits.mplot3d import Axes3D from scipy import integrate from matplotlib import pyplot as plt from matplotlib import cm import time import os plt.close('all') # model_case 1 = Heiles # model_case 2 = Crescent print(' ') print('Hamilton4D.py') print('Case: 1 = Heiles') print('Case: 2 = Crescent') model_case = int(input('Enter the Model Case (1-2)')) if model_case == 1: E = 1 # Heiles: 1, 0.3411 Crescent: 0.05, 1 epsE = 0.3411 # 3411 def flow_deriv(x_y_z_w,tspan): x, y, z, w = x_y_z_w a = z b = w c = -x - epsE*(2*x*y) d = -y - epsE*(x**2 - y**2) return[a,b,c,d] else: E = .1 # Crescent: 0.1, 1 epsE = 1 def flow_deriv(x_y_z_w,tspan): x, y, z, w = x_y_z_w a = z b = w c = -(epsE*(y-2*x**2)*(-4*x) + x) d = -(y-epsE*2*x**2) return[a,b,c,d] prms = np.sqrt(E) pmax = np.sqrt(2*E) # Potential Function if model_case == 1: V = np.zeros(shape=(100,100)) for xloop in range(100): x = -2 + 4*xloop/100 for yloop in range(100): y = -2 + 4*yloop/100 V[yloop,xloop] = 0.5*x**2 + 0.5*y**2 + epsE*(x**2*y - 0.33333*y**3) else: V = np.zeros(shape=(100,100)) for xloop in range(100): x = -2 + 4*xloop/100 for yloop in range(100): y = -2 + 4*yloop/100 V[yloop,xloop] = 0.5*x**2 + 0.5*y**2 + epsE*(2*x**4 - 2*x**2*y) fig = plt.figure(1) contr = plt.contourf(V,100, cmap=cm.coolwarm, vmin = 0, vmax = 10) fig.colorbar(contr, shrink=0.5, aspect=5) fig = plt.show() repnum = 250 mulnum = 64/repnum np.random.seed(1) for reploop in range(repnum): px1 = 2*(np.random.random((1))-0.499)*pmax py1 = np.sign(np.random.random((1))-0.499)*np.real(np.sqrt(2*(E-px1**2/2))) xp1 = 0 yp1 = 0 x_y_z_w0 = [xp1, yp1, px1, py1] tspan = np.linspace(1,1000,10000) x_t = integrate.odeint(flow_deriv, x_y_z_w0, tspan) siztmp = np.shape(x_t) siz = siztmp if reploop % 50 == 0: plt.figure(2) lines = plt.plot(x_t[:,0],x_t[:,1]) plt.setp(lines, linewidth=0.5) plt.show() time.sleep(0.1) #os.system("pause") y1 = x_t[:,0] y2 = x_t[:,1] y3 = x_t[:,2] y4 = x_t[:,3] py = np.zeros(shape=(2*repnum,)) yvar = np.zeros(shape=(2*repnum,)) cnt = -1 last = y1 for loop in range(2,siz): if (last < 0)and(y1[loop] > 0): cnt = cnt+1 del1 = -y1[loop-1]/(y1[loop] - y1[loop-1]) py[cnt] = y4[loop-1] + del1*(y4[loop]-y4[loop-1]) yvar[cnt] = y2[loop-1] + del1*(y2[loop]-y2[loop-1]) last = y1[loop] else: last = y1[loop] plt.figure(3) lines = plt.plot(yvar,py,'o',ms=1) plt.show() if model_case == 1: plt.savefig('Heiles') else: plt.savefig('Crescent')
Networks, Synchronization and Emergence
A central paradigm of nonlinear science is the emergence of patterns and organized behavior from seemingly random interactions among underlying constituents. Emergent phenomena are among the most awe inspiring topics in science. Crystals are emergent, forming slowly from solutions of reagents. Life is emergent, arising out of the chaotic soup of organic molecules on Earth (or on some distant planet). Intelligence is emergent, and so is consciousness, arising from the interactions among billions of neurons. Ecosystems are emergent, based on competition and symbiosis among species. Economies are emergent, based on the transfer of goods and money spanning scales from the local bodega to the global economy.
One of the common underlying properties of emergence is the existence of networks of interactions. Networks and network science are topics of great current interest driven by the rise of the World Wide Web and social networks. But networks are ubiquitous and have long been the topic of research into complex and nonlinear systems. Networks provide a scaffold for understanding many of the emergent systems. It allows one to think of isolated elements, like molecules or neurons, that interact with many others, like the neighbors in a crystal or distant synaptic connections.
From the point of view of modern dynamics, the state of a node can be a variable or a “dimension” and the interactions among links define the functions of the vector flow equation. Emergence is then something that “emerges” from the dynamical flow as many elements interact through complex networks to produce simple or emergent patterns.
Synchronization is a form of emergence that happens when lots of independent oscillators, each vibrating at their own personal frequency, are coupled together to push and pull on each other, entraining all the individual frequencies into one common global oscillation of the entire system. Synchronization plays an important role in the solar system, explaining why the Moon always shows one face to the Earth, why Saturn’s rings have gaps, and why asteroids are mainly kept away from colliding with the Earth. Synchronization plays an even more important function in biology where it coordinates the beating of the heart and the functioning of the brain.
One of the most dramatic examples of synchronization is the Kuramoto synchronization phase transition. This occurs when a large set of individual oscillators with differing natural frequencies interact with each other through a weak nonlinear coupling. For small coupling, all the individual nodes oscillate at their own frequency. But as the coupling increases, there is a sudden coalescence of all the frequencies into a single common frequency. This mechanical phase transition, called the Kuramoto transition, has many of the properties of a thermodynamic phase transition, including a solution that utilizes mean field theory.
The simulation of 20 Poncaré phase oscillators with global coupling is shown in Fig. 4 as a function of increasing coupling coefficient g. The original individual frequencies are spread randomly. The oscillators with similar frequencies are the first to synchronize, forming small clumps that then synchronize with other clumps of oscillators, until all oscillators are entrained to a single compromise frequency. The Kuramoto phase transition is not sharp in this case because the value of N = 20 is too small. If the simulation is run for 200 oscillators, there is a sudden transition from unsynchronized to synchronized oscillation at a threshold value of g.
The Kuramoto phase transition is one of the most important fundamental examples of modern dynamics because it illustrates many facets of nonlinear dynamics in a very simple way. It highlights the importance of nonlinearity, the simplification of phase oscillators, the use of mean field theory, the underlying structure of the network, and the example of a mechanical analog to a thermodynamic phase transition. It also has analytical solutions because of its simplicity, while still capturing the intrinsic complexity of nonlinear systems.
(Python code on GitHub.)
#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Created on Sat May 11 08:56:41 2019 @author: nolte D. D. Nolte, Introduction to Modern Dynamics: Chaos, Networks, Space and Time, 2nd ed. (Oxford,2019) """ # https://www.python-course.eu/networkx.php # https://networkx.github.io/documentation/stable/tutorial.html # https://networkx.github.io/documentation/stable/reference/functions.html import numpy as np from scipy import integrate from matplotlib import pyplot as plt import networkx as nx from UserFunction import linfit import time tstart = time.time() plt.close('all') Nfac = 25 # 25 N = 50 # 50 width = 0.2 # model_case 1 = complete graph (Kuramoto transition) # model_case 2 = Erdos-Renyi model_case = int(input('Input Model Case (1-2)')) if model_case == 1: facoef = 0.2 nodecouple = nx.complete_graph(N) elif model_case == 2: facoef = 5 nodecouple = nx.erdos_renyi_graph(N,0.1) # function: omegout, yout = coupleN(G) def coupleN(G): # function: yd = flow_deriv(x_y) def flow_deriv(y,t0): yp = np.zeros(shape=(N,)) for omloop in range(N): temp = omega[omloop] linksz = G.node[omloop]['numlink'] for cloop in range(linksz): cindex = G.node[omloop]['link'][cloop] g = G.node[omloop]['coupling'][cloop] temp = temp + g*np.sin(y[cindex]-y[omloop]) yp[omloop] = temp yd = np.zeros(shape=(N,)) for omloop in range(N): yd[omloop] = yp[omloop] return yd # end of function flow_deriv(x_y) mnomega = 1.0 for nodeloop in range(N): omega[nodeloop] = G.node[nodeloop]['element'] x_y_z = omega # Settle-down Solve for the trajectories tsettle = 100 t = np.linspace(0, tsettle, tsettle) x_t = integrate.odeint(flow_deriv, x_y_z, t) x0 = x_t[tsettle-1,0:N] t = np.linspace(1,1000,1000) y = integrate.odeint(flow_deriv, x0, t) siztmp = np.shape(y) sy = siztmp # Fit the frequency m = np.zeros(shape = (N,)) w = np.zeros(shape = (N,)) mtmp = np.zeros(shape=(4,)) btmp = np.zeros(shape=(4,)) for omloop in range(N): if np.remainder(sy,4) == 0: mtmp,btmp = linfit(t[0:sy//2],y[0:sy//2,omloop]); mtmp,btmp = linfit(t[sy//2+1:sy],y[sy//2+1:sy,omloop]); mtmp,btmp = linfit(t[sy//4+1:3*sy//4],y[sy//4+1:3*sy//4,omloop]); mtmp,btmp = linfit(t,y[:,omloop]); else: sytmp = 4*np.floor(sy/4); mtmp,btmp = linfit(t[0:sytmp//2],y[0:sytmp//2,omloop]); mtmp,btmp = linfit(t[sytmp//2+1:sytmp],y[sytmp//2+1:sytmp,omloop]); mtmp,btmp = linfit(t[sytmp//4+1:3*sytmp/4],y[sytmp//4+1:3*sytmp//4,omloop]); mtmp,btmp = linfit(t[0:sytmp],y[0:sytmp,omloop]); #m[omloop] = np.median(mtmp) m[omloop] = np.mean(mtmp) w[omloop] = mnomega + m[omloop] omegout = m yout = y return omegout, yout # end of function: omegout, yout = coupleN(G) Nlink = N*(N-1)//2 omega = np.zeros(shape=(N,)) omegatemp = width*(np.random.rand(N)-1) meanomega = np.mean(omegatemp) omega = omegatemp - meanomega sto = np.std(omega) lnk = np.zeros(shape = (N,), dtype=int) for loop in range(N): nodecouple.node[loop]['element'] = omega[loop] nodecouple.node[loop]['link'] = list(nx.neighbors(nodecouple,loop)) nodecouple.node[loop]['numlink'] = np.size(list(nx.neighbors(nodecouple,loop))) lnk[loop] = np.size(list(nx.neighbors(nodecouple,loop))) avgdegree = np.mean(lnk) mnomega = 1 facval = np.zeros(shape = (Nfac,)) yy = np.zeros(shape=(Nfac,N)) xx = np.zeros(shape=(Nfac,)) for facloop in range(Nfac): print(facloop) fac = facoef*(16*facloop/(Nfac))*(1/(N-1))*sto/mnomega for nodeloop in range(N): nodecouple.node[nodeloop]['coupling'] = np.zeros(shape=(lnk[nodeloop],)) for linkloop in range (lnk[nodeloop]): nodecouple.node[nodeloop]['coupling'][linkloop] = fac facval[facloop] = fac*avgdegree omegout, yout = coupleN(nodecouple) # Here is the subfunction call for the flow for omloop in range(N): yy[facloop,omloop] = omegout[omloop] xx[facloop] = facval[facloop] plt.figure(1) lines = plt.plot(xx,yy) plt.setp(lines, linewidth=0.5) plt.show() elapsed_time = time.time() - tstart print('elapsed time = ',format(elapsed_time,'.2f'),'secs')
The Web of Life
Ecosystems are among the most complex systems on Earth. The complex interactions among hundreds or thousands of species may lead to steady homeostasis in some cases, to growth and collapse in other cases, and to oscillations or chaos in yet others. But the definition of species can be broad and abstract, referring to businesses and markets in economic ecosystems, or to cliches and acquaintances in social ecosystems, among many other examples. These systems are governed by the laws of evolutionary dynamics that include fitness and survival as well as adaptation.
The dimensionality of the dynamical spaces for these systems extends to hundreds or thousands of dimensions—far too complex to visualize when thinking in four dimensions is already challenging. Yet there are shared principles and common behaviors that emerge even here. Many of these can be illustrated in a simple three-dimensional system that is represented by a triangular simplex that can be easily visualized, and then generalized back to ultra-high dimensions once they are understood.
A simplex is a closed (N-1)-dimensional geometric figure that describes a zero-sum game (game theory is an integral part of evolutionary dynamics) among N competing species. For instance, a two-simplex is a triangle that captures the dynamics among three species. Each vertex of the triangle represents the situation when the entire ecosystem is composed of a single species. Anywhere inside the triangle represents the situation when all three species are present and interacting.
A classic model of interacting species is the replicator equation. It allows for a fitness-based proliferation and for trade-offs among the individual species. The replicator dynamics equations are shown in Fig. 5.
The population dynamics on the 2D simplex are shown in Fig. 6 for several different pay-off matrices. The matrix values are shown in color and help interpret the trajectories. For instance the simplex on the upper-right shows a fixed point center. This reflects the antisymmetric character of the pay-off matrix around the diagonal. The stable spiral on the lower-left has a nearly asymmetric pay-off matrix, but with unequal off-diagonal magnitudes. The other two cases show central saddle points with stable fixed points on the boundary. A very large variety of behaviors are possible for this very simple system. The Python program is shown in Trirep.py.
(Python code on GitHub.)
#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ trirep.py Created on Thu May 9 16:23:30 2019 @author: nolte Derived from: D. D. Nolte, Introduction to Modern Dynamics: Chaos, Networks, Space and Time, 2nd ed. (Oxford,2019) """ import numpy as np from scipy import integrate from matplotlib import pyplot as plt plt.close('all') def tripartite(x,y,z): sm = x + y + z xp = x/sm yp = y/sm f = np.sqrt(3)/2 y0 = f*xp x0 = -0.5*xp - yp + 1; plt.figure(2) lines = plt.plot(x0,y0) plt.setp(lines, linewidth=0.5) plt.plot([0, 1],[0, 0],'k',linewidth=1) plt.plot([0, 0.5],[0, f],'k',linewidth=1) plt.plot([1, 0.5],[0, f],'k',linewidth=1) plt.show() def solve_flow(y,tspan): def flow_deriv(y, t0): #"""Compute the time-derivative .""" f = np.zeros(shape=(N,)) for iloop in range(N): ftemp = 0 for jloop in range(N): ftemp = ftemp + A[iloop,jloop]*y[jloop] f[iloop] = ftemp phitemp = phi0 # Can adjust this from 0 to 1 to stabilize (but Nth population is no longer independent) for loop in range(N): phitemp = phitemp + f[loop]*y[loop] phi = phitemp yd = np.zeros(shape=(N,)) for loop in range(N-1): yd[loop] = y[loop]*(f[loop] - phi); if np.abs(phi0) < 0.01: # average fitness maintained at zero yd[N-1] = y[N-1]*(f[N-1]-phi); else: # non-zero average fitness ydtemp = 0 for loop in range(N-1): ydtemp = ydtemp - yd[loop] yd[N-1] = ydtemp return yd # Solve for the trajectories t = np.linspace(0, tspan, 701) x_t = integrate.odeint(flow_deriv,y,t) return t, x_t # model_case 1 = zero diagonal # model_case 2 = zero trace # model_case 3 = asymmetric (zero trace) print(' ') print('trirep.py') print('Case: 1 = antisymm zero diagonal') print('Case: 2 = antisymm zero trace') print('Case: 3 = random') model_case = int(input('Enter the Model Case (1-3)')) N = 3 asymm = 3 # 1 = zero diag (replicator eqn) 2 = zero trace (autocatylitic model) 3 = random (but zero trace) phi0 = 0.001 # average fitness (positive number) damps oscillations T = 100; if model_case == 1: Atemp = np.zeros(shape=(N,N)) for yloop in range(N): for xloop in range(yloop+1,N): Atemp[yloop,xloop] = 2*(0.5 - np.random.random(1)) Atemp[xloop,yloop] = -Atemp[yloop,xloop] if model_case == 2: Atemp = np.zeros(shape=(N,N)) for yloop in range(N): for xloop in range(yloop+1,N): Atemp[yloop,xloop] = 2*(0.5 - np.random.random(1)) Atemp[xloop,yloop] = -Atemp[yloop,xloop] Atemp[yloop,yloop] = 2*(0.5 - np.random.random(1)) tr = np.trace(Atemp) A = Atemp for yloop in range(N): A[yloop,yloop] = Atemp[yloop,yloop] - tr/N else: Atemp = np.zeros(shape=(N,N)) for yloop in range(N): for xloop in range(N): Atemp[yloop,xloop] = 2*(0.5 - np.random.random(1)) tr = np.trace(Atemp) A = Atemp for yloop in range(N): A[yloop,yloop] = Atemp[yloop,yloop] - tr/N plt.figure(3) im = plt.matshow(A,3,cmap=plt.cm.get_cmap('seismic')) # hsv, seismic, bwr cbar = im.figure.colorbar(im) M = 20 delt = 1/M ep = 0.01; tempx = np.zeros(shape = (3,)) for xloop in range(M): tempx = delt*(xloop)+ep; for yloop in range(M-xloop): tempx = delt*yloop+ep tempx = 1 - tempx - tempx x0 = tempx/np.sum(tempx); # initial populations tspan = 70 t, x_t = solve_flow(x0,tspan) y1 = x_t[:,0] y2 = x_t[:,1] y3 = x_t[:,2] plt.figure(1) lines = plt.plot(t,y1,t,y2,t,y3) plt.setp(lines, linewidth=0.5) plt.show() plt.ylabel('X Position') plt.xlabel('Time') tripartite(y1,y2,y3)
Topics in Modern Dynamics
These three examples are just the tip of the iceberg. The topics in modern dynamics are almost numberless. Any system that changes in time is a potential object of study in modern dynamics. Here is a list of a few topics that spring to mind.
- Keplerian orbits
- Three-body problem
- Lagrange points
- Driven damped harmonic oscillator
- Duffing oscillator
- van der Pol oscillator
- Hamiltonian maps and tapestries
- Dynamic networks
- Viral infection
- Evolutionary dynamics
- Population dynamics
- Ecosystem symbiosis
- Neural networks: recurrent
- Worldlines of spacetime
- Optical ray trajectories (Eikonal) and caustics
- Photon orbits
- Gravitational lensing
- Black holes
- Dynamics of the expanding universe
D. D. Nolte, Introduction to Modern Dynamics: Chaos, Networks, Space and Time, 2nd Ed. (Oxford University Press, 2019) (The physics and the derivations of the equations for the examples in this blog can be found here.)
D. D. Nolte, Galileo Unbound: A Path Across Life, the Universe and Everything (Oxford University Press, 2018) (The historical origins of the examples in this blog can be found here.)