amari_calculus/
lie.rs

1//! Lie derivatives and Lie brackets
2//!
3//! This module provides Lie derivatives - derivatives along vector fields that measure
4//! how fields change along the flow of a vector field.
5//!
6//! ## Mathematical Background
7//!
8//! The Lie derivative generalizes the directional derivative to arbitrary tensor fields:
9//!
10//! ### Lie Bracket of Vector Fields
11//!
12//! For vector fields X and Y, the Lie bracket is:
13//! ```text
14//! [X, Y] = ∇_X Y - ∇_Y X
15//! ```
16//!
17//! In coordinates:
18//! ```text
19//! [X, Y]^i = X^j ∂_j Y^i - Y^j ∂_j X^i
20//! ```
21//!
22//! ### Lie Derivative
23//!
24//! - **Scalar field**: L_X(f) = X(f) = X^i ∂_i f (directional derivative)
25//! - **Vector field**: L_X(Y) = [X, Y]
26//! - **Multivector field**: L_X(F) extends via Leibniz rule
27//!
28//! ## Examples
29//!
30//! ```
31//! use amari_calculus::{VectorField, LieDerivative, vector_from_slice};
32//!
33//! // Define rotation field X = (-y, x, 0)
34//! let x = VectorField::<3, 0, 0>::new(|coords| {
35//!     vector_from_slice(&[-coords[1], coords[0], 0.0])
36//! });
37//!
38//! // Define radial field Y = (x, y, 0)
39//! let y = VectorField::<3, 0, 0>::new(|coords| {
40//!     vector_from_slice(&[coords[0], coords[1], 0.0])
41//! });
42//!
43//! let lie = LieDerivative::<3, 0, 0>::new();
44//!
45//! // Compute Lie bracket [X, Y] at origin
46//! let bracket = lie.bracket(&x, &y, &[0.0, 0.0, 0.0]);
47//! ```
48
49use crate::fields::*;
50use amari_core::Multivector;
51
52/// Lie derivative operator
53///
54/// Computes Lie derivatives and Lie brackets of fields along vector fields.
55pub struct LieDerivative<const P: usize, const Q: usize, const R: usize> {
56    /// Step size for numerical differentiation
57    h: f64,
58}
59
60impl<const P: usize, const Q: usize, const R: usize> LieDerivative<P, Q, R> {
61    /// Create new Lie derivative operator
62    ///
63    /// # Examples
64    ///
65    /// ```
66    /// use amari_calculus::LieDerivative;
67    ///
68    /// let lie = LieDerivative::<3, 0, 0>::new();
69    /// ```
70    pub fn new() -> Self {
71        Self { h: 1e-5 }
72    }
73
74    /// Set step size for numerical differentiation
75    pub fn with_step_size(mut self, h: f64) -> Self {
76        self.h = h;
77        self
78    }
79
80    /// Compute Lie derivative of scalar field along vector field: L_X(f)
81    ///
82    /// This is equivalent to the directional derivative X(f).
83    ///
84    /// # Arguments
85    ///
86    /// * `x` - Vector field along which to differentiate
87    /// * `f` - Scalar field to differentiate
88    /// * `coords` - Point at which to evaluate
89    ///
90    /// # Returns
91    ///
92    /// Scalar value L_X(f) = X^i ∂_i f
93    ///
94    /// # Examples
95    ///
96    /// ```
97    /// use amari_calculus::{ScalarField, VectorField, LieDerivative, vector_from_slice};
98    ///
99    /// // f(x, y) = x² + y²
100    /// let f = ScalarField::<3, 0, 0>::new(|coords| {
101    ///     coords[0].powi(2) + coords[1].powi(2)
102    /// });
103    ///
104    /// // X = (1, 0, 0) - unit vector in x direction
105    /// let x = VectorField::<3, 0, 0>::new(|_coords| {
106    ///     vector_from_slice(&[1.0, 0.0, 0.0])
107    /// });
108    ///
109    /// let lie = LieDerivative::<3, 0, 0>::new();
110    ///
111    /// // L_X(f) at (3, 4) should be ∂f/∂x = 2x = 6
112    /// let result = lie.of_scalar(&x, &f, &[3.0, 4.0, 0.0]);
113    /// ```
114    pub fn of_scalar(
115        &self,
116        x: &VectorField<P, Q, R>,
117        f: &ScalarField<P, Q, R>,
118        coords: &[f64],
119    ) -> f64 {
120        let dim = P + Q + R;
121        let x_val = x.evaluate(coords);
122        let mut result = 0.0;
123
124        // L_X(f) = X^i ∂_i f
125        for i in 0..dim {
126            let x_i = x_val.vector_component(i);
127            if let Ok(df_di) = f.partial_derivative(coords, i, self.h) {
128                result += x_i * df_di;
129            }
130        }
131
132        result
133    }
134
135    /// Compute Lie bracket of two vector fields: [X, Y]
136    ///
137    /// The Lie bracket measures the non-commutativity of the flows generated by X and Y.
138    ///
139    /// # Arguments
140    ///
141    /// * `x` - First vector field
142    /// * `y` - Second vector field
143    /// * `coords` - Point at which to evaluate
144    ///
145    /// # Returns
146    ///
147    /// Vector field [X, Y]^i = X^j ∂_j Y^i - Y^j ∂_j X^i
148    ///
149    /// # Examples
150    ///
151    /// ```
152    /// use amari_calculus::{VectorField, LieDerivative, vector_from_slice};
153    ///
154    /// // Coordinate vector fields
155    /// let e1 = VectorField::<3, 0, 0>::new(|_coords| {
156    ///     vector_from_slice(&[1.0, 0.0, 0.0])
157    /// });
158    /// let e2 = VectorField::<3, 0, 0>::new(|_coords| {
159    ///     vector_from_slice(&[0.0, 1.0, 0.0])
160    /// });
161    ///
162    /// let lie = LieDerivative::<3, 0, 0>::new();
163    ///
164    /// // [e1, e2] should be zero (coordinate fields commute)
165    /// let bracket = lie.bracket(&e1, &e2, &[1.0, 1.0, 0.0]);
166    /// ```
167    pub fn bracket(
168        &self,
169        x: &VectorField<P, Q, R>,
170        y: &VectorField<P, Q, R>,
171        coords: &[f64],
172    ) -> Multivector<P, Q, R> {
173        let dim = P + Q + R;
174        let x_val = x.evaluate(coords);
175        let y_val = y.evaluate(coords);
176
177        let mut bracket = Multivector::zero();
178
179        // [X, Y]^i = X^j ∂_j Y^i - Y^j ∂_j X^i
180        for i in 0..dim {
181            let mut component = 0.0;
182
183            // First term: X^j ∂_j Y^i
184            for j in 0..dim {
185                let x_j = x_val.vector_component(j);
186                if let Ok(dy_dj) = y.partial_derivative(coords, j, self.h) {
187                    component += x_j * dy_dj.vector_component(i);
188                }
189            }
190
191            // Second term: -Y^j ∂_j X^i
192            for j in 0..dim {
193                let y_j = y_val.vector_component(j);
194                if let Ok(dx_dj) = x.partial_derivative(coords, j, self.h) {
195                    component -= y_j * dx_dj.vector_component(i);
196                }
197            }
198
199            bracket.set_vector_component(i, component);
200        }
201
202        bracket
203    }
204
205    /// Compute Lie derivative of vector field along another vector field: L_X(Y) = [X, Y]
206    ///
207    /// This is an alias for the Lie bracket.
208    ///
209    /// # Arguments
210    ///
211    /// * `x` - Vector field along which to differentiate
212    /// * `y` - Vector field to differentiate
213    /// * `coords` - Point at which to evaluate
214    ///
215    /// # Returns
216    ///
217    /// Vector field L_X(Y) = [X, Y]
218    pub fn of_vector(
219        &self,
220        x: &VectorField<P, Q, R>,
221        y: &VectorField<P, Q, R>,
222        coords: &[f64],
223    ) -> Multivector<P, Q, R> {
224        self.bracket(x, y, coords)
225    }
226
227    /// Compute Lie derivative of multivector field along vector field: L_X(F)
228    ///
229    /// For a general multivector field, the Lie derivative extends via the Leibniz rule.
230    ///
231    /// # Arguments
232    ///
233    /// * `x` - Vector field along which to differentiate
234    /// * `f` - Multivector field to differentiate
235    /// * `coords` - Point at which to evaluate
236    ///
237    /// # Returns
238    ///
239    /// Multivector field L_X(F)
240    pub fn of_multivector(
241        &self,
242        x: &VectorField<P, Q, R>,
243        f: &MultivectorField<P, Q, R>,
244        coords: &[f64],
245    ) -> Multivector<P, Q, R> {
246        let dim = P + Q + R;
247        let x_val = x.evaluate(coords);
248
249        // L_X(F) = X^i ∂_i F (directional derivative for now)
250        // Full implementation would account for geometric product structure
251        let mut result = Multivector::zero();
252
253        for i in 0..dim {
254            let x_i = x_val.vector_component(i);
255            if let Ok(df_di) = f.partial_derivative(coords, i, self.h) {
256                result = result + df_di * x_i;
257            }
258        }
259
260        result
261    }
262}
263
264impl<const P: usize, const Q: usize, const R: usize> Default for LieDerivative<P, Q, R> {
265    fn default() -> Self {
266        Self::new()
267    }
268}
269
270#[cfg(test)]
271mod tests {
272    use super::*;
273
274    #[test]
275    fn test_lie_derivative_of_scalar() {
276        // f(x, y) = x² + y²
277        let f = ScalarField::<3, 0, 0>::new(|coords| coords[0].powi(2) + coords[1].powi(2));
278
279        // X = (1, 0, 0)
280        let x = VectorField::<3, 0, 0>::new(|_coords| crate::vector_from_slice(&[1.0, 0.0, 0.0]));
281
282        let lie = LieDerivative::<3, 0, 0>::new();
283        let result = lie.of_scalar(&x, &f, &[3.0, 4.0, 0.0]);
284
285        // L_X(f) = X · ∇f = (1,0,0) · (2x, 2y, 0) = 2x = 6
286        assert!(
287            (result - 6.0).abs() < 1e-4,
288            "Lie derivative should be 6.0, got {}",
289            result
290        );
291    }
292
293    #[test]
294    fn test_lie_bracket_coordinate_fields() {
295        // Coordinate vector fields commute, so bracket should be zero
296        let e1 = VectorField::<3, 0, 0>::new(|_coords| crate::vector_from_slice(&[1.0, 0.0, 0.0]));
297        let e2 = VectorField::<3, 0, 0>::new(|_coords| crate::vector_from_slice(&[0.0, 1.0, 0.0]));
298
299        let lie = LieDerivative::<3, 0, 0>::new();
300        let bracket = lie.bracket(&e1, &e2, &[1.0, 1.0, 0.0]);
301
302        // All components should be approximately zero
303        for i in 0..3 {
304            assert!(
305                bracket.vector_component(i).abs() < 1e-4,
306                "Coordinate fields should commute, got component {} = {}",
307                i,
308                bracket.vector_component(i)
309            );
310        }
311    }
312
313    #[test]
314    fn test_lie_bracket_noncommuting_fields() {
315        // X = (y, 0, 0)
316        let x =
317            VectorField::<3, 0, 0>::new(|coords| crate::vector_from_slice(&[coords[1], 0.0, 0.0]));
318
319        // Y = (0, x, 0)
320        let y =
321            VectorField::<3, 0, 0>::new(|coords| crate::vector_from_slice(&[0.0, coords[0], 0.0]));
322
323        let lie = LieDerivative::<3, 0, 0>::new();
324
325        // ∂_x Y = (0, 1, 0), ∂_y Y = (0, 0, 0)
326        // ∂_x X = (0, 0, 0), ∂_y X = (1, 0, 0)
327        // At point (2, 3, 0): X = (3, 0, 0), Y = (0, 2, 0)
328        // [X, Y]^x = X^x ∂_x Y^x + X^y ∂_y Y^x - (Y^x ∂_x X^x + Y^y ∂_y X^x)
329        //          = 3*0 + 0*0 - (0*0 + 2*1)
330        //          = 0 - 2 = -2
331        // [X, Y]^y = X^x ∂_x Y^y + X^y ∂_y Y^y - (Y^x ∂_x X^y + Y^y ∂_y X^y)
332        //          = 3*1 + 0*0 - (0*0 + 2*0)
333        //          = 3 - 0 = 3
334        let bracket = lie.bracket(&x, &y, &[2.0, 3.0, 0.0]);
335
336        assert!(
337            (bracket.vector_component(0) + 2.0).abs() < 1e-4,
338            "x-component should be -2, got {}",
339            bracket.vector_component(0)
340        );
341        assert!(
342            (bracket.vector_component(1) - 3.0).abs() < 1e-4,
343            "y-component should be 3, got {}",
344            bracket.vector_component(1)
345        );
346    }
347
348    #[test]
349    fn test_lie_bracket_antisymmetry() {
350        // [X, Y] = -[Y, X]
351        let x = VectorField::<3, 0, 0>::new(|coords| {
352            crate::vector_from_slice(&[-coords[1], coords[0], 0.0])
353        });
354        let y = VectorField::<3, 0, 0>::new(|coords| {
355            crate::vector_from_slice(&[coords[0], coords[1], 0.0])
356        });
357
358        let lie = LieDerivative::<3, 0, 0>::new();
359        let coords = [1.0, 2.0, 0.0];
360
361        let bracket_xy = lie.bracket(&x, &y, &coords);
362        let bracket_yx = lie.bracket(&y, &x, &coords);
363
364        for i in 0..3 {
365            let sum = bracket_xy.vector_component(i) + bracket_yx.vector_component(i);
366            assert!(
367                sum.abs() < 1e-4,
368                "Antisymmetry violated: [X,Y]_{} + [Y,X]_{} = {}",
369                i,
370                i,
371                sum
372            );
373        }
374    }
375}