In [2]:
# A first Course in Random Matrix Theory
# by Marc Potters and Jean-Philippe Bouchaud
# Cambridge University Press 2020
# Python code used to generate graphs in Part II
In [3]:
%pylab inline
import scipy.linalg as npl
import scipy.optimize as opt
import scipy.special as special
import scipy.integrate as integrate
from cycler import cycler
from sklearn.isotonic import IsotonicRegression
Populating the interactive namespace from numpy and matplotlib
In [4]:
#Figures 14.1 14.2a and 14.a2b need the TracyWidom package written by Yao-Yuan Mao, Rutgers University, available at
#https://protect-eu.mimecast.com/s/-JMpC32RRFB65Rghgdl-C?domain=gist.github.com
#download the file TracyWidom.py and put it in the same directory as this notebook
try:
    import TracyWidom as TW
    TW_Available=True
except:
    TW_Available=False
In [5]:
ColorGraphs=True
if(ColorGraphs):
    HistoColor='powderblue'
else:
    plt.rcParams['axes.prop_cycle'] = cycler(color=['#000000', '#777777','#bbbbbb'])
    HistoColor='#777777'
plt.rc('legend', fontsize=8)
plt.rc('font', family='serif', size=10)
plt.rc('lines', linewidth=1, color='black')

#uncomment if latex is installed
plt.rc('text', usetex=True)
plt.rc('font', family='serif', serif='New Times Roman', size=10)
plt.rc('text.latex', preamble=r'\usepackage{mathptmx}\usepackage{amsfonts}')


#figure sizes in inches
figwidth=9./2.54
figwidth2=6/2.54
figheight=6/2.54
figdpi=200
In [6]:
#helper functions
myseed=180569
def wigner(myN,sigma2=1.):
    mat=np.random.randn(myN, myN) #non-symmetric Gaussian matrix
    return sqrt(sigma2/(2.*myN))*(mat+mat.transpose()) #symmetrize and normalize
def myg(values,z):#returns the sample Stieltjes transform of data values evaluated at points z (must be 1d array)
    return np.average(1./(z[:,newaxis]-values[newaxis,:]),axis=1)
In [7]:
plt.figure(figsize=(figwidth,figheight),dpi=figdpi)
random.seed(myseed)
dy=np.random.randn(1000)
y=cumsum(dy)*1./sqrt(1000)
x=arange(0,1,.001)
plt.plot(x,y)
plt.xlabel('$t$')
plt.ylabel('$B_t$')
plt.xlim(0,1)
#plt.ylim(0,0.345)
plt.tight_layout(pad=0.1)
#plt.savefig('fig8_1.pdf',format='pdf')
plt.show()
In [8]:
plt.figure(figsize=(figwidth2,figheight),dpi=figdpi)
random.seed(myseed)
nstep=50
Tmax=2000
delta=1./nstep
allx=np.zeros(nstep*Tmax)
xx=0
vol=1./sqrt(nstep)
for i in range(nstep*Tmax):
    vpo2dt=-xx/2.*delta
    xx=np.random.randn()*vol+vpo2dt+xx
    allx[i]=xx
Tmaxplot=40
x=arange(0,Tmaxplot,delta)
plt.plot(x,allx[:(Tmaxplot*nstep)])
plt.xlabel('$t$')
plt.ylabel('$X_t$')
plt.tight_layout(pad=0.1)
#plt.savefig('fig8_2a.pdf',format='pdf')
plt.show()
In [9]:
random.seed(myseed)
plt.figure(figsize=(figwidth2,figheight),dpi=figdpi)
plt.hist(allx,100,density=True,color=HistoColor) 
x=arange(-4,4.01,.01)
y=exp(-x*x/2.)/sqrt(2*np.pi)
plt.plot(x,y)
plt.xlim(-4,4)
plt.xlabel('$X$')
plt.ylabel('$P(X)$')
plt.tight_layout(pad=0.1)
#plt.savefig('fig8_2b.pdf',format='pdf')
plt.show()
In [10]:
plt.figure(figsize=(figwidth,figheight),dpi=figdpi)
nstep=2500
myN=25
mat=wigner(myN,.25)
allvall=np.zeros((nstep,myN))
for i in range(nstep):
    wval= npl.eigvalsh(mat)
    allvall[i]=wval
    mat=mat+wigner(myN,1./nstep)
allvall=allvall.transpose()
for j in range(myN):
    plt.plot(arange(0,1,1./nstep),allvall[j])
plt.xlabel('$t$')
plt.ylabel('$\lambda_t$')
plt.tight_layout(pad=0.1)
#plt.savefig('fig9_1.pdf',format='pdf')
plt.show()
In [11]:
plt.figure(figsize=(figwidth2,figheight),dpi=figdpi)
nstep=100
Tmax=10
myN=25
mat=wigner(myN,.1)
allvall=np.zeros((nstep*Tmax,myN))
for i in range(nstep*Tmax):
    wval= npl.eigvalsh(mat)
    allvall[i]=wval
    nextval=wval-(0.5/nstep)*(wval+pow(wval,3)) #previous value plus restoring force for DBM in a potential
    mat=diag(nextval)+wigner(myN,1./nstep)
allvall=allvall.transpose()
for j in range(myN):
    plt.plot(arange(0,Tmax,1./nstep),allvall[j])
plt.xlabel('$t$')
plt.ylabel('$\lambda_t$')
plt.tight_layout(pad=0.1)
#plt.savefig('fig9_2a.pdf',format='pdf')
plt.show()
In [12]:
plt.figure(figsize=(figwidth2,figheight),dpi=figdpi)
nstep=200
Tmax=10
myN=400
mat=wigner(myN,0.6) #initialize DBM with wigner matrix
allvall=np.zeros((nstep*(Tmax-3),myN))
for i in range(nstep*Tmax):
    wval= npl.eigvalsh(mat)
    if(i>=nstep*3): #only record eigenvalues after time t>3 to let the distribution attain steady-state
        allvall[i-nstep*3]=wval
    nextval=wval-(0.5/nstep)*(wval+pow(wval,3)) #previous value plus restoring force for DBM in a potential
    mat=diag(nextval)+wigner(myN,1./nstep) 
plt.hist(allvall.flatten(),100,density=True,color=HistoColor) 
def rho(x,gamma=0):
    a2=(sqrt(1+12*gamma)-1)/(6*gamma)
    a=sqrt(a2)
    def q(x):
        return gamma*x**2+1+2*gamma*a2
    return q(x)*sqrt(np.maximum(4*a2-x**2,0))/(2*np.pi)
x=arange(-1.6,1.6,.01)
y=rho(x,1.)
plt.plot(x,y)
plt.xlim(-1.6,1.6)
plt.xlabel('$\lambda$')
plt.ylabel('$\\rho(\lambda)$')
plt.tight_layout(pad=0.1)
#plt.savefig('fig9_2b.pdf',format='pdf')
plt.show()
In [13]:
plt.figure(figsize=(figwidth,figheight),dpi=figdpi)
x=np.arange(0,6,.5)
y=x*0.
plt.plot(x,y,marker='x',linestyle='none',color='C0')
x=np.arange(7,8)
y=x*0.
plt.plot(x,y,marker='x',linestyle='none',color='C1')
x=np.arange(0,10)
y=x*0.
plt.plot(x,y,color='black')
myy=arange(-.5,.501,.01)
myx=6+sqrt(abs(1-4*myy**2))
plt.plot(myx,myy,color='C1',linestyle='--')
plt.annotate(text='', xy=(6,-1), xytext=(6,1), arrowprops=dict(arrowstyle='<-'))
plt.annotate(text='$\Lambda$', xy=(6,0), xytext=(5.5,.2), arrowprops=dict(arrowstyle='->'))
plt.annotate(text=r'$\mathfrak{z}(t)$', xy=(7,0), xytext=(7.4,.2), arrowprops=dict(arrowstyle='->'))
plt.annotate(text='$\lambda_{\max}$', xy=(5.5,0), xytext=(4.75,.2), arrowprops=dict(arrowstyle='->'))
plt.text(6.2,1,'$\Lambda+{\\rm i}\infty$',horizontalalignment='center')
plt.text(6.2,-1.1,'$\Lambda-{\\rm i}\infty$',horizontalalignment='center')
plt.xlabel('Re\,$z$')
plt.ylabel('Im\,$z$')
plt.xlim(0,8)
plt.ylim(-1.15,1.1)
plt.tick_params(
    axis='both',          # changes apply to the x-axis
    which='both',      # both major and minor ticks are affected
    bottom='off',      # ticks along the bottom edge are off
    top='off',         # ticks along the top edge are off
    labelbottom='off',
    left='off',
    labelleft='off') # labels along the bottom edge are off

plt.tight_layout(pad=0.1)
#plt.savefig('fig10_1.pdf',format='pdf')
plt.show()
In [14]:
plt.figure(figsize=(figwidth2,figheight),dpi=figdpi)
eig=array([-1.34193837, -0.63666888, -0.23054108,  0.9994115 ,  1.33925136])
x=arange(-4.,4.,.01)
y=myg(eig,x)
plt.plot(x,y)
plt.ylim(-8,8)
plt.xlim(-4,4)
plt.xlabel('$z$')
plt.ylabel('$g_n(z)$')
plt.axvspan(eig[4], 10, alpha=0.25, color='gray')
plt.axvspan(-10, eig[0], alpha=0.25, color='gray')
plt.tight_layout(pad=0.1)
#plt.savefig('fig10_2a.pdf',format='pdf')
plt.show()
In [15]:
plt.figure(figsize=(figwidth2,figheight),dpi=figdpi)
delta=0.01
x1=arange(-12,eig[0],delta)
x2=arange(eig[4]+delta*.5,12,delta)
x=np.concatenate((x2,x1))
y=myg(eig,x)
plt.plot(y,x,label="$\mathfrak{z}(g)$")
x=eig[4]+0*y
plt.plot(y,x,linestyle="--",label="$\lambda_{\\rm max}$")
x=eig[0]+0*y
plt.plot(y,x,linestyle=":",label="$\lambda_{\\rm min}$")
plt.xlim(-8,8)
plt.ylim(-8,8)
plt.legend()
plt.xlabel('$g$')
plt.ylabel('$\mathfrak{z}(g)$')
plt.tight_layout(pad=0.1)
#plt.savefig('fig10_2b.pdf',format='pdf')
plt.show()
In [16]:
plt.figure(figsize=(figwidth2,figheight),dpi=figdpi)
def gwig(z):
    return (z-z*sqrt(1-4./z/z))/2
x=arange(-8,8,.01)-1e-7j
y=gwig(x).real
y2=gwig(x).imag
plt.plot(x.real,y,label="Re\,$\mathfrak{g}(z)$")
plt.plot(x.real,y2,label="Im\,$\mathfrak{g}(z)$")
plt.legend()
plt.xlabel('$z$')
plt.ylabel('$\mathfrak{g}(z)$')
plt.xlim(-8,8)
plt.axvspan(2, 10, alpha=0.25, color='gray')
plt.axvspan(-10, -2, alpha=0.25, color='gray')
plt.tight_layout(pad=0.1)
#plt.savefig('fig10_3a.pdf',format='pdf')
plt.show()
In [17]:
plt.figure(figsize=(figwidth2,figheight),dpi=figdpi)
xmax=4
x=arange(-1,1,.01)
y=1./x+x
plt.plot(x,y,label="$\mathfrak{z}(g)$")
x=arange(1,xmax+1,1)
y=2+0*x
plt.plot(x,y,linestyle="--",label="$\lambda_\pm=\pm 2$")
x=arange(-xmax,-.9,1)
y=-2+0*x
plt.plot(x,y,linestyle="--",color='C1')
x=arange(-xmax,-1,.01)
y=1./x+x
plt.plot(x,y,linestyle=":",label="wrong",color='C2')
x=arange(1,xmax+1,.01)
y=1./x+x
plt.plot(x,y,linestyle=":",color='C2')

plt.xlim(-xmax,xmax)
plt.ylim(-8,8)
plt.legend()
plt.xlabel('$g$')
plt.ylabel('$\mathfrak{z}(g)$')
plt.tight_layout(pad=0.1)
#plt.savefig('fig10_3b.pdf',format='pdf')
plt.show()
In [18]:
plt.figure(figsize=(figwidth,figheight),dpi=figdpi)
def rho3(x):
    return x*(x*x-3)/(2*np.pi*sqrt(4-x*x))
def rho4(x):
    x2=x*x
    return (x2*x2-4*x2+2)/(2*np.pi*sqrt(4-x2))
x=arange(-1.99,1.995,.01)
y=rho3(x)
plt.plot(x,y,label='$\delta\\rho_3(\lambda)$')
y=rho4(x)
plt.plot(x,y,label='$\delta\\rho_4(\lambda)$')

plt.legend()
plt.xlabel('$\lambda$')
plt.ylabel('$\delta\\rho(\lambda)$')
plt.ylim(-.5,.5)
plt.tight_layout(pad=0.1)
#plt.savefig('fig12_3.pdf',format='pdf')
plt.show()
In [19]:
def optbplus(a=4,n=10):
    nm2s=(n-2.)*(n-2.)
    return (nm2s*a+sqrt(nm2s*(n*n*a*a-4*n+4.)))/(2*(n-1.)*(n-2.))
def sad3(b,n,a):
    lam=a-b
    lam2=lam+n*b
    z=lam+1./lam
    return a*n*z-(n-1)*lam*lam/2.-lam2*lam2/2.-(n-1)*log(z-lam)-log(z-lam2)
def sad_dif(a,n):
    return sad3(optbplus(a,n),n,a)-sad3(0,n,a)
def t_star(n):
    n_min=2*sqrt(max(n-1.,0))/n
    return opt.brentq(sad_dif,n_min+1e-7,1.1,args=(n,))
t_starV=vectorize(t_star)
In [20]:
plt.figure(figsize=(figwidth,figheight),dpi=figdpi)
datan=arange(2.01,10,.01)
y=t_starV(datan)
y2=(2*sqrt(datan-1.)/datan)
if (plt.rcParams['text.usetex']):
    plt.plot(datan,y,label='$t_{\\rm\scriptsize c}$')
    plt.plot(datan,y2,label='$t_{\\rm\scriptsize s}$')
    plt.ylabel('$t_{\\rm\scriptsize c}$')
else:
    plt.plot(datan,y,label='$t_{\\rm c}$')
    plt.plot(datan,y2,label='$t_{\\rm s}$')
    plt.ylabel('$t_{\\rm c}$')
plt.legend()
plt.xlim(2,10)
plt.xlabel('$n$')
plt.tight_layout(pad=0.1)
#plt.savefig('fig13_1.pdf',format='pdf')
plt.show()
In [21]:
plt.figure(figsize=(figwidth,figheight),dpi=figdpi)
def myS(x):
    return np.heaviside(1-x,1)*x**2/2.+np.heaviside(x-1,0)*(2*x-log(x)-1.5)
myx=arange(1e-7,5,.05)
myy1=myS(myx)
myy2=0.5*myx*myx
myy3=2*myx
plt.plot(myx,myy1,label='quenched')
plt.plot(myx,myy2,label='annealed')
plt.plot(myx,myy3,linestyle='--',label='upper bound')
plt.xlabel('$t$')
plt.ylabel('$H_{\mathbf{X}}(t)$')
plt.legend()
plt.tight_layout(pad=0.1)
#plt.savefig('fig13_2.pdf',format='pdf')
plt.show()
In [22]:
if(TW_Available):
    plt.figure(figsize=(figwidth,figheight),dpi=figdpi)
    x=arange(-5,3,.01)
    t1=TW.TracyWidom(1)
    t2=TW.TracyWidom(2)
    t3=TW.TracyWidom(4)
    y=t3.pdf(x)
    plt.plot(x,y,label="$f_4(u)$")
    y=t2.pdf(x)
    plt.plot(x,y,label="$f_2(u)$")
    y=t1.pdf(x)
    plt.plot(x,y,label='$f_1(u)$')
    plt.legend()
    plt.xlabel('$u$')
    plt.ylabel('$f_\\beta(u)$')
    plt.xlim(-5,3)
    plt.ylim(0,0.59)
    plt.tight_layout(pad=0.1)
    #plt.savefig('fig14_1.pdf',format='pdf')
    plt.show()
else:
    print("This plot requires TracyWidom package")
In [23]:
if(TW_Available):
    plt.figure(figsize=(figwidth2,figheight),dpi=figdpi)
    x=arange(-8,5,.01)
    ai=special.airy(x)
    y=ai[1]**2-x*ai[0]**2
    plt.plot(x,y,label='$\Phi_2(u)$')
    t2=TW.TracyWidom(2)
    y=t2.pdf(x)
    plt.plot(x,y,linestyle='--',label='$f_2(u)$')
    x=arange(-8,0.005,.01)
    y=sqrt(-x)/np.pi
    plt.plot(x,y,linestyle=':',label='$\sqrt{-u}/\pi$')
    plt.legend()
    plt.xlim(-8,2)
    plt.ylim(0,1)
    plt.xlabel('$u$')
    plt.tight_layout(pad=0.1)
    #plt.savefig('fig14_2a.pdf',format='pdf')
    plt.show()
else:
    print("This plot requires TracyWidom package")
In [24]:
if(TW_Available):
    plt.figure(figsize=(figwidth2,figheight),dpi=figdpi)
    x=arange(-3,8,.01)
    ai=special.airy(x)
    y=ai[1]**2-x*ai[0]**2
    plt.plot(x,y,label='$\Phi_2(u)$')
    t2=TW.TracyWidom(2)
    y=t2.pdf(x)
    plt.plot(x,y,linestyle='--',label='$f_2(u)$')
    x=arange(0.001,8,.01)
    y=exp(-(4./3)*x**(3./2))*13/96./x/np.pi
    plt.plot(x,y,linestyle=':',label='$\exp(-4u^{3/2}/3)/(8\pi u)$')
    plt.legend()
    plt.xlim(-3,7)
    plt.yscale('log')
    plt.ylim(1e-14,1)
    plt.xlabel('$u$')
    plt.tight_layout(pad=0.1)
    #plt.savefig('fig14_2b.pdf',format='pdf')
    plt.show()
else:
    print("This plot requires TracyWidom package")
In [25]:
plt.figure(figsize=(figwidth,figheight),dpi=figdpi)
def eigval_th(x):
    y=np.maximum(x,1.)
    return y+1./y 
random.seed(myseed)
myN=200
Ndata=200
xmax=4.
x=zeros(Ndata)
eigval=zeros(Ndata)
overlap=zeros(Ndata)
for k in range(Ndata):
    mat=wigner(myN)
    xx=(xmax*k)/Ndata
    mat[0][0]=mat[0][0]+xx
    val,vect=npl.eigh(mat,eigvals_only=False,eigvals=[myN-1,myN-1])#largest eigenvalue (with vector) of matrix mat
    x[k]=xx
    eigval[k]=val[0]
    overlap[k]=vect[0][0]**2
plt.plot(x,eigval,marker='.',linestyle='none',label='simulation',color='C1')
y2=eigval_th(x)
plt.plot(x,y2,label=r'$\lambda_1=\tilde{a}+1/\tilde{a}$; $\tilde{a}=\max(a,1)$',color='C0')
plt.legend()
plt.xlabel('$a$')
plt.ylabel('$\lambda_1$')
plt.xlim(0,4)
plt.tight_layout(pad=0.1)
#plt.savefig('fig14_3.pdf',format='pdf')
plt.show()
In [26]:
plt.figure(figsize=(figwidth,figheight),dpi=figdpi)
plt.plot(x,overlap,marker='.',linestyle='none',label='simulation',color='C1')
def overlap_th(x):
    y=np.maximum(x,1.)
    return 1-y**(-2)
y=overlap_th(x)
if (plt.rcParams['text.usetex']):
    plt.plot(x,y,label='$|\mathbf{v}_1^{\scriptscriptstyle T}\mathbf{u}|^2=\max(1-a^{-2},0)$',color='C0')
    plt.ylabel('$|\mathbf{v}_1^{\scriptscriptstyle T}\mathbf{u}|^2$')
else:
    plt.plot(x,y,label='$|\mathbf{v}_1^{T}\mathbf{u}|^2=\max(1-a^{-2},0)$',color='C0')
    plt.ylabel('$|\mathbf{v}_1^{T}\mathbf{u}|^2$')
plt.legend()
plt.xlabel('$a$')
plt.xlim(0,4)
plt.ylim(0,1)
plt.tight_layout(pad=0.1)
#plt.savefig('fig14_5.pdf',format='pdf')
plt.show()
In [27]:
plt.figure(figsize=(figwidth,figheight),dpi=figdpi)
x=arange(0.25,3,.05)
y=x+1./x
plt.plot(x,y,label='$g+1/g$')
x=[1]
y=[2]
plt.plot(x,y,marker='.',linestyle='none',label='$g_{+}$')
plt.legend()
plt.ylabel('$\mathfrak{z}(g)$')
plt.xlabel('$g$')
plt.tight_layout(pad=0.1)
#plt.savefig('fig14_4.pdf',format='pdf')
plt.show()
In [28]:
import os
if(os.path.isfile("tracy_5.1000")):
    plt.figure(figsize=(figwidth,figheight),dpi=figdpi)
    t1=TW.TracyWidom(1)
    x=arange(-15,15,.01)
    y=t1.pdf(x)*100
    x=2+x*.01
    data=np.loadtxt("tracy_5.1000")
    plt.hist(data,200,density=True,color=HistoColor,label='$N=1000$') 
    plt.plot(x,y,color='C0',label='Tracy-Widom')
    plt.plot(x,y)
    plt.legend()
    plt.yscale('log')
    plt.xlim(1.5,6)
    plt.ylim(1e-4,40)
    plt.xlabel('$\lambda_{\max}$')
    plt.ylabel('$P(\lambda_{\max})$')
    plt.tight_layout(pad=0.1)
    #plt.savefig('fig14_6.pdf',format='pdf')
    plt.show()
else:
    print("This figure needs a data file not provided with this notebook")
In [29]:
random.seed(myseed)
dataq=np.array([])
datavect=np.array([])
datavect2=np.array([])
for q in arange(.01,2,.025):
    T=int(floor(sqrt(1e6/q)))
    N=int(floor(q*T))
    aaa=np.random.randn(N, T)
    wei=np.heaviside(abs(aaa[0])-1,1)
    mat=wei*aaa
    bigm=aaa@mat.transpose()/T
    val,vect=npl.eigh(bigm,eigvals_only=False,eigvals=[N-1,N-1])#computes the largest eigenvalue (with vector) of bigm
    dataq=np.append(dataq,N/(float(T)))
    datavect=np.append(datavect,vect[0])
    wei=1-1./pow(aaa[0],2)
    mat=wei*aaa
    bigm=aaa@mat.transpose()/T
    val,vect=npl.eigh(bigm,eigvals_only=False,eigvals=[N-1,N-1])#computes the largest eigenvalue (with vector) of bigm
    datavect2=np.append(datavect2,vect[0])
plt.figure(figsize=(figwidth,figheight),dpi=figdpi)
p=special.erfc(1/sqrt(2))
c1=exp(-1/2)*sqrt(2/np.pi)+p
myx=arange(0,1,.01)
plt.plot(dataq,datavect2*datavect2,marker='s',markersize=2,linestyle='none',label='$f(y)=1-1/y$',color='C1')
plt.plot(dataq,datavect*datavect,marker='.',linestyle='none',label='$f(y)=\Theta(y-1)$',color='C0')
plt.plot(myx,1-c1/pow(c1-p,2)*myx,label='linear approx.',color='C0')
plt.ylim(0,1.1)
plt.xlim(0,2)
plt.legend()
plt.xlabel(r'$q$')
plt.ylabel(r'$\varrho$')
plt.tight_layout(pad=0.1)
#plt.savefig('fig14_7.pdf',format='pdf')
plt.show()