Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help



SymPy Validation Workflow

Topic: advanced.pde.sympy_validation

SymPy serves as the authoritative reference for validating MathHook PDE solvers. With 15+ years of development, extensive test coverage, and academic validation, SymPy provides a reliable baseline for comparing solution structures, eigenvalues, and boundary condition satisfaction. This workflow is used internally for validation only; public documentation cites textbooks.

Mathematical Definition

Validation criteria for PDE solutions:

  1. Solution Structure Match: Both implementations produce equivalent forms
  2. Eigenvalue Formula: matches numerically
  3. Boundary Conditions: satisfied
  4. Temporal Behavior: matches across implementations

SymPy Validation Workflow

Why SymPy is the Reference Implementation

SymPy (~/Documents/work/math/sympy/) is the authoritative reference for validating MathHook PDE solvers for the following reasons:

  1. Mature and Battle-Tested: SymPy's PDE solving has been developed and refined over 15+ years
  2. Extensive Test Suite: Thousands of test cases covering edge cases
  3. Academic Validation: Used in research and education worldwide
  4. Well-Documented: Clear mathematical foundations and algorithms
  5. Python MCP Available: Can be queried programmatically for validation

Important: SymPy is used internally for validation only. Public documentation cites textbooks and papers, NOT SymPy.

Validation Workflow

Step 1: Define Problem in Both Systems

Define the same PDE problem in both MathHook (Rust) and SymPy (Python) to enable comparison.

Step 2: Compare Solution Structure

Validation criteria:

  • ✅ Structure matches (sine modes, exponential decay)
  • ✅ Eigenvalue formula matches:
  • ✅ Both use symbolic coefficients
  • ✅ Temporal behavior matches:

Step 3: Verify Eigenvalues Numerically

Compare computed eigenvalues with high precision (typically < 1e-4 relative error).

Step 4: Validate Boundary Condition Satisfaction

SymPy can verify BC satisfaction through symbolic substitution. MathHook returns symbolic solutions that can be validated manually or programmatically.

Validation Test Cases

Heat Equation

Test 1: Dirichlet BCs, constant IC

  • Verify eigenvalue: λ₁ = π² ≈ 9.8696
  • Check solution structure matches SymPy

Test 2: Different domain lengths

  • L = 1: λ₁ = π²
  • L = 2: λ₁ = (π/2)² = π²/4
  • L = 0.5: λ₁ = (π/0.5)² = 4π²

Wave Equation

Test 1: Standing wave frequencies

  • Compare angular frequencies: ω_n = nπc/L
  • Verify relationship: ω_n = c*√λ_n

Laplace Equation

Test 1: Rectangular domain eigenvalues

  • Verify 2D eigenvalue formula: λₙ = (nπ/a)²

Known Differences (Acceptable)

1. Coefficient Representation

SymPy: Uses Sum() with index notation

Sum(C_n * sin(n*pi*x/L) * exp(-n²*pi²*alpha*t/L²), (n, 1, oo))

MathHook: Expands finite sum explicitly

#![allow(unused)]
fn main() {
A_1*sin(π*x)*exp(-π²*α*t) + A_2*sin(2π*x)*exp(-4π²*α*t) + ...
}

Why acceptable: Both representations are mathematically equivalent. MathHook finite sum is more practical for numerical evaluation.

2. Variable Naming

SymPy: Uses function notation u(x,t)

MathHook: Uses symbol u with independent variables as context

Why acceptable: Notational difference only; mathematical content identical.

3. Symbolic vs Numerical Coefficients

Both return symbolic coefficients for Fourier series. SymPy requires separate fourier_series() call; MathHook plans integration in Phase 2.

Why acceptable: Both defer coefficient computation to avoid integration challenges.

Validation Checklist

Before claiming a PDE solver is correct:

  1. Solution structure matches SymPy (sine/cosine modes, exp/sinh/cosh temporal)
  2. Eigenvalue formula matches SymPy (verified numerically)
  3. Boundary conditions satisfied when substituted
  4. Initial conditions structure correct (even if coefficients symbolic)
  5. Edge cases tested (different domain lengths, BCs)
  6. Known limitations documented (Neumann BCs, non-homogeneous BCs, etc.)

SymPy MCP Integration

Available via MCP: SymPy can be queried programmatically for validation.

Example workflow:

  1. Agent implements new MathHook PDE solver
  2. Agent queries SymPy MCP for reference solution
  3. Agent compares eigenvalues, solution structure
  4. Agent verifies BCs/ICs satisfied
  5. Agent documents any acceptable differences
  6. Agent adds regression tests

Important: SymPy MCP is for internal validation, NOT cited in public documentation.

Mathematical References (For Public Documentation)

When documenting PDE solvers, cite these instead of SymPy:

  1. Strauss, Walter A. Partial Differential Equations: An Introduction, 2nd ed.
  2. Evans, Lawrence C. Partial Differential Equations, 2nd ed.
  3. Haberman, Richard Applied Partial Differential Equations, 5th ed.

Summary

SymPy Validation Workflow:

  1. Implement solver in MathHook
  2. Compare solution structure with SymPy
  3. Verify eigenvalues numerically
  4. Test BC/IC satisfaction
  5. Document acceptable differences
  6. Add regression tests

Validation Criteria:

  • ✅ Structure matches
  • ✅ Eigenvalues match (numerical verification)
  • ✅ BCs/ICs satisfied
  • ⚠️ Symbolic coefficients acceptable (both implementations)

Examples

Heat Equation Validation

Compare MathHook solution with SymPy reference for heat equation

Rust
#![allow(unused)]
fn main() {
#[test]
fn test_heat_vs_sympy_dirichlet() {
    // MathHook solution
    let result = solve_heat_1d(...)?;

    // SymPy reference (computed offline)
    let expected_lambda_1 = 9.8696;  // π²

    // Validate eigenvalue
    let lambda_1 = result.eigenvalues[0].evaluate()?;
    assert!((lambda_1 - expected_lambda_1).abs() < 1e-4);
}

}
Python
def test_heat_vs_sympy_dirichlet():
    # MathHook solution (via Python bindings)
    result = solve_heat_1d(...)

    # SymPy reference
    expected_lambda_1 = 9.8696  # π²

    # Validate eigenvalue
    lambda_1 = result.eigenvalues[0].evaluate()
    assert abs(lambda_1 - expected_lambda_1) < 1e-4

JavaScript
test('heat equation matches SymPy', () => {
    // MathHook solution
    const result = solveHeat1d(...);

    // SymPy reference
    const expectedLambda1 = 9.8696;  // π²

    // Validate eigenvalue
    const lambda1 = result.eigenvalues[0].evaluate();
    expect(Math.abs(lambda1 - expectedLambda1)).toBeLessThan(1e-4);
});

Eigenvalue Scaling Validation

Test eigenvalue scaling with different domain lengths

Rust
#![allow(unused)]
fn main() {
#[test]
fn test_heat_eigenvalues_scaling() {
    // L = 1: λ₁ = π²
    // L = 2: λ₁ = (π/2)² = π²/4
    // L = 0.5: λ₁ = (π/0.5)² = 4π²

    let L = 2.0;
    let result = solve_heat_1d_with_length(L)?;
    let expected = std::f64::consts::PI.powi(2) / 4.0;
    assert!((result.eigenvalues[0].evaluate()? - expected).abs() < 1e-4);
}

}
Python
def test_heat_eigenvalues_scaling():
    # L = 1: λ₁ = π²
    # L = 2: λ₁ = (π/2)² = π²/4
    # L = 0.5: λ₁ = (π/0.5)² = 4π²

    L = 2.0
    result = solve_heat_1d_with_length(L)
    expected = (math.pi ** 2) / 4.0
    assert abs(result.eigenvalues[0].evaluate() - expected) < 1e-4

JavaScript
test('heat eigenvalues scale correctly', () => {
    // L = 1: λ₁ = π²
    // L = 2: λ₁ = (π/2)² = π²/4
    // L = 0.5: λ₁ = (π/0.5)² = 4π²

    const L = 2.0;
    const result = solveHeat1dWithLength(L);
    const expected = (Math.PI ** 2) / 4.0;
    expect(Math.abs(result.eigenvalues[0].evaluate() - expected)).toBeLessThan(1e-4);
});

Wave Equation Frequency Validation

Verify wave equation frequencies against SymPy

Rust
#![allow(unused)]
fn main() {
#[test]
fn test_wave_frequencies_vs_sympy() {
    let c = 340.0;  // m/s (speed of sound)
    let L = 1.0;    // m

    let result = solve_wave_1d(...)?;

    // SymPy: ω_n = n*π*c/L
    let omega_1 = std::f64::consts::PI * c / L;
    let f_1 = omega_1 / (2.0 * std::f64::consts::PI);  // Frequency in Hz

    // MathHook eigenvalues: λ_n = (nπ/L)²
    // ω_n = c*√λ_n = c*nπ/L
    let lambda_1 = result.eigenvalues[0].evaluate()?;
    let omega_mathhook = c * lambda_1.sqrt();

    assert!((omega_mathhook - omega_1).abs() < 1e-6);
}

}
Python
def test_wave_frequencies_vs_sympy():
    c = 340.0  # m/s (speed of sound)
    L = 1.0    # m

    result = solve_wave_1d(...)

    # SymPy: ω_n = n*π*c/L
    omega_1 = math.pi * c / L
    f_1 = omega_1 / (2.0 * math.pi)  # Frequency in Hz

    # MathHook eigenvalues: λ_n = (nπ/L)²
    # ω_n = c*√λ_n = c*nπ/L
    lambda_1 = result.eigenvalues[0].evaluate()
    omega_mathhook = c * math.sqrt(lambda_1)

    assert abs(omega_mathhook - omega_1) < 1e-6

JavaScript
test('wave equation frequencies match SymPy', () => {
    const c = 340.0;  // m/s (speed of sound)
    const L = 1.0;    // m

    const result = solveWave1d(...);

    // SymPy: ω_n = n*π*c/L
    const omega1 = Math.PI * c / L;
    const f1 = omega1 / (2.0 * Math.PI);  // Frequency in Hz

    // MathHook eigenvalues: λ_n = (nπ/L)²
    // ω_n = c*√λ_n = c*nπ/L
    const lambda1 = result.eigenvalues[0].evaluate();
    const omegaMathhook = c * Math.sqrt(lambda1);

    expect(Math.abs(omegaMathhook - omega1)).toBeLessThan(1e-6);
});

Performance

Time Complexity: O(n)

API Reference

  • Rust: mathhook_core::pde::validation
  • Python: mathhook.pde.validation
  • JavaScript: mathhook.pde.validation

See Also