Fixed Points of the Ricci Flow

Finding Einstein Metrics That “Optimize” Ricci Curvature

In Riemannian geometry, one of the most profound questions is: what is the “best” metric on a manifold? The Ricci flow, introduced by Richard Hamilton in 1982, answers this by evolving a metric toward a canonical shape. Its fixed points — metrics that don’t change under the flow — are called Einstein metrics, and they represent a kind of geometric optimum.


The Big Picture: What Is the Ricci Flow?

The Ricci flow is a geometric PDE that deforms a Riemannian metric $g_{ij}$ in the direction of its Ricci curvature:

$$\frac{\partial g_{ij}}{\partial t} = -2 R_{ij}$$

where $R_{ij}$ is the Ricci curvature tensor. Think of it as a heat equation for geometry — it smooths out irregularities in curvature over time.


Fixed Points: Einstein Metrics

A metric is a fixed point of the Ricci flow (up to rescaling) when:

$$R_{ij} = \lambda g_{ij}$$

for some constant $\lambda \in \mathbb{R}$. This is the Einstein condition. Such a metric is called an Einstein metric, and the constant $\lambda$ is called the Einstein constant.

Taking the trace:

$$R = \lambda n$$

where $R$ is the scalar curvature and $n = \dim(M)$.

The three cases are:

  • $\lambda > 0$: positive Einstein metric (e.g., round sphere $S^n$)
  • $\lambda = 0$: Ricci-flat metric (e.g., flat torus $T^n$, Calabi-Yau)
  • $\lambda < 0$: negative Einstein metric (e.g., hyperbolic space $\mathbb{H}^n$)

Concrete Example: The Round 2-Sphere $S^2$

The simplest and most instructive example is $S^2$ with the round metric in spherical coordinates $(\theta, \phi)$:

$$g = R^2 \begin{pmatrix} 1 & 0 \ 0 \sin^2\theta \end{pmatrix}$$

The Ricci tensor of the round sphere of radius $R$ is:

$$R_{ij} = \frac{1}{R^2} g_{ij}$$

This is exactly the Einstein condition with $\lambda = \frac{1}{R^2} > 0$.

Under the Ricci flow:

$$\frac{\partial g}{\partial t} = -2R_{ij} = -\frac{2}{R^2} g_{ij}$$

The sphere shrinks homothetically — it stays round but its radius changes. Setting $g(t) = r(t)^2 g_0$ where $g_0$ is the unit sphere metric:

$$2r \dot{r} g_0 = -\frac{2}{r^2} r^2 g_0 = -2 g_0$$

$$\Rightarrow r\dot{r} = -1 \Rightarrow r(t) = \sqrt{R^2 - 2t}$$

The sphere collapses to a point at $t^* = R^2/2$.


Python Implementation

We simulate and visualize the Ricci flow on $S^2$ numerically. We track the metric coefficient evolution, Ricci curvature, the Einstein condition residual, and the geometry in 3D.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
from matplotlib.gridspec import GridSpec
from mpl_toolkits.mplot3d import Axes3D

# ─────────────────────────────────────────────
# 1. ANALYTICAL RICCI FLOW ON S^2
# r(t) = sqrt(R0^2 - 2t), collapse at t* = R0^2/2
# ─────────────────────────────────────────────

R0 = 2.0 # initial radius
t_max = R0**2 / 2 # collapse time
N_t = 500 # time steps
t = np.linspace(0, t_max * 0.99, N_t)

r_t = np.sqrt(R0**2 - 2 * t) # radius under Ricci flow
vol_t = 4 * np.pi * r_t**2 # area of S^2
kappa_t = 1.0 / r_t**2 # sectional / Gaussian curvature = 1/r^2
scalar_R = 2.0 / r_t**2 # scalar curvature of S^2 = 2/r^2
lambda_t = 1.0 / r_t**2 # Einstein constant = 1/r^2 (R_ij = lambda * g_ij)

# Einstein condition residual: ||R_ij - lambda*g_ij|| / ||g_ij||
# For S^2 this is identically 0; we verify numerically.
# We perturb r slightly to model near-Einstein metrics.
epsilon = 0.05
r_perturb = r_t * (1 + epsilon * np.sin(8 * np.pi * t / t_max) * np.exp(-t))
lambda_p = 1.0 / r_perturb**2
# Metric tensor components (diagonal): g_theta = r^2, g_phi = r^2 sin^2(theta)
# Einstein residual (relative, averaged over the manifold):
residual = np.abs(lambda_p - lambda_t) / lambda_t # relative deviation

# ─────────────────────────────────────────────
# 2. RICCI FLOW ON A FAMILY OF ELLIPSOIDS → CONVERGENCE TO ROUND SPHERE
# Parametrize ellipsoid with semi-axes (a(t), a(t), c(t))
# ODE from symmetry reduction of Ricci flow on S^2 (Berger sphere analogy):
# da/dt = -1/c^2 (approx for rotationally symmetric metric)
# dc/dt = -1/a^2
# ─────────────────────────────────────────────

def ricci_flow_ellipsoid(t_span, y0, n_steps=4000):
"""Numerical Ricci flow for rotationally symmetric metric on S^2.
State y = [a, c] where a=equatorial radius, c=polar radius."""
dt = (t_span[1] - t_span[0]) / n_steps
y = np.array(y0, dtype=float)
ys = [y.copy()]
ts = [t_span[0]]
cur_t = t_span[0]

for _ in range(n_steps):
a, c = y
if a <= 1e-6 or c <= 1e-6:
break
# Ricci flow equations for rotationally symmetric metric on S^2
# (from Brendle's notes / Hamilton 1982, symmetry reduction):
# d(a^2)/dt = -2 R_aa, d(c^2)/dt = -2 R_cc
# For the metric ds^2 = a^2(dphi^2 + sin^2(phi) dtheta^2) ... simplified:
da_dt = -(1.0 / c**2)
dc_dt = -(1.0 / a**2)
y = y + dt * np.array([da_dt, dc_dt])
y = np.maximum(y, 1e-6)
cur_t += dt
ys.append(y.copy())
ts.append(cur_t)

return np.array(ts), np.array(ys)

# Three initial ellipsoid configurations
configs = [
([3.0, 1.0], 'Prolate (a<c)', '#e74c3c'),
([1.0, 1.0], 'Round sphere', '#2ecc71'),
([1.0, 3.0], 'Oblate (a>c)', '#3498db'),
]

t_end = 2.5
results = []
for y0_vals, label, color in configs:
ts_i, ys_i = ricci_flow_ellipsoid([0, t_end], y0_vals, n_steps=8000)
results.append((ts_i, ys_i, label, color))

# ─────────────────────────────────────────────
# 3. SPHERE SURFACE FOR 3D VISUALIZATION
# ─────────────────────────────────────────────

def sphere_surface(r, n=60):
u = np.linspace(0, np.pi, n)
v = np.linspace(0, 2 * np.pi, n)
U, V = np.meshgrid(u, v)
X = r * np.sin(U) * np.cos(V)
Y = r * np.sin(U) * np.sin(V)
Z = r * np.cos(U)
return X, Y, Z

def ellipsoid_surface(a, c, n=60):
u = np.linspace(0, np.pi, n)
v = np.linspace(0, 2 * np.pi, n)
U, V = np.meshgrid(u, v)
X = a * np.sin(U) * np.cos(V)
Y = a * np.sin(U) * np.sin(V)
Z = c * np.cos(U)
return X, Y, Z

# ─────────────────────────────────────────────
# 4. PLOTTING
# ─────────────────────────────────────────────

plt.style.use('dark_background')
fig = plt.figure(figsize=(20, 22))
fig.patch.set_facecolor('#0d1117')
gs = GridSpec(4, 3, figure=fig,
hspace=0.45, wspace=0.35,
left=0.06, right=0.97,
top=0.93, bottom=0.04)

clr_gold = '#f0c040'
clr_cyan = '#00e5ff'
clr_pink = '#ff4081'
clr_green = '#69ff47'
bg_ax = '#161b22'

def style_ax(ax, title, xlabel, ylabel):
ax.set_facecolor(bg_ax)
ax.set_title(title, color=clr_gold, fontsize=11, pad=8, fontweight='bold')
ax.set_xlabel(xlabel, color='#aaaaaa', fontsize=9)
ax.set_ylabel(ylabel, color='#aaaaaa', fontsize=9)
ax.tick_params(colors='#777777', labelsize=8)
for spine in ax.spines.values():
spine.set_edgecolor('#333333')
ax.grid(color='#222222', linewidth=0.5, linestyle='--')

# ── Panel 1: Radius r(t) under Ricci flow ──
ax1 = fig.add_subplot(gs[0, 0])
style_ax(ax1, 'Radius $r(t)$ Under Ricci Flow on $S^2$', 'Time $t$', '$r(t)$')
ax1.plot(t, r_t, color=clr_cyan, lw=2, label=r'$r(t)=\sqrt{R_0^2-2t}$')
ax1.axvline(t_max, color=clr_pink, lw=1, ls='--', label=f'Collapse $t^*={t_max:.2f}$')
ax1.legend(fontsize=8, facecolor='#1e2530', edgecolor='#444')
ax1.set_xlim(0, t_max)

# ── Panel 2: Scalar curvature R(t) ──
ax2 = fig.add_subplot(gs[0, 1])
style_ax(ax2, 'Scalar Curvature $R(t) = 2/r(t)^2$', 'Time $t$', '$R(t)$')
ax2.plot(t, scalar_R, color=clr_pink, lw=2, label=r'$R(t)=2/r(t)^2$')
ax2.legend(fontsize=8, facecolor='#1e2530', edgecolor='#444')
ax2.set_xlim(0, t_max)

# ── Panel 3: Area (volume) ──
ax3 = fig.add_subplot(gs[0, 2])
style_ax(ax3, 'Area of $S^2$: $A(t) = 4\pi r(t)^2$', 'Time $t$', '$A(t)$')
ax3.plot(t, vol_t, color=clr_green, lw=2, label=r'$A(t)=4\pi r^2$')
ax3.axhline(0, color='#555', lw=0.8, ls=':')
ax3.legend(fontsize=8, facecolor='#1e2530', edgecolor='#444')
ax3.set_xlim(0, t_max)

# ── Panel 4: Einstein constant λ(t) ──
ax4 = fig.add_subplot(gs[1, 0])
style_ax(ax4, r'Einstein Constant $\lambda(t)=1/r(t)^2$', 'Time $t$', r'$\lambda(t)$')
ax4.plot(t, lambda_t, color=clr_gold, lw=2, label=r'$\lambda=1/r^2$')
ax4.legend(fontsize=8, facecolor='#1e2530', edgecolor='#444')
ax4.set_xlim(0, t_max)

# ── Panel 5: Einstein residual for perturbed metric ──
ax5 = fig.add_subplot(gs[1, 1])
style_ax(ax5, 'Einstein Residual (Perturbed Metric)', 'Time $t$',
r'$|\lambda_\epsilon - \lambda|/\lambda$')
ax5.plot(t, residual, color=clr_pink, lw=1.5, alpha=0.9,
label=r'$\epsilon\sin$-perturbation')
ax5.fill_between(t, 0, residual, color=clr_pink, alpha=0.15)
ax5.legend(fontsize=8, facecolor='#1e2530', edgecolor='#444')
ax5.set_xlim(0, t_max)

# ── Panel 6: Ellipsoid axes under Ricci flow ──
ax6 = fig.add_subplot(gs[1, 2])
style_ax(ax6, 'Ellipsoid Axes $a(t), c(t)$ Under Ricci Flow',
'Time $t$', 'Axis length')
for ts_i, ys_i, label, color in results:
ax6.plot(ts_i, ys_i[:, 0], color=color, lw=1.8, label=f'{label} $a$')
ax6.plot(ts_i, ys_i[:, 1], color=color, lw=1.8, ls='--')
ax6.legend(fontsize=7, facecolor='#1e2530', edgecolor='#444')
ax6.set_xlim(0, t_end)

# ── Panel 7: Ratio a/c → 1 (convergence to round sphere) ──
ax7 = fig.add_subplot(gs[2, 0])
style_ax(ax7, 'Isotropy Ratio $a(t)/c(t)$ → 1\n(Convergence to Einstein Metric)',
'Time $t$', '$a/c$')
for ts_i, ys_i, label, color in results:
ratio = ys_i[:, 0] / (ys_i[:, 1] + 1e-12)
ax7.plot(ts_i, ratio, color=color, lw=1.8, label=label)
ax7.axhline(1.0, color=clr_gold, lw=1.2, ls='--', label='Einstein: $a=c$')
ax7.legend(fontsize=7, facecolor='#1e2530', edgecolor='#444')
ax7.set_xlim(0, t_end)
ax7.set_ylim(0, 4)

# ── Panel 8: Phase portrait (a vs c) ──
ax8 = fig.add_subplot(gs[2, 1])
style_ax(ax8, 'Phase Portrait $(a, c)$ of Ricci Flow', '$a(t)$', '$c(t)$')
for ts_i, ys_i, label, color in results:
ax8.plot(ys_i[:, 0], ys_i[:, 1], color=color, lw=1.8, label=label)
ax8.plot(ys_i[0, 0], ys_i[0, 1], 'o', color=color, ms=6)
diag = np.linspace(0, 3.5, 100)
ax8.plot(diag, diag, color=clr_gold, lw=1, ls='--', label='Einstein locus $a=c$')
ax8.legend(fontsize=7, facecolor='#1e2530', edgecolor='#444')
ax8.set_xlim(0, 3.5); ax8.set_ylim(0, 3.5)

# ── Panel 9: Scalar curvature of ellipsoid ──
ax9 = fig.add_subplot(gs[2, 2])
style_ax(ax9, 'Mean Scalar Curvature Along Ricci Flow',
'Time $t$', r'$\bar{R}(t)$')
for ts_i, ys_i, label, color in results:
a_arr, c_arr = ys_i[:, 0], ys_i[:, 1]
# Approximate mean scalar curvature for rotationally symmetric S^2:
R_mean = 1.0/c_arr**2 + 1.0/a_arr**2
ax9.plot(ts_i, R_mean, color=color, lw=1.8, label=label)
ax9.legend(fontsize=7, facecolor='#1e2530', edgecolor='#444')
ax9.set_xlim(0, t_end); ax9.set_ylim(0, 12)

# ── Panel 10 / 11 / 12: 3D ellipsoids at t=0, mid, final ──
snap_fracs = [0.0, 0.4, 0.85]
snap_titles = ['$t=0$ (Initial)', '$t=0.4\\,t_{end}$', '$t=0.85\\,t_{end}$']

ts0, ys0 = results[0][0], results[0][1] # prolate ellipsoid

for col_idx, (frac, title) in enumerate(zip(snap_fracs, snap_titles)):
ax3d = fig.add_subplot(gs[3, col_idx], projection='3d')
ax3d.set_facecolor(bg_ax)
ax3d.set_title(f'Prolate Ellipsoid\n{title}',
color=clr_gold, fontsize=10, pad=4, fontweight='bold')

idx = int(frac * (len(ts0) - 1))
a_v = ys0[idx, 0]
c_v = ys0[idx, 1]
X, Y, Z = ellipsoid_surface(a_v, c_v)

surf = ax3d.plot_surface(X, Y, Z, cmap='plasma',
alpha=0.85, linewidth=0, antialiased=True)
ax3d.set_xlim(-3.5, 3.5); ax3d.set_ylim(-3.5, 3.5); ax3d.set_zlim(-3.5, 3.5)
ax3d.set_xlabel('X', color='#888', fontsize=7, labelpad=2)
ax3d.set_ylabel('Y', color='#888', fontsize=7, labelpad=2)
ax3d.set_zlabel('Z', color='#888', fontsize=7, labelpad=2)
ax3d.tick_params(colors='#555', labelsize=6)
ax3d.xaxis.pane.fill = False
ax3d.yaxis.pane.fill = False
ax3d.zaxis.pane.fill = False
ax3d.xaxis.pane.set_edgecolor('#222')
ax3d.yaxis.pane.set_edgecolor('#222')
ax3d.zaxis.pane.set_edgecolor('#222')
ratio_str = f'$a={a_v:.2f},\\ c={c_v:.2f}$'
ax3d.text2D(0.5, -0.05, ratio_str, transform=ax3d.transAxes,
ha='center', color='#aaaaaa', fontsize=8)

fig.suptitle('Ricci Flow on $S^2$ — Fixed Points & Einstein Metrics',
color=clr_gold, fontsize=16, fontweight='bold', y=0.97)

plt.savefig('ricci_flow_einstein.png',
dpi=150, bbox_inches='tight', facecolor=fig.get_facecolor())
plt.show()
print("Figure saved.")

Code Walkthrough

Section 1 — Analytical Ricci Flow on $S^2$

The round sphere is an exact Einstein metric. We use the closed-form solution:

$$r(t) = \sqrt{R_0^2 - 2t}, \quad t \in \left[0,, \frac{R_0^2}{2}\right)$$

From this we derive:

Quantity Formula
Gaussian curvature $\kappa = 1/r^2$
Scalar curvature $R = 2/r^2$
Einstein constant $\lambda = 1/r^2$
Area $A = 4\pi r^2$

We also introduce a sinusoidal perturbation $r_\epsilon = r(t)(1 + \epsilon \sin(\cdots))$ to simulate a near-Einstein metric and measure how far it deviates from the Einstein condition. The Einstein residual:

$$\text{Res} = \frac{|\lambda_\epsilon - \lambda|}{\lambda}$$

should be small for near-Einstein metrics and goes to zero as the metric relaxes.


Section 2 — Numerical Ricci Flow on Rotationally Symmetric Ellipsoids

For a rotationally symmetric metric on $S^2$ parametrized by equatorial radius $a$ and polar radius $c$, the Ricci flow reduces to the ODE system:

$$\frac{da}{dt} = -\frac{1}{c^2}, \qquad \frac{dc}{dt} = -\frac{1}{a^2}$$

We integrate this with a simple explicit Euler method over 8000 steps (sufficient for stability on this timescale). We track three initial conditions:

  • Prolate ($a = 1, c = 3$): elongated along the $z$-axis
  • Round ($a = c = 1$): already at the fixed point
  • Oblate ($a = 3, c = 1$): flattened at the poles

The key observable is the isotropy ratio $a(t)/c(t) \to 1$, which signals convergence to the Einstein metric.


Section 3 — 3D Ellipsoid Visualization

Three snapshots of the prolate ellipsoid at $t = 0$, $t = 0.4,t_\text{end}$, and $t = 0.85,t_\text{end}$ are rendered as 3D surfaces, color-mapped with plasma. You can watch the shape “round out” — the Ricci flow literally sculpts the manifold toward its optimal geometry.


Graph-by-Graph Analysis

Panels 1–3 (top row): Analytical $S^2$ under Ricci flow

  • $r(t)$ decreases monotonically from $R_0 = 2$ to $0$ at $t^* = 2$.
  • $R(t) = 2/r^2$ blows up as $t \to t^*$ — this is the Type I singularity studied by Hamilton and Perelman.
  • The area $A(t) = 4\pi r^2$ contracts linearly in $r^2$, reaching zero at collapse.

Panels 4–5 (middle-left): Einstein constant and residual

  • $\lambda(t) = 1/r^2$ grows monotonically, confirming the metric remains Einstein (just rescaled) throughout the flow.
  • The residual plot shows the perturbed metric deviating from the Einstein condition — oscillating at early times, then growing as the sphere shrinks. A true Einstein metric would give a flat zero line.

Panels 6–7 (middle-center/right): Ellipsoid axis evolution

  • All three initial configurations converge: $a(t)$ and $c(t)$ approach each other before collapse.
  • The isotropy ratio $a/c \to 1$ is the smoking gun — the Ricci flow is driving the geometry toward the Einstein fixed point.

Panel 8: Phase portrait

  • The diagonal $a = c$ is the Einstein locus (fixed points up to rescaling).
  • All trajectories flow toward this diagonal and then collapse along it — precisely the behavior predicted by Hamilton’s theorem.

Panel 9: Scalar curvature

  • $\bar{R}(t) = 1/c^2 + 1/a^2$ increases and converges for all initial data, consistent with the curvature blow-up at the Einstein fixed point (which is also a finite-time singularity for the normalized flow).

Panels 10–12 (bottom row): 3D geometry

  • The prolate ellipsoid ($a=1, c=3$) visibly rounds out across the three snapshots.
  • By $t = 0.85,t_\text{end}$, $a \approx c$: the shape is nearly a round sphere — the Einstein metric.

Figure saved.

Summary

Concept Mathematical Statement
Ricci flow $\partial_t g_{ij} = -2R_{ij}$
Einstein condition (fixed point) $R_{ij} = \lambda g_{ij}$
Round sphere (positive Einstein) $\lambda = 1/r^2 > 0$
Collapse time $t^* = r_0^2/2$
Convergence criterion $a(t)/c(t) \to 1$

The Ricci flow is a gradient flow for the normalized Einstein–Hilbert functional, and its fixed points are exactly the Einstein metrics. On $S^2$, the unique (up to scaling) Einstein metric is the round sphere — and the Ricci flow finds it, even from a lumpy or anisotropic start. This is the geometric heart of Hamilton’s theorem on surfaces, and a precursor to Perelman’s proof of the Geometrization Conjecture.