amari_core/
unicode_ops.rs

1//! Unicode mathematical operations for Amari
2//!
3//! This module provides Unicode-inspired macro aliases for geometric algebra operations,
4//! making mathematical expressions more readable and intuitive while preserving all existing APIs.
5//!
6//! # Supported Unicode-Inspired Operators
7//!
8//! ## Binary Operations
9//! - `geo!(a, b)`: Geometric product (⊗ - tensor product symbol)
10//! - `wedge!(a, b)`: Wedge/outer product (∧ - logical AND symbol)  
11//! - `dot!(a, b)`: Inner product (• - bullet operator)
12//! - `lcon!(a, b)`: Left contraction (⌟ - bottom left corner)
13//! - `rcon!(a, b)`: Right contraction (⌞ - bottom right corner)
14//!
15//! ## Unary Operations
16//! - `dual!(a)`: Hodge dual (⋆ - star operator)
17//! - `rev!(a)`: Reverse/conjugate († - dagger)
18//! - `norm!(a)`: Magnitude/norm (‖·‖ - double vertical bars)
19//!
20//! ## Grade Operations
21//! - `grade!(a, k)`: Grade projection (⟨·⟩ₖ - angle brackets with subscript)
22//!
23//! # Examples
24//!
25//! ```rust
26//! use amari_core::{Multivector, Vector, Bivector};
27//! use amari_core::{geo, wedge, dot, rev, dual, norm};
28//!
29//! let a = Vector::<3, 0, 0>::e1();
30//! let b = Vector::<3, 0, 0>::e2();
31//!
32//! // Traditional syntax
33//! let geometric = a.geometric_product(&b);
34//! let wedge_traditional = a.outer_product(&b);
35//! let inner = a.inner_product(&b);
36//!
37//! // Unicode-inspired DSL syntax
38//! let geometric2 = geo!(a, b);      // ⊗
39//! let wedge2 = wedge!(a, b);        // ∧
40//! let inner2 = dot!(a, b);          // •
41//!
42//! // Unary operations
43//! let reversed = rev!(a);          // †
44//! let hodge_dual = dual!(a);       // ⋆
45//! let magnitude = norm!(a);        // ‖·‖
46//! ```
47
48/// Geometric product: a ⊗ b
49///
50/// The fundamental product in geometric algebra that combines
51/// both inner and outer products. Also known as the Clifford product.
52#[macro_export]
53macro_rules! geo {
54    ($a:expr, $b:expr) => {
55        $a.geometric_product(&$b)
56    };
57}
58
59/// Wedge/outer product: a ∧ b
60///
61/// Creates higher-grade elements from lower-grade ones.
62/// Anticommutative: a ∧ b = -(b ∧ a)
63#[macro_export]
64macro_rules! wedge {
65    ($a:expr, $b:expr) => {
66        $a.outer_product(&$b)
67    };
68}
69
70/// Inner product: a • b
71///
72/// Symmetric product that reduces grade.
73/// Commutative for vectors: a • b = b • a
74#[macro_export]
75macro_rules! dot {
76    ($a:expr, $b:expr) => {
77        $a.inner_product(&$b)
78    };
79}
80
81/// Left contraction: a ⌟ b
82///
83/// Generalized inner product where the grade of the result
84/// is |grade(b) - grade(a)|
85#[macro_export]
86macro_rules! lcon {
87    ($a:expr, $b:expr) => {
88        $a.left_contraction(&$b)
89    };
90}
91
92/// Right contraction: a ⌞ b  
93///
94/// Generalized inner product where the grade of the result
95/// is |grade(a) - grade(b)|
96#[macro_export]
97macro_rules! rcon {
98    ($a:expr, $b:expr) => {
99        $a.right_contraction(&$b)
100    };
101}
102
103/// Hodge dual: ⋆a
104///
105/// Maps k-vectors to (n-k)-vectors in n-dimensional space.
106/// Essential for electromagnetic field theory and differential forms.
107#[macro_export]
108macro_rules! dual {
109    ($a:expr) => {
110        $a.hodge_dual()
111    };
112}
113
114/// Reverse/conjugate: a†
115///
116/// Reverses the order of basis vectors in each term.
117/// Important for defining magnitudes and inverses.
118#[macro_export]
119macro_rules! rev {
120    ($a:expr) => {
121        $a.reverse()
122    };
123}
124
125/// Grade projection: ⟨a⟩ₖ
126///
127/// Extracts the k-grade part of a multivector.
128/// Grade 0 = scalar, 1 = vector, 2 = bivector, etc.
129#[macro_export]
130macro_rules! grade {
131    ($a:expr, $k:expr) => {
132        $a.grade_projection($k)
133    };
134}
135
136/// Magnitude/norm: ‖a‖
137///
138/// Euclidean magnitude of the multivector.
139/// Computed as sqrt(a† ⊗ a) for the scalar part.
140#[macro_export]
141macro_rules! norm {
142    ($a:expr) => {
143        $a.magnitude()
144    };
145}
146
147/// Tropical addition (max): a ⊕ b
148#[macro_export]
149macro_rules! trop_add {
150    ($a:expr, $b:expr) => {
151        $a.tropical_add(&$b)
152    };
153}
154
155/// Tropical multiplication (addition): a ⊙ b  
156#[macro_export]
157macro_rules! trop_mul {
158    ($a:expr, $b:expr) => {
159        $a.tropical_mul(&$b)
160    };
161}
162
163/// Commutator: [a, b] = (a⊗b - b⊗a)/2
164#[macro_export]
165macro_rules! commutator {
166    ($a:expr, $b:expr) => {{
167        let ab = geo!($a, $b);
168        let ba = geo!($b, $a);
169        (ab - ba) * 0.5
170    }};
171}
172
173/// Anticommutator: {a, b} = (a⊗b + b⊗a)/2  
174#[macro_export]
175macro_rules! anticommutator {
176    ($a:expr, $b:expr) => {{
177        let ab = geo!($a, $b);
178        let ba = geo!($b, $a);
179        (ab + ba) * 0.5
180    }};
181}
182
183/// Squared magnitude: ‖a‖²
184#[macro_export]
185macro_rules! norm_squared {
186    ($a:expr) => {
187        $a.norm_squared()
188    };
189}
190
191/// Unit vector: â = a/‖a‖
192#[macro_export]
193macro_rules! unit {
194    ($a:expr) => {{
195        $a.normalize().unwrap_or($a.clone())
196    }};
197}
198
199#[cfg(test)]
200mod tests {
201    use crate::{basis::MultivectorBuilder, Vector};
202    use approx::assert_relative_eq;
203
204    #[test]
205    fn test_unicode_geometric_product() {
206        let a = Vector::<3, 0, 0>::e1();
207        let b = Vector::<3, 0, 0>::e2();
208
209        // Unicode-inspired syntax
210        let unicode_result = geo!(a, b);
211
212        // Traditional syntax
213        let traditional_result = a.geometric_product(&b);
214
215        // Should be identical
216        for i in 0..8 {
217            assert_relative_eq!(unicode_result.get(i), traditional_result.get(i));
218        }
219    }
220
221    #[test]
222    fn test_unicode_wedge_product() {
223        let e1 = Vector::<3, 0, 0>::e1();
224        let e2 = Vector::<3, 0, 0>::e2();
225
226        let wedge_result = wedge!(e1, e2);
227        let traditional = e1.outer_product(&e2);
228
229        // Should produce e12 bivector
230        assert_relative_eq!(wedge_result.bivector_part().magnitude(), 1.0);
231
232        // Should match traditional syntax
233        for i in 0..8 {
234            assert_relative_eq!(wedge_result.get(i), traditional.get(i));
235        }
236    }
237
238    #[test]
239    fn test_unicode_inner_product() {
240        let a = Vector::<3, 0, 0>::from_components(3.0, 4.0, 0.0);
241        let b = Vector::<3, 0, 0>::from_components(1.0, 1.0, 0.0);
242
243        let inner_result = dot!(a, b);
244        let traditional = a.inner_product(&b);
245
246        // Should be scalar: 3*1 + 4*1 = 7
247        assert_relative_eq!(inner_result.scalar_part(), 7.0);
248
249        // Should match traditional
250        for i in 0..8 {
251            assert_relative_eq!(inner_result.get(i), traditional.get(i));
252        }
253    }
254
255    #[test]
256    fn test_unicode_unary_operations() {
257        let mv = MultivectorBuilder::<3, 0, 0>::new()
258            .scalar(1.0)
259            .e(1, 2.0)
260            .e(3, 3.0) // e12
261            .build();
262
263        // Test reverse
264        let reversed = rev!(mv);
265        let traditional_reverse = mv.reverse();
266
267        for i in 0..8 {
268            assert_relative_eq!(reversed.get(i), traditional_reverse.get(i));
269        }
270
271        // Test magnitude
272        let magnitude = norm!(mv);
273        let traditional_magnitude = mv.magnitude();
274
275        assert_relative_eq!(magnitude, traditional_magnitude);
276    }
277
278    #[test]
279    fn test_unicode_grade_projection() {
280        let mv = MultivectorBuilder::<3, 0, 0>::new()
281            .scalar(1.0)
282            .e(1, 2.0)
283            .e(2, 3.0)
284            .e(3, 4.0) // e12
285            .build();
286
287        // Test grade projections
288        let grade0 = grade!(mv, 0);
289        let grade1 = grade!(mv, 1);
290        let grade2 = grade!(mv, 2);
291
292        let traditional_grade0 = mv.grade_projection(0);
293        let traditional_grade1 = mv.grade_projection(1);
294        let traditional_grade2 = mv.grade_projection(2);
295
296        // Should match traditional syntax
297        for i in 0..8 {
298            assert_relative_eq!(grade0.get(i), traditional_grade0.get(i));
299            assert_relative_eq!(grade1.get(i), traditional_grade1.get(i));
300            assert_relative_eq!(grade2.get(i), traditional_grade2.get(i));
301        }
302    }
303
304    #[test]
305    fn test_extended_unicode_operations() {
306        let a = Vector::<3, 0, 0>::e1();
307        let b = Vector::<3, 0, 0>::e2();
308
309        // Test commutator [a,b]
310        let commutator_result = commutator!(a, b);
311
312        // For orthogonal vectors, [e1,e2] should be related to e12
313        assert!(commutator_result.bivector_part().magnitude() > 0.0);
314
315        // Test unit vector
316        let v = Vector::<3, 0, 0>::from_components(3.0, 4.0, 0.0);
317        let unit_result = unit!(v);
318
319        // Should be unit magnitude
320        assert_relative_eq!(unit_result.norm(), 1.0, epsilon = 1e-10);
321
322        // Test squared magnitude
323        let squared_mag = norm_squared!(v);
324        let expected = 3.0 * 3.0 + 4.0 * 4.0;
325        assert_relative_eq!(squared_mag, expected);
326    }
327
328    #[test]
329    fn test_unicode_mathematical_identities() {
330        let a = Vector::<3, 0, 0>::from_components(1.0, 2.0, 3.0);
331        let b = Vector::<3, 0, 0>::from_components(4.0, 5.0, 6.0);
332
333        // Test geometric product decomposition: a⊗b = a•b + a∧b
334        let geometric = geo!(a, b);
335        let inner = dot!(a, b);
336        let outer = wedge!(a, b);
337        let decomposed = inner + outer;
338
339        for i in 0..8 {
340            assert_relative_eq!(geometric.get(i), decomposed.get(i), epsilon = 1e-10);
341        }
342
343        // Test reverse property: (a⊗b)† = b†⊗a†
344        let left_side = rev!(geometric);
345        let right_side = geo!(rev!(b), rev!(a));
346
347        for i in 0..8 {
348            assert_relative_eq!(left_side.get(i), right_side.get(i), epsilon = 1e-10);
349        }
350    }
351}