clifford_codegen/spec/ir.rs
1//! Intermediate representation for parsed algebra specifications.
2//!
3//! These types represent the validated, processed form of a TOML specification.
4//! They contain all information needed for code generation.
5
6use std::collections::HashMap;
7
8/// Involution kind for norm computation.
9///
10/// Specifies which involution the algebra uses for its canonical norm.
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
12pub enum InvolutionKind {
13 /// Reverse: (-1)^(k(k-1)/2) for grade k.
14 ///
15 /// Default for most algebras (Euclidean, PGA, Minkowski).
16 /// Used for versor operations regardless of norm involution.
17 #[default]
18 Reverse,
19 /// Grade involution: (-1)^k for grade k.
20 ///
21 /// Used for split-complex/hyperbolic numbers.
22 GradeInvolution,
23 /// Clifford conjugate: composition of reverse and grade involution.
24 ///
25 /// Sign = (-1)^(k(k+1)/2) for grade k.
26 CliffordConjugate,
27}
28
29/// Norm configuration for an algebra.
30///
31/// Specifies which involution produces the "primary" norm for the algebra.
32/// The codegen generates `Involute` based on this setting.
33#[derive(Debug, Clone, Default)]
34pub struct NormSpec {
35 /// Which involution to use for primary norm computation.
36 ///
37 /// This determines what `involute()` returns for types in this algebra.
38 pub primary_involution: InvolutionKind,
39}
40
41/// Parsed algebra specification.
42///
43/// This is the main output of the parser, containing all information
44/// needed to generate code for an algebra.
45#[derive(Debug, Clone)]
46pub struct AlgebraSpec {
47 /// Algebra identifier (e.g., "euclidean3").
48 pub name: String,
49 /// Rust module path (e.g., "euclidean::dim3").
50 pub module_path: Option<String>,
51 /// Documentation string.
52 pub description: Option<String>,
53 /// Metric signature.
54 pub signature: SignatureSpec,
55 /// Norm configuration.
56 pub norm: NormSpec,
57 /// Custom blade name mappings (blade index -> name).
58 pub blade_names: HashMap<usize, String>,
59 /// Type definitions.
60 pub types: Vec<TypeSpec>,
61 /// Product specifications.
62 pub products: ProductsSpec,
63 /// Whether completeness checking is enabled.
64 ///
65 /// When `true`, the parser verified that all products between defined types
66 /// have matching output types.
67 pub complete: bool,
68}
69
70/// Metric signature specification.
71///
72/// Defines the basis vectors and their metric (how they square).
73#[derive(Debug, Clone)]
74pub struct SignatureSpec {
75 /// All basis vectors with their properties.
76 pub basis: Vec<BasisVector>,
77 /// Number of positive-square basis vectors.
78 pub p: usize,
79 /// Number of negative-square basis vectors.
80 pub q: usize,
81 /// Number of zero-square (degenerate) basis vectors.
82 pub r: usize,
83}
84
85impl SignatureSpec {
86 /// Total dimension (number of basis vectors).
87 #[inline]
88 pub fn dim(&self) -> usize {
89 self.p + self.q + self.r
90 }
91
92 /// Total number of blades (2^dim).
93 #[inline]
94 pub fn num_blades(&self) -> usize {
95 1 << self.dim()
96 }
97
98 /// Returns indices of basis vectors that square to +1 (positive metric).
99 pub fn positive_indices(&self) -> impl Iterator<Item = usize> + '_ {
100 self.basis.iter().filter(|b| b.metric == 1).map(|b| b.index)
101 }
102
103 /// Returns a vector of metric values indexed by basis index.
104 ///
105 /// This is useful for creating an `Algebra` with the correct per-basis metrics:
106 /// ```ignore
107 /// let metrics = signature.metrics_by_index();
108 /// let algebra = Algebra::from_metrics(metrics);
109 /// ```
110 pub fn metrics_by_index(&self) -> Vec<i8> {
111 let mut metrics = vec![0i8; self.dim()];
112 for bv in &self.basis {
113 metrics[bv.index] = bv.metric;
114 }
115 metrics
116 }
117
118 /// Returns indices of basis vectors that square to -1 (negative metric).
119 pub fn negative_indices(&self) -> impl Iterator<Item = usize> + '_ {
120 self.basis
121 .iter()
122 .filter(|b| b.metric == -1)
123 .map(|b| b.index)
124 }
125
126 /// Returns indices of basis vectors that square to 0 (degenerate/null).
127 ///
128 /// In PGA, this is typically the e0 basis (projective origin).
129 pub fn degenerate_indices(&self) -> impl Iterator<Item = usize> + '_ {
130 self.basis.iter().filter(|b| b.metric == 0).map(|b| b.index)
131 }
132
133 /// Returns true if this algebra has a degenerate metric (r > 0).
134 ///
135 /// Algebras with degenerate metrics (like PGA) have different normalization
136 /// behavior and require bulk/weight decomposition.
137 #[inline]
138 pub fn is_degenerate(&self) -> bool {
139 self.r > 0
140 }
141
142 /// Returns true if this algebra has an indefinite metric (q > 0).
143 ///
144 /// Algebras with indefinite metrics (like Minkowski) can have timelike,
145 /// spacelike, and lightlike vectors.
146 #[inline]
147 pub fn is_indefinite(&self) -> bool {
148 self.q > 0
149 }
150
151 /// Returns the signature type name derived from (p, q, r).
152 ///
153 /// This generates a generic name like `Cl3_0_1` instead of algebra-specific
154 /// names like "Projective3".
155 pub fn signature_type_name(&self) -> String {
156 format!("Cl{}_{}{}", self.p, self.q, self.r)
157 }
158}
159
160/// A single basis vector in the signature.
161#[derive(Debug, Clone)]
162pub struct BasisVector {
163 /// Name of the basis vector (e.g., "e1", "x").
164 pub name: String,
165 /// Index in the algebra (0-based).
166 pub index: usize,
167 /// Metric value: +1, -1, or 0.
168 pub metric: i8,
169}
170
171/// A type definition in the specification.
172#[derive(Debug, Clone)]
173pub struct TypeSpec {
174 /// Type name (e.g., "Rotor", "Vector").
175 pub name: String,
176 /// Grades contained in this type.
177 pub grades: Vec<usize>,
178 /// Documentation string.
179 pub description: Option<String>,
180 /// Fields in order (matching blade layout).
181 pub fields: Vec<FieldSpec>,
182 /// If this type aliases another (same storage layout).
183 pub alias_of: Option<String>,
184 /// Versor information (if this type is a versor).
185 ///
186 /// If present, this type can transform other elements via the sandwich
187 /// product: `X' = V * X * rev(V)`.
188 pub versor: Option<VersorSpec>,
189 /// Whether this type is sparse (uses only a subset of blades within its grades).
190 ///
191 /// Sparse types have explicit blade mappings and don't use all blades of their grades.
192 /// For example, a Line in CGA uses only 6 of the 10 grade-3 blades.
193 pub is_sparse: bool,
194 /// Types that can be transformed via inverse sandwich product.
195 ///
196 /// This allows non-versor types (like Circle in CGA) to perform
197 /// inverse sandwich transformations: `X' = T * X * T⁻¹`.
198 ///
199 /// For versors, this is typically empty (uses auto-inferred targets).
200 /// For blades like Circle, this explicitly lists valid targets.
201 pub inverse_sandwich_targets: Vec<String>,
202}
203
204/// Versor specification for a type.
205///
206/// Versors are elements that can transform other elements via the sandwich
207/// product. This includes rotors (rotations), motors (rigid motions),
208/// and flectors (reflections).
209#[derive(Debug, Clone)]
210pub struct VersorSpec {
211 /// Whether this versor has unit norm (`V * rev(V) = 1`).
212 ///
213 /// Unit versors have simpler sandwich formulas since `V⁻¹ = rev(V)`.
214 pub is_unit: bool,
215 /// Types that this versor can transform via sandwich product.
216 ///
217 /// Empty means auto-detect based on grade compatibility.
218 pub sandwich_targets: Vec<String>,
219}
220
221/// A field in a type.
222#[derive(Debug, Clone)]
223pub struct FieldSpec {
224 /// Field name (e.g., "x", "xy").
225 pub name: String,
226 /// Blade index this field holds (canonical bitmask form).
227 pub blade_index: usize,
228 /// Grade of the blade.
229 pub grade: usize,
230 /// Sign relative to canonical blade ordering (+1 or -1).
231 ///
232 /// When a blade is specified in non-canonical order (e.g., "e20" instead of "e02"),
233 /// this sign captures the parity of the permutation needed to reach canonical form.
234 /// For example, e20 = -e02, so sign = -1.
235 ///
236 /// This sign is applied during product generation to ensure correct results.
237 pub sign: i8,
238}
239
240/// Product specifications for all product types.
241///
242/// Product naming follows [Rigid Geometric Algebra](https://rigidgeometricalgebra.org/) conventions:
243/// - `∧` = wedge (exterior product)
244/// - `∨` = antiwedge (regressive product)
245/// - `★` = dual (bulk dual)
246/// - `☆` = antidual (weight dual)
247/// - `•` = dot (metric inner product, same-grade only)
248/// - `⊚` = antidot (metric antiproduct inner, same-antigrade only)
249#[derive(Debug, Clone, Default)]
250pub struct ProductsSpec {
251 /// Geometric product entries (used for Mul operator on versors).
252 pub geometric: Vec<ProductEntry>,
253 /// Wedge product entries (∧, exterior, grade-raising).
254 pub wedge: Vec<ProductEntry>,
255 /// Left contraction entries.
256 pub left_contraction: Vec<ProductEntry>,
257 /// Right contraction entries.
258 pub right_contraction: Vec<ProductEntry>,
259 /// Antiwedge product entries (∨, regressive/meet).
260 pub antiwedge: Vec<ProductEntry>,
261 /// Scalar product entries.
262 pub scalar: Vec<ProductEntry>,
263 /// Antigeometric product entries (used for antisandwich computation).
264 pub antigeometric: Vec<ProductEntry>,
265 /// Antiscalar product entries.
266 pub antiscalar: Vec<ProductEntry>,
267 /// Bulk contraction entries (a ∨ b★).
268 pub bulk_contraction: Vec<ProductEntry>,
269 /// Weight contraction entries (a ∨ b☆).
270 pub weight_contraction: Vec<ProductEntry>,
271 /// Bulk expansion entries (a ∧ b★).
272 pub bulk_expansion: Vec<ProductEntry>,
273 /// Weight expansion entries (a ∧ b☆).
274 pub weight_expansion: Vec<ProductEntry>,
275 /// Dot product entries (•, metric inner, same-grade only, returns scalar).
276 pub dot: Vec<ProductEntry>,
277 /// Antidot product entries (⊚, metric antiproduct inner, same-antigrade only, returns scalar).
278 pub antidot: Vec<ProductEntry>,
279 /// Projection entries: b ∨ (a ∧ b☆).
280 pub project: Vec<ProductEntry>,
281 /// Antiprojection entries: b ∧ (a ∨ b☆).
282 pub antiproject: Vec<ProductEntry>,
283}
284
285/// A single product entry specifying lhs × rhs → output.
286#[derive(Debug, Clone)]
287pub struct ProductEntry {
288 /// Left-hand side type name (e.g., "Vector", "UnitRotor").
289 pub lhs: String,
290 /// Right-hand side type name.
291 pub rhs: String,
292 /// Output type name.
293 pub output: String,
294 /// Whether the output is a constrained type (uses new_unchecked).
295 pub output_constrained: bool,
296}
297
298/// Wrapper constraint kinds for constraint simplification.
299///
300/// These represent the different normalization and constraint wrappers
301/// that can be applied to geometric types. Each wrapper has specific
302/// algebraic constraints that can be used during Groebner basis simplification.
303#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
304pub enum WrapperKind {
305 /// Unit wrapper: `norm() == 1` (Euclidean norm).
306 ///
307 /// Constraint: `norm_squared - 1 = 0`
308 Unit,
309 /// Bulk wrapper: `bulk_norm() == 1` (PGA versors).
310 ///
311 /// Constraint: `bulk_norm_squared - 1 = 0`
312 Bulk,
313 /// Unitized wrapper: `weight_norm() == 1` (PGA standard form).
314 ///
315 /// Constraint: `weight_norm_squared - 1 = 0`
316 Unitized,
317 /// Ideal wrapper: `weight_norm() ≈ 0` (PGA elements at infinity).
318 ///
319 /// Constraint: each weight component = 0
320 Ideal,
321 /// Proper wrapper: timelike, `|norm²| == 1` (Minkowski 4-velocities).
322 ///
323 /// Constraint: `norm_squared - 1 = 0`
324 Proper,
325 /// Spacelike wrapper: spacelike, `|norm²| == 1` (Minkowski spatial).
326 ///
327 /// Constraint: `norm_squared + 1 = 0`
328 Spacelike,
329 /// Null wrapper: `norm_squared ≈ 0` (Minkowski lightlike).
330 ///
331 /// Constraint: `norm_squared = 0`
332 Null,
333}