# ADR-006: GNN-Enhanced CSI Pattern Recognition ## Status Partially realized in [ADR-023](ADR-023-trained-densepose-model-ruvector-pipeline.md); extended by [ADR-027](ADR-027-cross-environment-domain-generalization.md) > **Note:** ADR-023 implements a 2-layer GCN on the COCO skeleton graph for spatial reasoning. ADR-027 (MERIDIAN) adds domain-adversarial regularization via a gradient reversal layer that forces the GCN to learn environment-invariant graph features, shedding room-specific multipath patterns. ## Date 2026-02-28 ## Context ### Limitations of Independent Vector Search ADR-004 introduces HNSW-based similarity search for CSI pattern matching. While HNSW provides fast nearest-neighbor retrieval, it treats each vector independently. CSI patterns, however, have rich relational structure: 1. **Temporal adjacency**: CSI frames captured 10ms apart are more related than frames 10s apart. Sequential patterns reveal motion trajectories. 2. **Spatial correlation**: CSI readings from adjacent subcarriers are highly correlated due to frequency proximity. Antenna pairs capture different spatial perspectives. 3. **Cross-session similarity**: The "walking to kitchen" pattern from Tuesday should inform Wednesday's recognition, but the environment baseline may have shifted. 4. **Multi-person entanglement**: When multiple people are present, CSI patterns are superpositions. Disentangling requires understanding which pattern fragments co-occur. Standard HNSW cannot capture these relationships. Each query returns neighbors based solely on vector distance, ignoring the graph structure of how patterns relate to each other. ### RuVector's GNN Enhancement RuVector implements a Graph Neural Network layer that sits on top of the HNSW index: ``` Standard HNSW: Query → Distance-based neighbors → Results GNN-Enhanced: Query → Distance-based neighbors → GNN refinement → Improved results ``` The GNN performs three operations in <1ms: 1. **Message passing**: Each node aggregates information from its HNSW neighbors 2. **Attention weighting**: Multi-head attention identifies which neighbors are most relevant for the current query context 3. **Representation update**: Node embeddings are refined based on neighborhood context Additionally, **temporal learning** tracks query sequences to discover: - Vectors that frequently appear together in sessions - Temporal ordering patterns (A usually precedes B) - Session context that changes relevance rankings ## Decision We will integrate RuVector's GNN layer to enhance CSI pattern recognition with three core capabilities: relational search, temporal sequence modeling, and multi-person disentanglement. ### GNN Architecture for CSI ``` ┌─────────────────────────────────────────────────────────────────────┐ │ GNN-Enhanced CSI Pattern Graph │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ Layer 1: HNSW Spatial Graph │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ Nodes = CSI feature vectors │ │ │ │ Edges = HNSW neighbor connections (distance-based) │ │ │ │ Node features = [amplitude | phase | doppler | PSD] │ │ │ └───────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ Layer 2: Temporal Edges │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ Additional edges between temporally adjacent vectors │ │ │ │ Edge weight = 1/Δt (closer in time = stronger) │ │ │ │ Direction = causal (past → future) │ │ │ └───────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ Layer 3: GNN Message Passing (2 rounds) │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ Round 1: h_i = σ(W₁·h_i + Σⱼ α_ij · W₂·h_j) │ │ │ │ Round 2: h_i = σ(W₃·h_i + Σⱼ α'_ij · W₄·h_j) │ │ │ │ α_ij = softmax(LeakyReLU(a^T[W·h_i || W·h_j])) │ │ │ │ (Graph Attention Network mechanism) │ │ │ └───────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ Layer 4: Refined Representations │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ Updated vectors incorporate neighborhood context │ │ │ │ Re-rank search results using refined distances │ │ │ └───────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────┘ ``` ### Three Integration Modes #### Mode 1: Query-Time Refinement (Default) GNN refines HNSW results after retrieval. No modifications to stored vectors. ```rust pub struct GnnQueryRefiner { /// GNN weights (small: ~50K parameters) gnn_weights: GnnModel, /// Number of message passing rounds num_rounds: usize, // 2 /// Attention heads for neighbor weighting num_heads: usize, // 4 /// How many HNSW neighbors to consider in GNN neighborhood_size: usize, // 20 (retrieve 20, GNN selects best 5) } impl GnnQueryRefiner { /// Refine HNSW results using graph context pub fn refine(&self, query: &[f32], hnsw_results: &[SearchResult]) -> Vec { // Build local subgraph from query + HNSW results let subgraph = self.build_local_subgraph(query, hnsw_results); // Run message passing let refined = self.message_pass(&subgraph, self.num_rounds); // Re-rank based on refined representations self.rerank(query, &refined) } } ``` **Latency**: +0.2ms on top of HNSW search (total <1.5ms for 100K vectors). #### Mode 2: Temporal Sequence Recognition Tracks CSI vector sequences to recognize activity patterns that span multiple frames: ```rust /// Temporal pattern recognizer using GNN edges pub struct TemporalPatternRecognizer { /// Sliding window of recent query vectors window: VecDeque, /// Maximum window size (in frames) max_window: usize, // 100 (10 seconds at 10 Hz) /// Temporal edge decay factor decay: f64, // 0.95 (edges weaken with time) /// Known activity sequences (learned from data) activity_templates: HashMap>>, } impl TemporalPatternRecognizer { /// Feed new CSI vector and check for activity pattern matches pub fn observe(&mut self, vector: &[f32], timestamp: f64) -> Vec { self.window.push_back(TimestampedVector { vector: vector.to_vec(), timestamp }); // Build temporal subgraph from window let temporal_graph = self.build_temporal_graph(); // GNN aggregates temporal context let sequence_embedding = self.gnn_aggregate(&temporal_graph); // Match against known activity templates self.match_activities(&sequence_embedding) } } ``` **Activity patterns detectable**: | Activity | Frames Needed | CSI Signature | |----------|--------------|---------------| | Walking | 10-30 | Periodic Doppler oscillation | | Falling | 5-15 | Sharp amplitude spike → stillness | | Sitting down | 10-20 | Gradual descent in reflection height | | Breathing (still) | 30-100 | Micro-periodic phase variation | | Gesture (wave) | 5-15 | Localized high-frequency amplitude variation | #### Mode 3: Multi-Person Disentanglement When N>1 people are present, CSI is a superposition. The GNN learns to cluster pattern fragments: ```rust /// Multi-person CSI disentanglement using GNN clustering pub struct MultiPersonDisentangler { /// Maximum expected simultaneous persons max_persons: usize, // 10 /// GNN-based spectral clustering cluster_gnn: GnnModel, /// Per-person tracking state person_tracks: Vec, } impl MultiPersonDisentangler { /// Separate CSI features into per-person components pub fn disentangle(&mut self, features: &CsiFeatures) -> Vec { // Decompose CSI into subcarrier groups using GNN attention let subcarrier_graph = self.build_subcarrier_graph(features); // GNN clusters subcarriers by person contribution let clusters = self.cluster_gnn.cluster(&subcarrier_graph, self.max_persons); // Extract per-person features from clustered subcarriers clusters.iter().map(|c| self.extract_person_features(features, c)).collect() } } ``` ### GNN Learning Loop The GNN improves with every query through RuVector's built-in learning: ``` Query → HNSW retrieval → GNN refinement → User action (click/confirm/reject) │ ▼ Update GNN weights via: 1. Positive: confirmed results get higher attention 2. Negative: rejected results get lower attention 3. Temporal: successful sequences reinforce edges ``` For WiFi-DensePose, "user action" is replaced by: - **Temporal consistency**: If frame N+1 confirms frame N's detection, reinforce - **Multi-AP agreement**: If two APs agree on detection, reinforce both - **Physical plausibility**: If pose satisfies skeletal constraints, reinforce ### Performance Budget | Component | Parameters | Memory | Latency (per query) | |-----------|-----------|--------|-------------------| | GNN weights (2 layers, 4 heads) | 52K | 208 KB | 0.15 ms | | Temporal graph (100-frame window) | N/A | ~130 KB | 0.05 ms | | Multi-person clustering | 18K | 72 KB | 0.3 ms | | **Total GNN overhead** | **70K** | **410 KB** | **0.5 ms** | ## Consequences ### Positive - **Context-aware search**: Results account for temporal and spatial relationships, not just vector distance - **Activity recognition**: Temporal GNN enables sequence-level pattern matching - **Multi-person support**: GNN clustering separates overlapping CSI patterns - **Self-improving**: Every query provides learning signal to refine attention weights - **Lightweight**: 70K parameters, 410 KB memory, 0.5ms latency overhead ### Negative - **Training data needed**: GNN weights require initial training on CSI pattern graphs - **Complexity**: Three modes increase testing and debugging surface - **Graph maintenance**: Temporal edges must be pruned to prevent unbounded growth - **Approximation**: GNN clustering for multi-person is approximate; may merge/split incorrectly ### Interaction with Other ADRs - **ADR-004** (HNSW): GNN operates on HNSW graph structure; depends on HNSW being available - **ADR-005** (SONA): GNN weights can be adapted via SONA LoRA for environment-specific tuning - **ADR-003** (RVF): GNN weights stored in model container alongside inference weights - **ADR-010** (Witness): GNN weight updates recorded in witness chain ## References - [Graph Attention Networks (GAT)](https://arxiv.org/abs/1710.10903) - [Temporal Graph Networks](https://arxiv.org/abs/2006.10637) - [Spectral Clustering with Graph Neural Networks](https://arxiv.org/abs/1907.00481) - [WiFi-based Multi-Person Sensing](https://dl.acm.org/doi/10.1145/3534592) - [RuVector GNN Implementation](https://github.com/ruvnet/ruvector) - ADR-004: HNSW Vector Search for Signal Fingerprinting