Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Nonanti/mathcore/llms.txt

Use this file to discover all available pages before exploring further.

MathCore’s differential module provides a complete toolkit for solving ordinary and partial differential equations numerically. ODEs are expressed as symbolic Expr values — you write the right-hand side as a MathCore expression string, and the solver evaluates it step by step. PDE solvers use finite-difference schemes and operate on closures for boundary and initial conditions. All solvers return structured result types that make downstream plotting and analysis straightforward.

Import

use mathcore::differential::{DifferentialEquations, PDESolver};
use mathcore::MathCore;

Result Types

ODESolution

All ODE solvers return an ODESolution struct:
#[derive(Debug, Clone)]
pub struct ODESolution {
    pub t: Vec<f64>,       // time (or independent variable) values
    pub y: Vec<Vec<f64>>,  // solution values; y[i] is the state vector at t[i]
}
For scalar (first-order, single equation) solvers, y[i] is a one-element vector [y_i]. For systems of ODEs, y[i] contains one entry per equation.

BoundaryCondition

#[derive(Debug, Clone)]
pub enum BoundaryCondition {
    InitialValue {
        t0: f64,
        y0: Vec<f64>,
    },
    BoundaryValue {
        ta: f64,
        tb: f64,
        ya: Vec<f64>,
        yb: Vec<f64>,
    },
}

ODE Solvers

First-Order ODE — RK4

DifferentialEquations::solve_ode_first_order(f, t_var, y_var, initial_condition, t_final, steps) -> Result<ODESolution, MathError>

Solves dy/dt = f(t, y) using the 4th-order Runge-Kutta method. This is the recommended solver for smooth, non-stiff problems.
ParameterTypeDescription
f&ExprRight-hand side expression; may reference t_var and y_var
t_var&strName of the independent variable (e.g. "t")
y_var&strName of the dependent variable (e.g. "y")
initial_condition(f64, f64)(t₀, y₀) — initial point
t_finalf64End of the integration interval
stepsusizeNumber of uniform steps; step size h = (t_final − t₀) / steps
The RK4 update formula applied at each step:
k1 = f(t,       y)
k2 = f(t + h/2, y + h·k1/2)
k3 = f(t + h/2, y + h·k2/2)
k4 = f(t + h,   y + h·k3)
y_new = y + h·(k1 + 2·k2 + 2·k3 + k4) / 6
Exponential decay — dy/dt = −y, y(0) = 1 (analytical: y = exp(-t)):
use mathcore::differential::DifferentialEquations;
use mathcore::MathCore;

let f = MathCore::parse("-y").unwrap();

let solution = DifferentialEquations::solve_ode_first_order(
    &f,
    "t",           // independent variable
    "y",           // dependent variable
    (0.0, 1.0),   // initial: t=0, y=1
    5.0,           // solve to t=5
    100,           // 100 steps
).unwrap();

println!("t values: {:?}", &solution.t[..5]);
println!("y values: {:?}", &solution.y[..5]);

// Verify: y(1) ≈ e^(-1) ≈ 0.3679
let final_y = solution.y.last().unwrap()[0];
println!("y(5) ≈ {:.6}", final_y); // ≈ 0.006738
Logistic growth — real-world example from examples/scientific_computing.rs:
use mathcore::differential::DifferentialEquations;
use mathcore::parser::Parser;

// dP/dt = r·P·(1 - P/K),  r=0.5, K=1000
let expr = Parser::parse("0.5*P*(1 - P/1000)").unwrap();

let solution = DifferentialEquations::solve_ode_first_order(
    &expr,
    "t",
    "P",
    (0.0, 10.0), // initial population 10
    20.0,         // 20 time units
    200,
).unwrap();

for i in (0..solution.t.len()).step_by(20) {
    println!("t={:.1}  P={:.1}", solution.t[i], solution.y[i][0]);
}

First-Order ODE — Euler’s Method

DifferentialEquations::euler_method(f, t_var, y_var, initial_condition, t_final, steps) -> Result<ODESolution, MathError>

Simple forward Euler integration: y_ = y_n + h·f(t_n, y_n). Same signature as solve_ode_first_order.
use mathcore::differential::DifferentialEquations;
use mathcore::MathCore;

let f = MathCore::parse("-y").unwrap();

let solution = DifferentialEquations::euler_method(
    &f, "t", "y",
    (0.0, 1.0),
    5.0,
    100,
).unwrap();
Euler’s method has first-order accuracy (local error O(h²), global error O(h)). RK4 achieves fourth-order accuracy with the same step count. Prefer solve_ode_first_order unless you have a specific reason to use Euler — such as debugging or demonstrating the accuracy difference.

Stiff ODE Solver

DifferentialEquations::solve_stiff_ode(f, jacobian, t_var, y_var, initial_condition, t_final, steps, tolerance) -> Result<ODESolution, MathError>

Solves stiff problems using the implicit Backward Euler method with Newton iteration at each step. Stiff ODEs have rapidly decaying components that force explicit methods to use very small step sizes.
ParameterTypeDescription
jacobianOption<&Expr>Analytic ∂f/∂y; if None, a numerical Jacobian (finite differences) is used
tolerancef64Newton iteration convergence threshold
use mathcore::differential::DifferentialEquations;
use mathcore::MathCore;

// Stiff equation: dy/dt = -1000*(y - cos(t))
let f = MathCore::parse("-1000*(y - cos(t))").unwrap();

let solution = DifferentialEquations::solve_stiff_ode(
    &f,
    None,          // use numerical Jacobian
    "t", "y",
    (0.0, 1.0),
    2.0,
    200,
    1e-8,          // Newton tolerance
).unwrap();
Providing an analytic Jacobian expression speeds up the Newton iteration significantly for stiff problems, because it avoids the extra function evaluation needed for the numerical approximation.

Second-Order ODE

DifferentialEquations::solve_ode_second_order(p, q, r, x_var, initial_conditions, x_final, steps) -> Result<ODESolution, MathError>

Solves the linear second-order equation y″ + p(x)·y′ + q(x)·y = r(x) by converting it to a first-order system internally. The returned ODESolution stores both y and y′ at each step: solution.y[i] = [y_i, y'_i].
ParameterTypeDescription
p, q, r&ExprCoefficient functions of the independent variable
initial_conditions(f64, f64, f64)(x₀, y(x₀), y′(x₀))
use mathcore::differential::DifferentialEquations;
use mathcore::MathCore;

// Simple harmonic oscillator: y'' + y = 0
// p=0, q=1, r=0; y(0)=1, y'(0)=0  =>  y = cos(x)
let p = MathCore::parse("0").unwrap();
let q = MathCore::parse("1").unwrap();
let r = MathCore::parse("0").unwrap();

let solution = DifferentialEquations::solve_ode_second_order(
    &p, &q, &r,
    "x",
    (0.0, 1.0, 0.0), // x0=0, y0=1, y'0=0
    6.28318,           // one full period (2π)
    1000,
).unwrap();

// solution.y[i][0] = y(x_i), solution.y[i][1] = y'(x_i)
println!("y(π) ≈ {:.4}", solution.y[500][0]); // ≈ -1

System of First-Order ODEs

DifferentialEquations::solve_ode_system(functions, t_var, y_vars, initial_conditions, t_final, steps) -> Result<ODESolution, MathError>

Solves dy⃗/dt = F⃗(t, y⃗) using RK4 applied to all equations simultaneously. Each equation in functions may reference t_var and any variable name in y_vars. Nonlinear pendulum — from examples/scientific_computing.rs:
use mathcore::differential::DifferentialEquations;
use mathcore::parser::Parser;

// θ' = ω,  ω' = -(g/L)·sin(θ)
let theta_dot = Parser::parse("omega").unwrap();
let omega_dot = Parser::parse("-9.81*sin(theta)").unwrap();

let solution = DifferentialEquations::solve_ode_system(
    &[theta_dot, omega_dot],
    "t",
    &["theta".to_string(), "omega".to_string()],
    (0.0, vec![1.0, 0.0]), // θ(0)=1 rad, ω(0)=0
    10.0,                   // 10 seconds
    1000,
).unwrap();

println!("Time\tAngle\t\tAngular Velocity");
for i in (0..solution.t.len()).step_by(100) {
    println!(
        "{:.1}\t{:.4}\t\t{:.4}",
        solution.t[i],
        solution.y[i][0], // theta
        solution.y[i][1], // omega
    );
}

Analytical Solution for Linear ODEs with Constant Coefficients

DifferentialEquations::solve_linear_constant_coeff(coefficients: &[f64], initial_conditions: &[f64]) -> Result<Expr, MathError>

Returns a symbolic general solution for an nth-order linear ODE with constant coefficients. coefficients must be provided from highest to lowest degree: [a_n, a_{n-1}, …, a_1, a_0]. Real roots produce exponential terms Cᵢ·exp(rᵢ·t) and complex conjugate pairs produce exp(α·t)·(C₁·cos(β·t) + C₂·sin(β·t)) terms.
use mathcore::differential::DifferentialEquations;

// y'' - 3y' + 2y = 0  =>  coefficients [1, -3, 2]
// Roots: r=1, r=2  =>  solution: C1*e^t + C2*e^(2t)
let solution = DifferentialEquations::solve_linear_constant_coeff(
    &[1.0, -3.0, 2.0],  // coefficients
    &[1.0, 0.0],         // initial conditions (y(0), y'(0))
).unwrap();

println!("General solution: {}", solution);

PDE Solvers

PDESolver uses finite-difference methods on uniform grids.

Heat Equation

PDESolver::solve_heat_equation(alpha, initial_condition, boundary_left, boundary_right, x_range, t_final, nx, nt) -> Result<Vec<Vec<f64>>, MathError>

Solves ∂u/∂t = α·∂²u/∂x² using the explicit finite-difference scheme. Returns a 2D grid u[t_index][x_index].
The explicit scheme is only stable when α·Δt/Δx² ≤ 0.5 (Von Neumann stability condition). If this ratio exceeds 0.5, the solver returns Err(MathError::InvalidOperation). Reduce t_final / nt or increase nx to satisfy the condition.
use mathcore::differential::PDESolver;

let initial_temp = |x: f64| {
    if (x - 0.5).abs() < 0.1 { 100.0 } else { 20.0 }
};

let grid = PDESolver::solve_heat_equation(
    0.01,           // thermal diffusivity α
    &initial_temp,
    20.0,           // left boundary temperature
    20.0,           // right boundary temperature
    (0.0, 1.0),     // x ∈ [0, 1]
    0.5,            // solve to t=0.5
    50,             // 50 spatial points
    100,            // 100 time steps
).unwrap();

println!("Initial max temp: {:.1}", grid[0].iter().cloned().fold(f64::NEG_INFINITY, f64::max));
println!("Final max temp:   {:.1}", grid[99].iter().cloned().fold(f64::NEG_INFINITY, f64::max));

Wave Equation

PDESolver::solve_wave_equation(c, initial_position, initial_velocity, x_range, t_final, nx, nt) -> Result<Vec<Vec<f64>>, MathError>

Solves ∂²u/∂t² = c²·∂²u/∂x² using the explicit Courant–Friedrichs–Lewy (CFL) scheme.
The CFL condition requires c·Δt/Δx ≤ 1. The solver returns an error if this is violated. Increase nt (more time steps) or decrease nx (coarser spatial grid) to satisfy it.
use mathcore::differential::PDESolver;

let initial_position = |x: f64| {
    let (center, width) = (0.5, 0.05);
    (-(((x - center) / width).powi(2))).exp()
};
let initial_velocity = |_: f64| 0.0;

let grid = PDESolver::solve_wave_equation(
    0.5,                  // wave speed c
    &initial_position,
    &initial_velocity,
    (0.0, 1.0),           // x ∈ [0, 1]
    1.0,                  // t ∈ [0, 1]
    100,                  // spatial points
    200,                  // time steps
).unwrap();

Laplace Equation

PDESolver::solve_laplace_equation(boundary_conditions, x_range, y_range, nx, ny, tolerance, max_iterations) -> Result<Vec<Vec<f64>>, MathError>

Solves ∇²u = 0 (steady-state heat distribution, electrostatics, etc.) using Gauss-Seidel iteration on a 2D grid. boundary_conditions is a closure (x, y) -> Option<f64>; return Some(value) on boundary points and None for interior points.
use mathcore::differential::PDESolver;

let bcs = |x: f64, y: f64| -> Option<f64> {
    if x == 0.0 || x == 1.0 || y == 0.0 { Some(0.0) }
    else if y == 1.0 { Some(100.0) }  // hot top wall
    else { None }
};

let grid = PDESolver::solve_laplace_equation(
    &bcs,
    (0.0, 1.0),   // x range
    (0.0, 1.0),   // y range
    50,            // nx
    50,            // ny
    1e-6,          // convergence tolerance
    10_000,        // max iterations
).unwrap();

println!("Center temperature: {:.2}", grid[25][25]);

RK4 vs Euler Accuracy

The choice of solver matters. For the same step size h, RK4 makes four function evaluations per step and achieves global error O(h⁴), while Euler makes one evaluation and produces O(h) error.
SolverEvaluations/stepGlobal errorBest for
euler_method1O(h)Simple demos, very cheap f
solve_ode_first_order (RK4)4O(h⁴)Most smooth problems
solve_stiff_ode (Backward Euler)~10 (Newton)O(h)Stiff problems
For a given accuracy target, RK4 typically allows a step size ~10× larger than Euler, meaning far fewer steps for the same precision. Unless your right-hand side f is extremely expensive to evaluate, RK4 is the better choice.

Build docs developers (and LLMs) love