# SCRIPT: BH-FIGS-05 # Figures for the black-hole paper. Every plotted number is computed here from the same formulas as # BH-CORE-02 / BH-WALL-03 / BH-FOLD-PARALLEL-01. Dark background, no white panels. import os as _os _HERE=_os.path.dirname(_os.path.abspath(__file__)) _FIG=_os.path.join(_HERE,'..','paper','fig') if _os.path.isdir(_os.path.join(_HERE,'..','paper','fig')) else _os.path.join(_HERE,'..','fig') import numpy as np, matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt from matplotlib.patches import FancyArrowPatch, Circle, Polygon import os OUT = _FIG; os.makedirs(OUT, exist_ok=True) BG='#0f1115'; INK='#e6e8ec'; MUT='#9aa3ae'; RULE='#2a313c' C1='#3987e5'; C2='#d95926'; C3='#199e70'; C4='#c98500'; C5='#d55181' # validated dark categorical order plt.rcParams.update({'figure.facecolor':BG,'axes.facecolor':BG,'savefig.facecolor':BG,'text.color':INK, 'axes.labelcolor':INK,'xtick.color':MUT,'ytick.color':MUT,'axes.edgecolor':RULE,'font.family':'serif', 'font.serif':['TeX Gyre Pagella','URW Palladio L','Palatino','DejaVu Serif'],'mathtext.fontset':'cm','font.size':11}) def clean(ax): for s in ['top','right']: ax.spines[s].set_visible(False) def save(fig, name): fig.savefig(f'{OUT}/{name}.png', dpi=260, bbox_inches='tight', pad_inches=0.12); plt.close(fig) # ---------- operator (same closed forms as BH-CORE-02) ---------- def blocks(t, w): t1,t2,t3 = t; w1,w2,w3 = w A11=(t1+t2)-(t1-t2)*(w1-w2); A22=2*t3+w3*(t1+t2-2*t3) A12=((t1-t2)-(t1+t2-2*t3)*(w1-w2))/np.sqrt(3); A21=np.sqrt(3)*(t1-t2)*w3 return np.array([[A11,A12],[A21,A22]],dtype=complex) def full5(t, w): B = np.zeros((5,5),dtype=complex); B[:2,:2]=blocks(t,w) B[2,2]=t[0]+t[1]; B[3,3]=t[0]+t[2]; B[4,4]=t[1]+t[2]; return B t=(2.0,1.0,1.3); dl=0.4 def wz(z, d=dl): return ((1-z+d)/2,(1-z-d)/2,z) # ---------- F1 the fold ---------- fig = plt.figure(figsize=(9.2,5.2)) ax = fig.add_axes([0.06,0.56,0.9,0.36]); bx = fig.add_axes([0.06,0.08,0.9,0.36]) s = np.linspace(-0.55,0.55,400) for a in (ax,bx): clean(a) ax.plot(s, 0*s, color=RULE, lw=1) tt = np.linspace(-1,1,400); sig = 0.5*tt ax.plot(sig, 0.18*np.ones_like(sig), color=C1, lw=2.4) for x0 in [-0.4,-0.1,0.2]: ax.add_patch(FancyArrowPatch((x0,0.18),(x0+0.08,0.18),arrowstyle='-|>',mutation_scale=14,color=C1)) ax.axvline(0,color=C3,lw=1.2,ls=(0,(4,3))) ax.set_xlim(-0.6,0.6); ax.set_ylim(-0.25,0.42); ax.set_yticks([]) ax.set_xlabel(r'signed coordinate $\sigma_3=\pm\sqrt{w_3}$ (sign = orientation, $\mathrm{sign}\,\det X$)') ax.text(-0.58,0.36,r'metric $d\sigma_3^2$: smooth, even under $\sigma_3\to-\sigma_3$',color=INK,fontsize=11) ax.text(0.015,-0.18,'coplanar\n(det X = 0)',color=C3,fontsize=9.5) def tetra(axh, cx, cy, sgn, sc=0.07, col=C2): P = np.array([[-1,-0.6],[1,-0.6],[0,0.9],[0.35*sgn,-0.05]])*sc + [cx,cy] for i in range(4): for j in range(i+1,4): axh.plot([P[i,0],P[j,0]],[P[i,1],P[j,1]],color=col,lw=1.1,alpha=0.9) axh.plot(P[3,0],P[3,1],'o',ms=3.2,color=C4) tetra(ax,-0.42,-0.05,-1); tetra(ax,0.42,-0.05,1) ax.text(-0.52,-0.21,'mirror image',color=MUT,fontsize=9); ax.text(0.37,-0.21,'tetrahedron',color=MUT,fontsize=9) w3 = sig**2 bx.plot(tt, w3, color=C1, lw=2.4); bx.axhline(0,color=C3,lw=1.2,ls=(0,(4,3))) bx.set_xlabel('time along the same path'); bx.set_ylabel(r'$w_3=\sigma_3^2$') bx.set_xlim(-1,1); bx.set_ylim(-0.02,0.3) bx.text(-0.98,0.255,r'Gram observer: $ds^2=dw_3^2/(4w_3)$ — the path seems to bounce off the wall',color=INK,fontsize=11) bx.text(-0.97,0.012,r'distance to the wall $=\arcsin\sqrt{\varepsilon}$, finite',color=C3,fontsize=10) save(fig,'f1_fold') # ---------- F2 one-way coupling at the wall ---------- fig = plt.figure(figsize=(9.2,4.2)) ax = fig.add_axes([0.02,0.05,0.42,0.9]); ax.axis('off'); ax.set_xlim(0,1); ax.set_ylim(0,1) for (x,y,lab,sub) in [(0.2,0.5,r'$E_1$','in the wall\n($w_1-w_2$)'),(0.8,0.5,r'$E_2$','across the wall\n($w_3$)')]: ax.add_patch(Circle((x,y),0.11,fc='#161a21',ec=C1,lw=2)); ax.text(x,y,lab,ha='center',va='center',fontsize=17) ax.text(x,y-0.24,sub,ha='center',va='top',color=MUT,fontsize=9.5) ax.add_patch(FancyArrowPatch((0.69,0.58),(0.31,0.58),connectionstyle='arc3,rad=0.35',arrowstyle='-|>',mutation_scale=18,color=C2,lw=2.6)) ax.text(0.5,0.86,r'$A_{12}\neq0$ survives',ha='center',color=C2,fontsize=12) ax.add_patch(FancyArrowPatch((0.31,0.42),(0.69,0.42),connectionstyle='arc3,rad=0.35',arrowstyle='-|>',mutation_scale=18,color=C4,lw=1.2,ls=(0,(3,3)))) ax.text(0.5,0.12,r'$A_{21}=\sqrt{3}\,(t_1-t_2)\,\sigma_3^2\to0$',ha='center',color=C4,fontsize=12) bx = fig.add_axes([0.53,0.17,0.44,0.72]); clean(bx) sg = np.linspace(0,0.7,300) a12 = [abs(blocks(t,wz(x*x))[0,1]) for x in sg]; a21=[abs(blocks(t,wz(x*x))[1,0]) for x in sg] bx.plot(sg,a12,color=C2,lw=2.2,label=r'$|A_{12}|$ (across $\to$ in)'); bx.plot(sg,a21,color=C4,lw=2.2,label=r'$|A_{21}|$ (in $\to$ across)') bx.set_xlabel(r'$\sigma_3$ (distance from the wall)'); bx.legend(frameon=False,fontsize=9.5,loc='upper left') bx.set_title(r'$t=(2,1,1.3)$, $w_1-w_2=0.4$',color=MUT,fontsize=10) save(fig,'f2_oneway') # ---------- F3 braid ---------- def track(fun, n=3000, loops=1): th=np.linspace(0,2*np.pi*loops,n*loops+1); cur=np.linalg.eigvals(fun(th[0])); path=[cur.copy()] for x in th[1:]: e=np.linalg.eigvals(fun(x)) if abs(e[0]-cur[0])+abs(e[1]-cur[1])>abs(e[1]-cur[0])+abs(e[0]-cur[1]): e=e[::-1] cur=e; path.append(cur.copy()) return np.array(path) r=1e-3 pw=lambda th: blocks(t,wz(r*np.exp(1j*th))); ps=lambda th: blocks(t,wz((np.sqrt(r)*np.exp(1j*th))**2/1.0)) fig,(ax,bx)=plt.subplots(1,2,figsize=(9.2,4.2)) for a,(fun,title) in zip((ax,bx),[(pw,r'one loop of $w_3$ around the wall point'),(lambda th: blocks(t,wz((np.sqrt(r)*np.exp(1j*th))**2)),r'one loop of $\sigma_3$ (two of $w_3$)')]): P=track(fun); clean(a) for k,col in enumerate([C1,C3]): a.plot(P[:,k].real-2.6,P[:,k].imag,color=col,lw=(4.5 if k==0 else 1.8)) a.plot(P[0,k].real-2.6,P[0,k].imag,'o',color=col,ms=7); a.plot(P[-1,k].real-2.6,P[-1,k].imag,'s',color=col,ms=9,mfc='none',mew=1.6) m=len(P)//4; a.add_patch(FancyArrowPatch((P[m,k].real-2.6,P[m,k].imag),(P[m+40,k].real-2.6,P[m+40,k].imag),arrowstyle='-|>',mutation_scale=13,color=col)) a.set_title(title,fontsize=11); a.set_xlabel(r'Re $(\lambda-\lambda_*)$'); a.set_aspect('equal','datalim') ax.set_ylabel(r'Im $\lambda$') ax.text(0.02,0.02,'start (dot), end (square): the pair has exchanged',transform=ax.transAxes,color=MUT,fontsize=9) bx.text(0.02,0.02,'both run the full circle; each comes home',transform=bx.transAxes,color=MUT,fontsize=9) save(fig,'f3_braid') # ---------- F4 the record outlives the metric ---------- def riesz(A, c, rad, n=1500): zs=c+rad*np.exp(1j*np.linspace(0,2*np.pi,n,endpoint=False)); P=np.zeros_like(A) for z in zs: P+=np.linalg.inv(z*np.eye(A.shape[0])-A)*(z-c) return P/n def gkin(w): e1=np.array([1,-1,0])/np.sqrt(2); e2=np.array([1,1,-2])/np.sqrt(6); wv=np.array(w) f=lambda a,b: np.sum(a*b/(4*wv)); return np.array([[f(e1,e1),f(e1,e2)],[f(e2,e1),f(e2,e2)]]) sg=np.logspace(-4,-1,40); nP=[]; trP=[]; led=[]; gp=[]; nK=[] for x in sg: A=full5(t,wz(x*x)); B=A[:2,:2]; ev=np.linalg.eigvals(B); o=np.argsort(ev.real); ev=ev[o] g=abs(ev[1]-ev[0]); gp.append(g) Pm=riesz(B,ev[0],g/2); nP.append(np.linalg.norm(Pm,2)) Lc=np.linalg.cholesky(gkin(wz(x*x))); nK.append(np.linalg.norm(Lc.T@Pm@np.linalg.inv(Lc.T),2)); trP.append(np.trace(riesz(A,2.6,0.25)).real) led.append(np.log(np.linalg.det(np.eye(2)+B)).real) fig,ax=plt.subplots(figsize=(9.2,4.6)); clean(ax) ax.loglog(sg,nP,color=C4,lw=2.3,label=r'one eigenprojector, Gram-frame norm ($\propto 1/\sigma_3$)') ax.loglog(sg,nK,color=C5,lw=2.3,ls=(0,(1,2)),label=r'the same projector, kinetic norm $=1$') ax.loglog(sg,gp,color=C1,lw=2.3,label=r'gap $|\lambda_+-\lambda_-|=4|t_1-t_2|\sqrt{w_1w_2}\,\sigma_3$') ax.loglog(sg,trP,color=C2,lw=2.6,label=r'pair projector $\mathrm{Tr}\,P_{\rm pair}=2$') ax.loglog(sg,np.exp(np.array(led))/5,color=C3,lw=2.0,ls=(0,(5,3)),label=r'ledger $\det(1+A_{\rm pair})$ (scaled $\times\frac{1}{5}$)') ax.set_xlabel(r'$\sigma_3$ (distance to the wall, at the joint point)'); ax.legend(frameon=False,fontsize=9.5,loc='upper right') ax.set_ylim(1e-4,2e4) save(fig,'f4_record') # ---------- F5 shared fold normal form ---------- X=np.linspace(1e-4,0.6,400) fig,(ax,bx)=plt.subplots(1,2,figsize=(9.2,4.0)) for a in (ax,bx): clean(a) ax.plot(X,1/(4*(1-X)**4),color=C1,lw=2.2,label=r'Schwarzschild base, $\kappa^2 N\,h_{NN}$') m=1.0; r4=np.sqrt(2*m/(1-X)); dr=np.gradient(r4,X); ax.plot(X,(1/(2*m))*X*dr**2/X,color=C5,lw=2,ls=(0,(5,2)),label=r'Tangherlini (4D), $\kappa^2N\,h_{NN}$') ax.plot(X,1/(4*(1-X)),color=C2,lw=2.2,label=r'Bures wall path, $w\,g_{ww}$') ax.axhline(0.25,color=C3,lw=1,ls=':'); ax.text(0.33,0.12,r'$\frac{1}{4}$ at the locus',color=C3) ax.set_xlabel(r'even observable $X$ ($N$ or $w$)'); ax.set_ylim(0,1.6); ax.legend(frameon=False,fontsize=9,loc='upper left') L=np.linspace(-0.75,0.75,400) bx.plot(L,1/(1-L**2)**4,color=C1,lw=2.2,label=r'$\kappa^2 h_{\lambda\lambda}=(1-\lambda^2)^{-4}$') bx.plot(L,1/(1-L**2),color=C2,lw=2.2,label=r'$g_{\sigma\sigma}=(1-\sigma^2)^{-1}$') bx.set_xlabel(r'signed coordinate $\lambda=\sqrt{N}$ or $\sigma=\sqrt{w}$'); bx.set_ylim(0,4); bx.legend(frameon=False,fontsize=9.5,loc='upper center') save(fig,'f5_normalform') # ---------- F6 Schur-Cayley boundary ---------- fig=plt.figure(figsize=(9.2,3.9)); ax=fig.add_axes([0,0,1,1]); ax.axis('off'); ax.set_xlim(0,10); ax.set_ylim(0,4.2) ax.add_patch(Polygon([[0.4,1.0],[4.6,1.0],[4.6,3.7],[0.4,3.7]],closed=True,fc='#161a21',ec=RULE,lw=1.5)) ax.text(2.5,3.25,r'interior $L_{ii}$',ha='center',fontsize=13); ax.text(2.5,2.55,r'$\mathrm{Herm}\,L\succeq0$ (passive)',ha='center',color=MUT,fontsize=11) ax.text(2.5,1.6,r'$Z_b=L_{bb}-L_{bi}L_{ii}^{-1}L_{ib}=W^{*}LW$',ha='center',color=C1,fontsize=12) ax.plot([4.6,4.6],[1.0,3.7],color=C3,lw=3) ax.text(4.75,3.85,'boundary',color=C3,fontsize=10) ax.add_patch(FancyArrowPatch((8.8,2.75),(4.85,2.75),arrowstyle='-|>',mutation_scale=18,color=C2,lw=2.2)); ax.text(6.8,2.95,'incoming',color=C2,ha='center') ax.add_patch(FancyArrowPatch((4.85,1.75),(8.8,1.75),arrowstyle='-|>',mutation_scale=18,color=C4,lw=1.2,ls=(0,(3,3)))); ax.text(6.8,1.35,r'returned: $S_b=(Z_b-I)(Z_b+I)^{-1}$',color=C4,ha='center') ax.text(5.0,0.3,r'$I-S_b^*S_b=4(Z_b+I)^{-*}\,\mathrm{Herm}\,Z_b\,(Z_b+I)^{-1}$; matched load $Z_b=I\Rightarrow S_b=0$',ha='center',fontsize=11) save(fig,'f6_boundary') # ---------- F7 local-symbol horizon control ---------- M=1.0; rr=np.linspace(2.0001,8,400); N=1-2*M/rr; k,mm=0.7,1.0 fig,ax=plt.subplots(figsize=(9.2,4.0)); clean(ax) ax.plot(rr,N,color=C3,lw=2,label=r'lapse $N=1-2M/r$') ax.plot(rr,2*N*np.sqrt(k*k+mm*mm),color=C4,lw=2,ls=(0,(5,3)),label=r'coordinate gap $2N\sqrt{k^2+m^2}$') ax.plot(rr,2*np.sqrt(k*k+mm*mm)*np.ones_like(rr),color=C1,lw=2.2,label=r'proper gap $2\sqrt{k^2+m^2}$') ax.plot(rr,4*mm*mm/(4*(k*k+mm*mm)**2)*np.ones_like(rr),color=C2,lw=2.4,label=r'projector metric $g_{kk}$ ($\times4$)') ax.axvline(2*M,color=MUT,lw=1,ls=':'); ax.text(2.05,3.0,'horizon $r=2M$',color=MUT) ax.set_xlabel(r'$r/M$'); ax.legend(frameon=False,fontsize=9.5,loc='upper right',ncol=2); ax.set_ylim(0,3.9) save(fig,'f7_horizon_control') print('figures written to', OUT)