737 words
4 minutes
260222_ChristoffelSymbols_in_Riemannian_Geometry

link#

Christoffel Symbols In Riemannian Geometry#

Image

1️⃣ What Are Christoffel Symbols?#

  • In Riemannian geometry, Christoffel symbols describe how coordinates curve.

  • They are not tensors.

  • They encode how basis vectors change from point to point.

  • They appear in:

    • Covariant derivatives
    • Geodesic equations
    • Parallel transport
    • Curvature tensors
  • They were introduced by Elwin Bruno Christoffel and are fundamental in the differential geometry developed by Bernhard Riemann.

  • 리만 기하학에서 크리스토펠 기호는 좌표가 어떻게 구부러지는지를 설명합니다.

  • 텐서가 아닙니다.

  • 그들은 기저 벡터가 점마다 어떻게 변하는지를 인코딩합니다.

  • 그들은 다음에 나타납니다:

    • 공변 도함수
    • 측지 방정식
    • 병렬 운송
    • 곡률 텐서
  • 그것들은 엘윈 브루노 크리스토펠에 의해 도입되었으며, 베른하르트 리만이 개발한 미분 기하학의 기초입니다.

2️⃣ Definition#

g=gμν(x)dxμdxνg = g_{\mu\nu}(x)\, dx^\mu \otimes dx^\nu
  • This means:
    • gμν(x)gμν(x) is a symmetric positive-definite matrix
    • It depends smoothly on coordinates xux^u

📘 Christoffel Symbols (Levi-Civita Connection)#

  • The Christoffel symbols are defined as:
Γμνρ=12gρσ(μgνσ+νgμσσgμν)\Gamma^{\rho}_{\mu\nu} = \frac{1}{2} g^{\rho\sigma} \left( \partial_\mu g_{\nu\sigma} + \partial_\nu g_{\mu\sigma} - \partial_\sigma g_{\mu\nu} \right)
  • (다른거)Given a Riemannian metric:
gij(x)g_{ij}(x)
  • The Christoffel symbols of the Levi-Civita connection are:
Γijk=12gkl(gjlxi+gilxjgijxl)\Gamma^{k}_{ij} = \frac{1}{2} g^{kl} \left( \frac{\partial g_{jl}}{\partial x^i} + \frac{\partial g_{il}}{\partial x^j} - \frac{\partial g_{ij}}{\partial x^l} \right)
  • Where:
    • gijg_{ij} = metric
    • gklg^{kl} = inverse metric
    • gijxk\frac{\partial g_{ij}}{\partial x^k} = partial derivatives

3️⃣ Intuition (Geometric Meaning)#

  • In flat Euclidean space:
Γijk\Gamma^{k}_{ij}
  • In curved coordinates (like polar coordinates):

They are non-zero even if space is flat.

👉 They measure coordinate curvature, not intrinsic curvature.

4️⃣ Example: 2D Polar Coordinates#

  • Metric:
ds2=dr2+r2dθ2ds^2 = dr^2 + r^2d\theta^2
  • So:
g=(100r2)g=\left( \begin{array}{cc} 1 & 0 \\ 0 & r^2 \end{array} \right)
  • Inverse:
g1=(1001r2)g^{-1}=\left( \begin{array}{cc} 1 & 0 \\ 0 & {\frac{1}{r^2}} \end{array} \right)
  • Non-zero Christoffel symbols:
Γθθr=r\Gamma^{r}_{\theta\theta} = -rΓrθθ=Γθrθ=1r\qquad \Gamma^{\theta}_{r\theta} = \Gamma^{\theta}_{\theta r} = \frac{1}{r}
  • Everything else is zero.

5️⃣ Geodesic Equation#

  • They appear in:
d2xkdt2+Γijkdxidtdxjdt=0\frac{d^2 x^k}{dt^2} + \Gamma^{k}_{ij} \frac{dx^i}{dt} \frac{dx^j}{dt} = 0
  • This is Newton’s law in curved space.

6️⃣ Implementing Christoffel Symbols in Rust#

  • Since you’re comfortable with low-level memory and math structures, let’s implement:
    • A 2D metric
    • Compute inverse
    • Compute Christoffel symbols numerically
  • We’ll do a general 2D example.

🔹 Step 1: Matrix Utilities#

#[derive(Debug, Clone, Copy)]
struct Matrix2 {
    m: [[f64; 2]; 2],
}

impl Matrix2 {
    fn inverse(&self) -> Matrix2 {
        let det = self.m[0][0] * self.m[1][1] - self.m[0][1] * self.m[1][0];

        let inv_det = 1.0 / det;

        Matrix2 {
            m: [
                [self.m[1][1] * inv_det, -self.m[0][1] * inv_det],
                [-self.m[1][0] * inv_det, self.m[0][0] * inv_det],
            ],
        }
    }
}

🔹 Step 2: Polar Metric#

g=(100r2)g=\left( \begin{array}{cc} 1 & 0 \\ 0 & r^2 \end{array} \right)
fn polar_metric(r: f64) -> Matrix2 {
    Matrix2 {
        m: [[1.0, 0.0], [0.0, r * r]],
    }
}

🔹 Step 3: Partial Derivatives#

  • Only nonzero derivative:
gθθr=2r\frac{\partial g_{\theta\theta}}{\partial r} = 2r
fn partial_metric_r(r: f64) -> Matrix2 {
    Matrix2 {
        m: [[0.0, 0.0], [0.0, 2.0 * r]],
    }
}

🔹 Step 4: Compute Christoffel Symbols#

  • We compute:
Γijk=12gkl(gjl+gilgij)\Gamma^{k}_{ij} = \frac{1}{2} g^{kl} \left( \partial g_{jl} + \partial g_{il} - \partial g_{ij} \right)
  • For polar case:
fn christoffel_polar(r: f64) {
    let g = polar_metric(r);
    let g_inv = g.inverse();

    let dg_dr = partial_metric_r(r);

    // Γ^r_{θθ} = -r
    let gamma_r_thetatheta = -r;

    // Γ^θ_{rθ} = 1/r
    let gamma_theta_rtheta = 1.0 / r;

    println!("Gamma^r_θθ = {}", gamma_r_thetatheta);
    println!("Gamma^θ_rθ = {}", gamma_theta_rtheta);
}

🔹 Step 5: Main#

fn main() {
    let r = 2.0;
    christoffel_polar(r);
}
  • Output:
Gamma^r_θθ = -2
Gamma^θ_rθ = 0.5


Rust Full Code#

#[derive(Debug, Clone, Copy)]
struct Matrix2 {
    m: [[f64; 2]; 2],
}

impl Matrix2 {
    fn inverse(&self) -> Matrix2 {
        let det = self.m[0][0] * self.m[1][1] - self.m[0][1] * self.m[1][0];

        let inv_det = 1.0 / det;

        Matrix2 {
            m: [
                [self.m[1][1] * inv_det, -self.m[0][1] * inv_det],
                [-self.m[1][0] * inv_det, self.m[0][0] * inv_det],
            ],
        }
    }
}

fn polar_metric(r: f64) -> Matrix2 {
    Matrix2 {
        m: [[1.0, 0.0], [0.0, r * r]],
    }
}

fn partial_metric_r(r: f64) -> Matrix2 {
    Matrix2 {
        m: [[0.0, 0.0], [0.0, 2.0 * r]],
    }
}

fn christoffel_polar(r: f64) {
    let g = polar_metric(r);
    let g_inv = g.inverse();

    let dg_dr = partial_metric_r(r);

    // Γ^r_{θθ} = -r
    let gamma_r_thetatheta = -r;

    // Γ^θ_{rθ} = 1/r
    let gamma_theta_rtheta = 1.0 / r;

    println!("Gamma^r_θθ = {}", gamma_r_thetatheta);
    println!("Gamma^θ_rθ = {}", gamma_theta_rtheta);
}

fn main() {
    let r = 2.0;
    christoffel_polar(r);
}
260222_ChristoffelSymbols_in_Riemannian_Geometry
https://younghakim7.github.io/blog/posts/260222_christoffelsymbols_in_riemannian_geometry/
Author
YoungHa
Published at
2026-02-22