atlas_embeddings/foundations/primitives.rs
1//! # Chapter 0.1: Primitive Concepts
2//!
3//! We begin by defining the most basic mathematical objects needed for our construction.
4//! No prior knowledge is assumed beyond elementary set theory and basic programming concepts.
5//!
6//! This chapter establishes:
7//! - What is a graph?
8//! - What is exact arithmetic?
9//! - What is a group?
10//! - What are vectors and inner products?
11//!
12//! Each concept is accompanied by minimal Rust implementations that serve as
13//! executable definitions.
14
15// # 0.1.1 Graphs
16
17//! ## 0.1.1 Graphs
18//!
19//! A **graph** is one of the most fundamental structures in mathematics and computer science.
20//!
21//! ### Definition 0.1.1 (Graph)
22//!
23//! A graph G = (V, E) consists of:
24//! - A finite set **V** of **vertices** (also called nodes)
25//! - A set **E ⊆ V × V** of **edges** (pairs of vertices)
26//!
27//! We write `v ~ w` when (v,w) ∈ E, read "v is adjacent to w".
28//!
29//! ### Properties
30//!
31//! - **Undirected**: If v ~ w, then w ~ v (edges have no direction)
32//! - **Simple**: No self-loops (v ≁ v) and no multiple edges
33//! - **Finite**: Both V and E are finite sets
34//!
35//! ### Example 0.1.1: Triangle Graph
36//!
37//! The triangle graph has 3 vertices {A, B, C} with edges forming a cycle:
38//!
39//! ```text
40//! A
41//! / \
42//! B---C
43//! ```
44//!
45//! In set notation: V = {A, B, C}, E = {(A,B), (B,C), (C,A)}.
46//!
47//! ### Computational Representation
48//!
49//! We represent graphs using **adjacency lists**: for each vertex, store its neighbors.
50
51use std::collections::HashSet;
52
53/// A simple undirected graph with vertices labeled by generic type V.
54///
55/// **Implementation note**: This is a minimal educational implementation.
56/// The Atlas uses a more specialized representation in [`crate::atlas`].
57///
58/// # Examples
59///
60/// ```
61/// use atlas_embeddings::foundations::primitives::SimpleGraph;
62///
63/// // Create triangle graph
64/// let mut g = SimpleGraph::new();
65/// g.add_edge(0, 1);
66/// g.add_edge(1, 2);
67/// g.add_edge(2, 0);
68///
69/// assert_eq!(g.vertex_count(), 3);
70/// assert_eq!(g.edge_count(), 3);
71/// assert!(g.is_adjacent(0, 1));
72/// assert!(g.is_adjacent(1, 0)); // Undirected
73/// ```
74#[derive(Debug, Clone)]
75pub struct SimpleGraph {
76 /// Adjacency lists: for each vertex, store its neighbors
77 adjacency: Vec<HashSet<usize>>,
78}
79
80impl SimpleGraph {
81 /// Create an empty graph with no vertices.
82 #[must_use]
83 pub const fn new() -> Self {
84 Self { adjacency: Vec::new() }
85 }
86
87 /// Add a vertex to the graph, returning its index.
88 ///
89 /// Vertices are numbered sequentially: 0, 1, 2, ...
90 pub fn add_vertex(&mut self) -> usize {
91 let idx = self.adjacency.len();
92 self.adjacency.push(HashSet::new());
93 idx
94 }
95
96 /// Add an undirected edge between vertices u and v.
97 ///
98 /// Automatically adds vertices if they don't exist yet.
99 pub fn add_edge(&mut self, u: usize, v: usize) {
100 // Ensure both vertices exist
101 while self.adjacency.len() <= u.max(v) {
102 self.add_vertex();
103 }
104
105 // Add edge in both directions (undirected)
106 self.adjacency[u].insert(v);
107 self.adjacency[v].insert(u);
108 }
109
110 /// Check if vertices u and v are adjacent.
111 #[must_use]
112 pub fn is_adjacent(&self, u: usize, v: usize) -> bool {
113 if u >= self.adjacency.len() || v >= self.adjacency.len() {
114 return false;
115 }
116 self.adjacency[u].contains(&v)
117 }
118
119 /// Get the degree of vertex v (number of neighbors).
120 ///
121 /// **Definition**: The **degree** of a vertex is the number of edges incident to it.
122 #[must_use]
123 pub fn degree(&self, v: usize) -> usize {
124 if v >= self.adjacency.len() {
125 return 0;
126 }
127 self.adjacency[v].len()
128 }
129
130 /// Get the number of vertices in the graph.
131 #[must_use]
132 pub fn vertex_count(&self) -> usize {
133 self.adjacency.len()
134 }
135
136 /// Get the number of edges in the graph.
137 ///
138 /// Since the graph is undirected, we count each edge once.
139 #[must_use]
140 pub fn edge_count(&self) -> usize {
141 self.adjacency.iter().map(HashSet::len).sum::<usize>() / 2
142 }
143
144 /// Get the neighbors of vertex v.
145 #[must_use]
146 pub fn neighbors(&self, v: usize) -> Vec<usize> {
147 if v >= self.adjacency.len() {
148 return Vec::new();
149 }
150 self.adjacency[v].iter().copied().collect()
151 }
152}
153
154impl Default for SimpleGraph {
155 fn default() -> Self {
156 Self::new()
157 }
158}
159
160// # 0.1.2 Exact Arithmetic
161
162// ## 0.1.2 Exact Arithmetic
163//
164// In this work, we use **exact arithmetic** exclusively. No floating-point
165// calculations are permitted.
166//
167// ### Principle 0.1.2 (Exactness)
168//
169// All numerical computations use exact representations:
170// - **Integers**: ℤ represented as `i64` (64-bit signed integers)
171// - **Rationals**: ℚ represented as `Ratio<i64>` (pairs of integers p/q)
172// - **Half-integers**: ½ℤ represented as `HalfInteger` (multiples of 1/2)
173//
174// **NO floating-point arithmetic** is used anywhere in this crate.
175//
176// ### Why Exactness?
177//
178// 1. **Mathematical correctness**: No rounding errors corrupt our results
179// 2. **Reproducibility**: Same results on all platforms (no architecture-dependent floats)
180// 3. **Verifiability**: Equality is decidable (can test if a = b exactly)
181// 4. **Peer review**: Reviewers can verify exact values
182//
183// ### Example 0.1.2: Rational Arithmetic
184//
185// ```
186// use num_rational::Ratio;
187//
188// // Exact: 1/3 + 1/6 = 1/2
189// let a = Ratio::new(1, 3);
190// let b = Ratio::new(1, 6);
191// let c = a + b;
192// assert_eq!(c, Ratio::new(1, 2));
193//
194// // Contrast with floating point:
195// // 0.333... + 0.166... ≈ 0.5 (approximation)
196// ```
197//
198// ### Mathematical Perspective
199//
200// Exact arithmetic aligns with mathematical practice: when a mathematician
201// writes "√2", they mean the exact value, not a decimal approximation like 1.41421356.
202// Similarly, our code manipulates exact values.
203//
204// ### Computer Science Perspective
205//
206// Exact arithmetic trades performance for correctness. Floating-point operations
207// are faster but introduce errors. For mathematical research where correctness
208// is paramount, this tradeoff favors exactness.
209//
210// ### Type Aliases
211//
212// For convenience, we re-export commonly used exact types:
213
214pub use crate::arithmetic::{HalfInteger, Rational, Vector8};
215
216// # 0.1.3 Groups
217
218// ## 0.1.3 Groups
219//
220// A **group** is a fundamental algebraic structure encoding the concept of symmetry.
221//
222// ### Definition 0.1.3 (Group)
223//
224// A group (G, ·, e) consists of:
225// - A set **G**
226// - A binary operation **· : G × G → G** (multiplication)
227// - An identity element **e ∈ G**
228//
229// satisfying:
230//
231// 1. **Associativity**: (a · b) · c = a · (b · c) for all a,b,c ∈ G
232// 2. **Identity**: e · a = a · e = a for all a ∈ G
233// 3. **Inverses**: For each a ∈ G, there exists a⁻¹ ∈ G with a · a⁻¹ = e
234//
235// ### Example 0.1.3 (Integers under Addition)
236//
237// The integers ℤ form a group under addition:
238// - Set: G = ℤ = {..., -2, -1, 0, 1, 2, ...}
239// - Operation: · is ordinary addition +
240// - Identity: e = 0 (since 0 + a = a)
241// - Inverses: a⁻¹ = -a (since a + (-a) = 0)
242//
243// ### Example 0.1.4 (Klein Four-Group)
244//
245// The Klein four-group V₄ has 4 elements {e, a, b, c} with multiplication:
246//
247// ```text
248// · | e a b c
249// ----+------------
250// e | e a b c
251// a | a e c b
252// b | b c e a
253// c | c b a e
254// ```
255//
256// Every element is its own inverse: a² = b² = c² = e.
257//
258// **Physical interpretation**: The Klein group describes the symmetries of a
259// rectangle (rotations by 0°, 180° and reflections through both axes).
260//
261// This group will appear in our construction of G₂ in Chapter 4.
262
263/// Klein four-group: V₄ = {e, a, b, c} with every element its own inverse.
264///
265/// Used in the product construction of G₂.
266///
267/// # Examples
268///
269/// ```
270/// use atlas_embeddings::foundations::primitives::KleinElement;
271///
272/// let a = KleinElement::A;
273/// let b = KleinElement::B;
274///
275/// // Group operation
276/// let c = a.multiply(b);
277/// assert_eq!(c, KleinElement::C);
278///
279/// // Every element is its own inverse
280/// assert_eq!(a.multiply(a), KleinElement::Identity);
281/// ```
282#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
283pub enum KleinElement {
284 /// Identity element e
285 Identity,
286 /// Element a
287 A,
288 /// Element b
289 B,
290 /// Element c = a·b
291 C,
292}
293
294impl KleinElement {
295 /// Group multiplication in V₄.
296 #[must_use]
297 pub const fn multiply(self, other: Self) -> Self {
298 use KleinElement::{Identity, A, B, C};
299 match (self, other) {
300 (Identity, x) | (x, Identity) => x,
301 (A, A) | (B, B) | (C, C) => Identity,
302 (A, B) | (B, A) => C,
303 (A, C) | (C, A) => B,
304 (B, C) | (C, B) => A,
305 }
306 }
307
308 /// Get the inverse element (every element is its own inverse).
309 #[must_use]
310 pub const fn inverse(self) -> Self {
311 self
312 }
313}
314
315// # 0.1.4 Vectors and Inner Products
316
317// ## 0.1.4 Vectors and Inner Products
318//
319// Much of our work takes place in 8-dimensional Euclidean space ℝ⁸.
320//
321// ### Definition 0.1.4 (Vector Space)
322//
323// A **vector space** over ℝ is a set V with operations:
324// - **Addition**: + : V × V → V
325// - **Scalar multiplication**: · : ℝ × V → V
326//
327// satisfying standard axioms (associativity, distributivity, etc.).
328//
329// **For our purposes**: We work with V = ℝ⁸, vectors are 8-tuples of real numbers.
330//
331// ### Definition 0.1.5 (Inner Product)
332//
333// An **inner product** on a vector space V is a function ⟨·,·⟩ : V × V → ℝ
334// satisfying:
335//
336// 1. **Symmetry**: ⟨u, v⟩ = ⟨v, u⟩
337// 2. **Linearity**: ⟨au + bv, w⟩ = a⟨u,w⟩ + b⟨v,w⟩
338// 3. **Positive-definite**: ⟨v, v⟩ ≥ 0, with equality iff v = 0
339//
340// ### The Standard Inner Product on ℝ⁸
341//
342// For vectors u = (u₁, ..., u₈) and v = (v₁, ..., v₈):
343//
344// $$ \langle u, v \rangle = u_1 v_1 + u_2 v_2 + \cdots + u_8 v_8 $$
345//
346// ### Definition 0.1.6 (Norm)
347//
348// The **norm** (or length) of a vector v is:
349//
350// $$ \|v\| = \sqrt{\langle v, v \rangle} $$
351//
352// We often work with **norm squared** to avoid square roots:
353//
354// $$ \|v\|^2 = \langle v, v \rangle = v_1^2 + \cdots + v_8^2 $$
355//
356// ### Example 0.1.5: Computing Norms
357//
358// ```
359// use atlas_embeddings::arithmetic::Vector8;
360// use num_rational::Ratio;
361//
362// // Integer vector (1,1,0,0,0,0,0,0)
363// let v = Vector8::new([
364// Ratio::from_integer(1),
365// Ratio::from_integer(1),
366// Ratio::from_integer(0),
367// Ratio::from_integer(0),
368// Ratio::from_integer(0),
369// Ratio::from_integer(0),
370// Ratio::from_integer(0),
371// Ratio::from_integer(0),
372// ]);
373//
374// // Norm squared: 1² + 1² = 2
375// assert_eq!(v.norm_squared(), Ratio::from_integer(2));
376// ```
377//
378// **Significance**: All E₈ roots have norm² = 2, making this the fundamental
379// scale in our construction.
380
381// ## 0.1.5 Summary
382//
383// We have established the primitive concepts needed for our construction:
384//
385// 1. **Graphs**: Vertices and edges, adjacency
386// 2. **Exact arithmetic**: Rationals, no floating point
387// 3. **Groups**: Algebraic structures encoding symmetry
388// 4. **Vectors**: 8-dimensional space with inner products
389//
390// These are the building blocks. In the next section, we introduce the
391// action functional that generates the Atlas.
392//
393// ---
394//
395// **Navigation**:
396// - Next: [§0.2 Action Functionals](super::action)
397// - Up: [Chapter 0: Foundations](super)
398
399#[cfg(test)]
400mod tests {
401 use super::*;
402
403 #[test]
404 fn test_simple_graph_triangle() {
405 let mut g = SimpleGraph::new();
406 g.add_edge(0, 1);
407 g.add_edge(1, 2);
408 g.add_edge(2, 0);
409
410 assert_eq!(g.vertex_count(), 3);
411 assert_eq!(g.edge_count(), 3);
412
413 // Check all edges exist (both directions)
414 assert!(g.is_adjacent(0, 1));
415 assert!(g.is_adjacent(1, 0));
416 assert!(g.is_adjacent(1, 2));
417 assert!(g.is_adjacent(2, 1));
418 assert!(g.is_adjacent(2, 0));
419 assert!(g.is_adjacent(0, 2));
420
421 // Check degrees
422 assert_eq!(g.degree(0), 2);
423 assert_eq!(g.degree(1), 2);
424 assert_eq!(g.degree(2), 2);
425 }
426
427 #[test]
428 fn test_simple_graph_no_self_loops() {
429 let mut g = SimpleGraph::new();
430 g.add_vertex();
431
432 // Graph should not have self-loops
433 assert!(!g.is_adjacent(0, 0));
434 }
435
436 #[test]
437 fn test_klein_group_multiplication() {
438 use KleinElement::{Identity, A, B, C};
439
440 // Identity laws
441 assert_eq!(Identity.multiply(A), A);
442 assert_eq!(A.multiply(Identity), A);
443
444 // Self-inverse property
445 assert_eq!(A.multiply(A), Identity);
446 assert_eq!(B.multiply(B), Identity);
447 assert_eq!(C.multiply(C), Identity);
448
449 // Products
450 assert_eq!(A.multiply(B), C);
451 assert_eq!(B.multiply(A), C);
452 assert_eq!(A.multiply(C), B);
453 assert_eq!(B.multiply(C), A);
454 }
455
456 #[test]
457 fn test_klein_group_associativity() {
458 use KleinElement::{A, B, C};
459
460 // (a·b)·c = a·(b·c)
461 let left = A.multiply(B).multiply(C);
462 let right = A.multiply(B.multiply(C));
463 assert_eq!(left, right);
464 }
465}