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:
- Solution Structure Match: Both implementations produce equivalent forms
- Eigenvalue Formula: matches numerically
- Boundary Conditions: satisfied
- 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:
- Mature and Battle-Tested: SymPy's PDE solving has been developed and refined over 15+ years
- Extensive Test Suite: Thousands of test cases covering edge cases
- Academic Validation: Used in research and education worldwide
- Well-Documented: Clear mathematical foundations and algorithms
- 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:
- ✅ Solution structure matches SymPy (sine/cosine modes, exp/sinh/cosh temporal)
- ✅ Eigenvalue formula matches SymPy (verified numerically)
- ✅ Boundary conditions satisfied when substituted
- ✅ Initial conditions structure correct (even if coefficients symbolic)
- ✅ Edge cases tested (different domain lengths, BCs)
- ✅ Known limitations documented (Neumann BCs, non-homogeneous BCs, etc.)
SymPy MCP Integration
Available via MCP: SymPy can be queried programmatically for validation.
Example workflow:
- Agent implements new MathHook PDE solver
- Agent queries SymPy MCP for reference solution
- Agent compares eigenvalues, solution structure
- Agent verifies BCs/ICs satisfied
- Agent documents any acceptable differences
- 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:
- Strauss, Walter A. Partial Differential Equations: An Introduction, 2nd ed.
- Evans, Lawrence C. Partial Differential Equations, 2nd ed.
- Haberman, Richard Applied Partial Differential Equations, 5th ed.
Summary
SymPy Validation Workflow:
- Implement solver in MathHook
- Compare solution structure with SymPy
- Verify eigenvalues numerically
- Test BC/IC satisfaction
- Document acceptable differences
- 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