39_finite_difference_derivative/39_finite_difference_derivative.rs
1//! # Example: Finite-Difference Derivative
2//!
3//! Run: cargo run --example 39_finite_difference_derivative
4//!
5//! ## Problem
6//! Approximate the derivative of a function sampled on a grid, without any symbolic
7//! math — just function values and arithmetic.
8//!
9//! ## Math idea
10//! The central difference `(f(x+h) - f(x-h)) / (2h)` approximates `f'(x)` with an
11//! error that shrinks like `h²` (second-order accurate). Here `f(x) = x³`, whose
12//! exact derivative is `3x²`; for a cubic the central-difference error is exactly
13//! `h²`, which makes the approximation easy to see.
14//!
15//! ## Tensor representation
16//! The sample grid is a 1-D `Tensor` built with `linspace`; the function values are
17//! another `Tensor` computed with elementwise multiplication.
18//!
19//! ## What this demonstrates
20//! - `Tensor::linspace` for an evenly spaced grid;
21//! - elementwise `Tensor` arithmetic (`&x * &x`) to evaluate `f`;
22//! - a central-difference stencil over the sampled values.
23//!
24//! ## Expected output
25//! ```text
26//! h = 0.25
27//! x f'approx f'exact
28//! 0.25 0.2500 0.1875
29//! 0.50 0.8125 0.7500
30//! 0.75 1.7500 1.6875
31//! 1.00 3.0625 3.0000
32//! 1.25 4.7500 4.6875
33//! 1.50 6.8125 6.7500
34//! 1.75 9.2500 9.1875
35//! max abs error = 0.0625 (= h^2 = 0.0625)
36//! Finite-difference derivative: OK
37//! ```
38//!
39//! This is a numerical approximation, not symbolic differentiation.
40
41use matten::Tensor;
42
43fn main() {
44 // Evenly spaced sample grid on [0, 2].
45 let x = Tensor::linspace(0.0, 2.0, 9);
46 let xs = x.as_slice();
47 let h = xs[1] - xs[0];
48
49 // f(x) = x^3, evaluated with elementwise Tensor multiplication.
50 let x2 = &x * &x;
51 let f = &x2 * &x;
52 let fs = f.as_slice();
53
54 println!("h = {h}");
55 println!(" x f'approx f'exact");
56 let mut max_err = 0.0f64;
57 for i in 1..xs.len() - 1 {
58 let approx = (fs[i + 1] - fs[i - 1]) / (2.0 * h);
59 let exact = 3.0 * xs[i] * xs[i]; // d/dx x^3 = 3x^2
60 let err = (approx - exact).abs();
61 max_err = max_err.max(err);
62 println!("{:4.2} {:6.4} {:6.4}", xs[i], approx, exact);
63 // Central difference of x^3 has error exactly h^2.
64 assert!((approx - (exact + h * h)).abs() < 1e-9);
65 }
66 println!("max abs error = {max_err:.4} (= h^2 = {:.4})", h * h);
67 assert!((max_err - h * h).abs() < 1e-9);
68 println!("Finite-difference derivative: OK");
69}