//! QuantumFabric Orchestration Layer //! //! This module provides the top-level API for the ruQu coherence gate system. //! It manages the 256-tile WASM fabric, coordinates syndrome processing across //! worker tiles, and exposes a clean interface for quantum control systems. //! //! # Quick Start //! //! ```rust,no_run //! use ruqu::fabric::{QuantumFabric, PatchMap, surface_code_d7}; //! //! // Initialize the 256-tile quantum control fabric //! let mut fabric = QuantumFabric::builder() //! .tiles(256) // 255 workers + TileZero //! .patch_map(surface_code_d7()) // Surface code layout //! .syndrome_buffer(1024) // Ring buffer depth //! .build() //! .expect("Failed to build fabric"); //! //! // Each cycle: ingest syndromes and get gate decision //! // let decision = fabric.tick()?; //! ``` //! //! # Architecture //! //! The QuantumFabric coordinates: //! - **255 WorkerTiles** (IDs 1-255): Process local patches of the quantum device //! - **TileZero** (ID 0): Merges worker reports and issues gate decisions //! - **CoherenceGate**: Three-filter decision pipeline (Structural, Shift, Evidence) //! - **PatchMap**: Hardware topology mapping qubits to tiles //! //! # Latency Budget //! //! Target: <4μs p99 end-to-end decision latency //! //! ```text //! Syndrome Arrival → 0 ns //! Worker Distribution → +100 ns //! Parallel Worker Ticks → +500 ns //! Report Collection → +100 ns //! TileZero Merge → +500 ns //! Three-Filter Eval → +100 ns //! Gate Decision → +100 ns //! Token Signing → +500 ns //! Receipt Append → +100 ns //! ───────────────────────────────── //! Total → ~2,000 ns //! ``` use std::time::Instant; use crate::error::{Result, RuQuError}; use crate::filters::{FilterConfig, FilterPipeline, FilterResults, SystemState, Verdict}; use crate::syndrome::SyndromeRound; use crate::tile::{ GateDecision as TileGateDecision, GateThresholds, ReceiptLog, TileReport, TileZero, WorkerTile, }; use crate::types::{GateDecision, RegionMask, SequenceId}; use crate::{DEFAULT_BUFFER_CAPACITY, TILE_COUNT, WORKER_TILE_COUNT}; // ═══════════════════════════════════════════════════════════════════════════════ // PatchMap - Hardware Topology // ═══════════════════════════════════════════════════════════════════════════════ /// Assignment of qubits/vertices to a specific tile. #[derive(Debug, Clone)] pub struct TileAssignment { /// Tile ID (1-255 for workers, 0 reserved for TileZero) pub tile_id: u8, /// Qubit/vertex IDs assigned to this tile pub vertices: Vec, /// Boundary vertices shared with other tiles pub boundary_vertices: Vec, /// Neighboring tile IDs pub neighbors: Vec, } impl TileAssignment { /// Create a new tile assignment. pub fn new(tile_id: u8) -> Self { Self { tile_id, vertices: Vec::new(), boundary_vertices: Vec::new(), neighbors: Vec::new(), } } /// Add a vertex to this tile. pub fn add_vertex(&mut self, vertex_id: u64) { self.vertices.push(vertex_id); } /// Add a boundary vertex (shared with neighboring tiles). pub fn add_boundary(&mut self, vertex_id: u64) { self.boundary_vertices.push(vertex_id); } /// Add a neighboring tile. pub fn add_neighbor(&mut self, tile_id: u8) { if !self.neighbors.contains(&tile_id) { self.neighbors.push(tile_id); } } /// Get the total number of vertices (including boundary). pub fn vertex_count(&self) -> usize { self.vertices.len() + self.boundary_vertices.len() } } /// Hardware topology mapping qubits to tiles. /// /// The PatchMap defines how the quantum device is partitioned across the 256-tile /// fabric. Each tile is responsible for a "patch" of qubits, with boundary regions /// shared between neighboring tiles. #[derive(Debug, Clone)] pub struct PatchMap { /// Human-readable name for this topology pub name: String, /// Total number of qubits in the device pub qubit_count: usize, /// Per-tile assignments pub tile_assignments: Vec, /// Code distance (for surface codes) pub distance: Option, /// Number of detectors per round pub detector_count: usize, } impl PatchMap { /// Create a new empty patch map. pub fn new(name: impl Into, qubit_count: usize) -> Self { Self { name: name.into(), qubit_count, tile_assignments: Vec::new(), distance: None, detector_count: qubit_count, // Default: one detector per qubit } } /// Set the code distance. pub fn with_distance(mut self, d: usize) -> Self { self.distance = Some(d); self } /// Set the detector count. pub fn with_detectors(mut self, count: usize) -> Self { self.detector_count = count; self } /// Add a tile assignment. pub fn add_assignment(&mut self, assignment: TileAssignment) { self.tile_assignments.push(assignment); } /// Get the number of active tiles. pub fn tile_count(&self) -> usize { self.tile_assignments.len() } /// Get assignment for a specific tile. pub fn get_assignment(&self, tile_id: u8) -> Option<&TileAssignment> { self.tile_assignments.iter().find(|a| a.tile_id == tile_id) } /// Find which tile owns a vertex. pub fn find_tile_for_vertex(&self, vertex_id: u64) -> Option { for assignment in &self.tile_assignments { if assignment.vertices.contains(&vertex_id) { return Some(assignment.tile_id); } } None } /// Validate the patch map. pub fn validate(&self) -> Result<()> { if self.qubit_count == 0 { return Err(RuQuError::InvalidFabricConfig( "PatchMap has zero qubits".to_string(), )); } if self.tile_assignments.is_empty() { return Err(RuQuError::InvalidFabricConfig( "PatchMap has no tile assignments".to_string(), )); } // Check for duplicate tile IDs let mut seen_ids = std::collections::HashSet::new(); for assignment in &self.tile_assignments { if assignment.tile_id == 0 { return Err(RuQuError::InvalidFabricConfig( "TileId 0 is reserved for TileZero".to_string(), )); } if !seen_ids.insert(assignment.tile_id) { return Err(RuQuError::InvalidFabricConfig(format!( "Duplicate tile ID: {}", assignment.tile_id ))); } } Ok(()) } } /// Create a patch map for a distance-7 surface code. /// /// This is the canonical example topology with approximately 97 data qubits /// (7x7 lattice) partitioned across available tiles. pub fn surface_code_d7() -> PatchMap { surface_code(7) } /// Create a patch map for a surface code of given distance. /// /// # Arguments /// /// * `distance` - The code distance (must be odd, >= 3) /// /// # Returns /// /// A PatchMap with qubits distributed across tiles. pub fn surface_code(distance: usize) -> PatchMap { assert!(distance >= 3, "Surface code distance must be >= 3"); assert!(distance % 2 == 1, "Surface code distance must be odd"); // Surface code has d^2 data qubits + (d-1)^2 + (d)^2 ancilla qubits // Simplified: use approximately 2*d^2 total qubits let qubit_count = 2 * distance * distance; // Detector count: approximately (d-1)^2 X-checks + (d-1)^2 Z-checks per round let detector_count = 2 * (distance - 1) * (distance - 1); let mut patch_map = PatchMap::new(format!("surface_code_d{}", distance), qubit_count) .with_distance(distance) .with_detectors(detector_count); // Partition qubits across tiles // Strategy: assign sqrt(qubit_count) qubits per tile let qubits_per_tile = (qubit_count as f64).sqrt().ceil() as usize; let num_tiles = (qubit_count + qubits_per_tile - 1) / qubits_per_tile; let num_tiles = num_tiles.min(WORKER_TILE_COUNT); for tile_idx in 0..num_tiles { let tile_id = (tile_idx + 1) as u8; // Tile IDs start at 1 let mut assignment = TileAssignment::new(tile_id); let start_qubit = tile_idx * qubits_per_tile; let end_qubit = ((tile_idx + 1) * qubits_per_tile).min(qubit_count); for qubit in start_qubit..end_qubit { assignment.add_vertex(qubit as u64); } // Add neighbors (simple linear topology for now) if tile_idx > 0 { assignment.add_neighbor(tile_idx as u8); } if tile_idx < num_tiles - 1 { assignment.add_neighbor((tile_idx + 2) as u8); } // Mark boundary vertices if tile_idx > 0 { assignment.add_boundary(start_qubit as u64); } if tile_idx < num_tiles - 1 && end_qubit > start_qubit { assignment.add_boundary((end_qubit - 1) as u64); } patch_map.add_assignment(assignment); } patch_map } /// Create a simple linear patch map for testing. pub fn linear_patch_map(qubit_count: usize, tiles: usize) -> PatchMap { let tiles = tiles.min(WORKER_TILE_COUNT).max(1); let mut patch_map = PatchMap::new("linear", qubit_count); let qubits_per_tile = (qubit_count + tiles - 1) / tiles; for tile_idx in 0..tiles { let tile_id = (tile_idx + 1) as u8; let mut assignment = TileAssignment::new(tile_id); let start = tile_idx * qubits_per_tile; let end = ((tile_idx + 1) * qubits_per_tile).min(qubit_count); for qubit in start..end { assignment.add_vertex(qubit as u64); } patch_map.add_assignment(assignment); } patch_map } // ═══════════════════════════════════════════════════════════════════════════════ // FabricConfig - Configuration // ═══════════════════════════════════════════════════════════════════════════════ /// Configuration for the QuantumFabric. #[derive(Debug, Clone)] pub struct FabricConfig { /// Number of tiles (max 256) pub tile_count: usize, /// Syndrome buffer size per tile pub buffer_size: usize, /// Gate decision thresholds pub thresholds: GateThresholds, /// Filter pipeline configuration pub filter_config: FilterConfig, /// Enable receipt logging pub enable_receipts: bool, /// Decision budget in nanoseconds pub decision_budget_ns: u64, } impl Default for FabricConfig { fn default() -> Self { Self { tile_count: TILE_COUNT, buffer_size: DEFAULT_BUFFER_CAPACITY, thresholds: GateThresholds::default(), filter_config: FilterConfig::default(), enable_receipts: true, decision_budget_ns: 4_000, // 4 microseconds } } } impl FabricConfig { /// Validate the configuration. pub fn validate(&self) -> Result<()> { if self.tile_count == 0 || self.tile_count > TILE_COUNT { return Err(RuQuError::InvalidFabricConfig(format!( "tile_count must be 1-{}, got {}", TILE_COUNT, self.tile_count ))); } if self.buffer_size == 0 { return Err(RuQuError::InvalidFabricConfig( "buffer_size must be positive".to_string(), )); } Ok(()) } } // ═══════════════════════════════════════════════════════════════════════════════ // FabricState - Runtime State // ═══════════════════════════════════════════════════════════════════════════════ /// Current state of the QuantumFabric. #[derive(Debug, Clone)] pub struct FabricState { /// Current tick number pub tick: u64, /// Total syndromes ingested pub syndromes_ingested: u64, /// Number of active worker tiles pub active_tiles: usize, /// Most recent gate decision pub last_decision: GateDecision, /// Regions currently flagged as unsafe pub quarantine_mask: RegionMask, /// Average decision latency (nanoseconds) pub avg_latency_ns: u64, /// Peak decision latency (nanoseconds) pub peak_latency_ns: u64, /// Total permit decisions pub permit_count: u64, /// Total defer decisions pub defer_count: u64, /// Total deny decisions pub deny_count: u64, } impl Default for FabricState { fn default() -> Self { Self { tick: 0, syndromes_ingested: 0, active_tiles: 0, last_decision: GateDecision::Cautious, quarantine_mask: RegionMask::none(), avg_latency_ns: 0, peak_latency_ns: 0, permit_count: 0, defer_count: 0, deny_count: 0, } } } impl FabricState { /// Get the total number of decisions made. pub fn total_decisions(&self) -> u64 { self.permit_count + self.defer_count + self.deny_count } /// Get the permit rate (0.0 to 1.0). pub fn permit_rate(&self) -> f64 { let total = self.total_decisions(); if total == 0 { return 0.0; } self.permit_count as f64 / total as f64 } } // ═══════════════════════════════════════════════════════════════════════════════ // WitnessReceipt - Audit Trail // ═══════════════════════════════════════════════════════════════════════════════ /// A witness receipt for auditing gate decisions. /// /// Each gate decision produces a receipt containing cryptographic proof of /// the decision inputs and output, enabling post-hoc verification. #[derive(Debug, Clone)] pub struct WitnessReceipt { /// Decision sequence number pub sequence: SequenceId, /// Timestamp (nanoseconds since epoch) pub timestamp: u64, /// The gate decision pub decision: GateDecision, /// Blake3 hash of input state pub input_hash: [u8; 32], /// Filter results summary pub filter_summary: FilterSummary, /// Previous receipt hash (for chaining) pub previous_hash: [u8; 32], /// This receipt's hash pub hash: [u8; 32], } /// Summary of filter results for the receipt. #[derive(Debug, Clone, Default)] pub struct FilterSummary { /// Structural filter: min-cut value pub cut_value: f64, /// Shift filter: pressure value pub shift_pressure: f64, /// Evidence filter: e-value pub e_value: f64, /// Regions affected pub affected_regions: u32, } // ═══════════════════════════════════════════════════════════════════════════════ // CoherenceGate - Public Gate Interface // ═══════════════════════════════════════════════════════════════════════════════ /// The coherence gate - the decision-making core of ruQu. /// /// The gate uses three stacked filters to make coherence assessments: /// 1. **Structural Filter**: Min-cut based partition detection /// 2. **Shift Filter**: Distribution drift detection /// 3. **Evidence Filter**: Anytime-valid e-value accumulation /// /// All three must pass for PERMIT. Any one can trigger DENY or DEFER. #[derive(Debug)] pub struct CoherenceGate { /// The three-filter pipeline pipeline: FilterPipeline, /// System state tracking state: SystemState, /// Current sequence number sequence: SequenceId, /// Last receipt (for chaining) last_receipt_hash: [u8; 32], } impl CoherenceGate { /// Create a new coherence gate with the given configuration. pub fn new(config: FilterConfig) -> Self { Self { pipeline: FilterPipeline::new(config), state: SystemState::new(0), sequence: 0, last_receipt_hash: [0u8; 32], } } /// Create with default configuration. pub fn with_defaults() -> Self { Self::new(FilterConfig::default()) } /// Evaluate the current system state and return a gate decision. /// /// This is the main entry point for coherence assessment. pub fn evaluate(&self) -> Result { let results = self.pipeline.evaluate(&self.state); let decision = match results.verdict { Some(Verdict::Permit) => GateDecision::Safe, Some(Verdict::Deny) => GateDecision::Unsafe, Some(Verdict::Defer) | None => GateDecision::Cautious, }; Ok(decision) } /// Evaluate and return detailed filter results. pub fn evaluate_detailed(&self) -> FilterResults { self.pipeline.evaluate(&self.state) } /// Get the current witness receipt (if available). pub fn receipt(&self) -> Option { if self.sequence == 0 { return None; } let results = self.pipeline.evaluate(&self.state); let summary = FilterSummary { cut_value: results.structural.cut_value, shift_pressure: results.shift.pressure, e_value: results.evidence.e_value, affected_regions: results.affected_regions.count(), }; // Compute simple hash (use blake3 in production) let mut hash = [0u8; 32]; hash[0..8].copy_from_slice(&self.sequence.to_le_bytes()); hash[8..16].copy_from_slice(&summary.cut_value.to_le_bytes()); Some(WitnessReceipt { sequence: self.sequence, timestamp: std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .map(|d| d.as_nanos() as u64) .unwrap_or(0), decision: self.evaluate().unwrap_or(GateDecision::Cautious), input_hash: self.last_receipt_hash, filter_summary: summary, previous_hash: self.last_receipt_hash, hash, }) } /// Update the system state with new data. pub fn update_state(&mut self, state: SystemState) { self.state = state; } /// Get a mutable reference to the filter pipeline. pub fn pipeline_mut(&mut self) -> &mut FilterPipeline { &mut self.pipeline } /// Get a reference to the filter pipeline. pub fn pipeline(&self) -> &FilterPipeline { &self.pipeline } /// Get the current system state. pub fn state(&self) -> &SystemState { &self.state } /// Increment the sequence counter (called after each decision). pub(crate) fn increment_sequence(&mut self) { self.sequence += 1; } } // ═══════════════════════════════════════════════════════════════════════════════ // FabricBuilder - Builder Pattern // ═══════════════════════════════════════════════════════════════════════════════ /// Builder for constructing a QuantumFabric. /// /// # Example /// /// ```rust,no_run /// use ruqu::fabric::{QuantumFabric, surface_code_d7}; /// /// let fabric = QuantumFabric::builder() /// .tiles(256) /// .patch_map(surface_code_d7()) /// .syndrome_buffer(1024) /// .build() /// .expect("Failed to build fabric"); /// ``` #[derive(Debug)] pub struct FabricBuilder { tile_count: usize, patch_map: Option, buffer_size: usize, thresholds: GateThresholds, filter_config: FilterConfig, enable_receipts: bool, } impl Default for FabricBuilder { fn default() -> Self { Self::new() } } impl FabricBuilder { /// Create a new builder with default settings. pub fn new() -> Self { Self { tile_count: TILE_COUNT, patch_map: None, buffer_size: DEFAULT_BUFFER_CAPACITY, thresholds: GateThresholds::default(), filter_config: FilterConfig::default(), enable_receipts: true, } } /// Set the number of tiles (max 256). /// /// The fabric will have `count - 1` worker tiles plus TileZero. pub fn tiles(mut self, count: usize) -> Self { self.tile_count = count.min(TILE_COUNT); self } /// Set the patch map (hardware topology). pub fn patch_map(mut self, map: PatchMap) -> Self { self.patch_map = Some(map); self } /// Set the syndrome buffer size per tile. pub fn syndrome_buffer(mut self, size: usize) -> Self { self.buffer_size = size.max(1); self } /// Set the gate thresholds. pub fn thresholds(mut self, t: GateThresholds) -> Self { self.thresholds = t; self } /// Set custom filter configuration. pub fn filter_config(mut self, config: FilterConfig) -> Self { self.filter_config = config; self } /// Enable or disable receipt logging. pub fn enable_receipts(mut self, enable: bool) -> Self { self.enable_receipts = enable; self } /// Build the QuantumFabric. /// /// # Errors /// /// Returns an error if: /// - Configuration is invalid /// - PatchMap validation fails pub fn build(self) -> Result { // Validate patch map if provided if let Some(ref map) = self.patch_map { map.validate()?; } // Create configuration let config = FabricConfig { tile_count: self.tile_count, buffer_size: self.buffer_size, thresholds: self.thresholds.clone(), filter_config: self.filter_config.clone(), enable_receipts: self.enable_receipts, ..Default::default() }; config.validate()?; // Determine number of worker tiles let worker_count = if let Some(ref map) = self.patch_map { map.tile_count().min(WORKER_TILE_COUNT) } else { (self.tile_count - 1).min(WORKER_TILE_COUNT) }; // Create worker tiles let mut tiles: Vec = Vec::with_capacity(worker_count); for i in 0..worker_count { let tile_id = (i + 1) as u8; // Tile IDs start at 1 tiles.push(WorkerTile::new(tile_id)); } // Create TileZero let tile_zero = TileZero::new(self.thresholds); // Create coherence gate let gate = CoherenceGate::new(self.filter_config); // Create patch map if not provided let patch_map = self.patch_map.unwrap_or_else(|| { linear_patch_map(64, worker_count) // Default: 64 qubits }); Ok(QuantumFabric { tiles, tile_zero, config, patch_map, gate, state: FabricState::default(), receipt_log: ReceiptLog::new(), }) } } // ═══════════════════════════════════════════════════════════════════════════════ // QuantumFabric - Main Orchestrator // ═══════════════════════════════════════════════════════════════════════════════ /// The main orchestrator for the ruQu coherence gate system. /// /// QuantumFabric manages the 256-tile WASM fabric, coordinating syndrome /// processing across worker tiles and issuing coherence gate decisions. /// /// # Architecture /// /// ```text /// ┌─────────────────────────────────────────────────────────────────┐ /// │ QuantumFabric │ /// ├─────────────────────────────────────────────────────────────────┤ /// │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ /// │ │ Worker 1 │ │ Worker 2 │ │ Worker 3 │ ... │Worker 255│ │ /// │ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ /// │ │ │ │ │ │ /// │ └─────────────┴──────┬──────┴──────────────────┘ │ /// │ │ │ /// │ ┌──────▼──────┐ │ /// │ │ TileZero │ │ /// │ │ (Coordinator)│ │ /// │ └──────┬──────┘ │ /// │ │ │ /// │ ┌──────▼──────┐ │ /// │ │CoherenceGate │ │ /// │ │ (Decision) │ │ /// │ └─────────────┘ │ /// └─────────────────────────────────────────────────────────────────┘ /// ``` /// /// # Example /// /// ```rust,no_run /// use ruqu::fabric::{QuantumFabric, surface_code_d7}; /// use ruqu::syndrome::{DetectorBitmap, SyndromeRound}; /// /// // Build the fabric /// let mut fabric = QuantumFabric::builder() /// .tiles(256) /// .patch_map(surface_code_d7()) /// .syndrome_buffer(1024) /// .build() /// .expect("Failed to build fabric"); /// /// // Process syndrome rounds /// let round = SyndromeRound::new( /// 1, /// 1000, /// 1705500000000, /// DetectorBitmap::new(64), /// 0, /// ); /// /// // Ingest and tick /// fabric.ingest_syndromes(&[round]).expect("Ingest failed"); /// let decision = fabric.tick().expect("Tick failed"); /// ``` #[derive(Debug)] pub struct QuantumFabric { /// Worker tiles (IDs 1-255) tiles: Vec, /// Coordinator tile (ID 0) tile_zero: TileZero, /// Fabric configuration config: FabricConfig, /// Hardware topology patch_map: PatchMap, /// The coherence gate pub gate: CoherenceGate, /// Runtime state state: FabricState, /// Receipt log for audit receipt_log: ReceiptLog, } impl QuantumFabric { /// Create a new FabricBuilder. pub fn builder() -> FabricBuilder { FabricBuilder::new() } /// Ingest a batch of syndrome rounds. /// /// Syndromes are distributed to the appropriate worker tiles based on /// the patch map. Each tile processes its assigned syndromes. /// /// # Arguments /// /// * `batch` - Slice of syndrome rounds to ingest /// /// # Errors /// /// Returns an error if syndrome distribution fails. pub fn ingest_syndromes(&mut self, batch: &[SyndromeRound]) -> Result<()> { for round in batch { self.state.syndromes_ingested += 1; // Distribute syndrome to appropriate tile(s) let tile_id = round.source_tile; if tile_id == 0 || tile_id as usize > self.tiles.len() { // Distribute to all tiles if source is TileZero or invalid // This handles the case where syndromes aren't pre-assigned for tile in &mut self.tiles { // Convert syndrome round to delta for tile processing let delta = crate::tile::SyndromeDelta::new(0, 0, round.fired_count() as u16); tile.tick(&delta); } } else { // Send to specific tile let tile_idx = (tile_id - 1) as usize; if tile_idx < self.tiles.len() { let delta = crate::tile::SyndromeDelta::new(0, 0, round.fired_count() as u16); self.tiles[tile_idx].tick(&delta); } } } Ok(()) } /// Execute one tick of the coherence gate. /// /// This is the main processing loop entry point: /// 1. Collect reports from all worker tiles /// 2. Merge reports in TileZero /// 3. Evaluate the three-filter pipeline /// 4. Issue gate decision /// 5. Update receipts /// /// # Returns /// /// The gate decision (Safe, Cautious, or Unsafe). /// /// # Errors /// /// Returns an error if: /// - Decision latency exceeds budget /// - Filter evaluation fails pub fn tick(&mut self) -> Result { let start = Instant::now(); self.state.tick += 1; // Collect reports from all worker tiles let mut reports: Vec = Vec::with_capacity(self.tiles.len()); for tile in &self.tiles { let report = TileReport::new(tile.tile_id); // In a real implementation, we'd get the actual report from the tile // For now, create a synthetic report based on tile state let mut report = report; report.local_cut = tile.local_cut_state.cut_value; report.shift_score = 0.1; // Would compute from tile report.e_value = tile.evidence.e_value(); report.num_vertices = tile.patch_graph.num_vertices; report.num_edges = tile.patch_graph.num_edges; reports.push(report); } // Merge reports in TileZero let tile_decision = self.tile_zero.merge_reports(reports); // Convert tile decision to domain decision let decision = match tile_decision { TileGateDecision::Permit => GateDecision::Safe, TileGateDecision::Defer => GateDecision::Cautious, TileGateDecision::Deny => GateDecision::Unsafe, }; // Update state self.state.last_decision = decision; self.state.active_tiles = self.tiles.len(); match decision { GateDecision::Safe => self.state.permit_count += 1, GateDecision::Cautious => self.state.defer_count += 1, GateDecision::Unsafe => self.state.deny_count += 1, } // Update latency tracking let elapsed = start.elapsed().as_nanos() as u64; self.state.peak_latency_ns = self.state.peak_latency_ns.max(elapsed); let n = self.state.total_decisions(); if n > 0 { self.state.avg_latency_ns = (self.state.avg_latency_ns * (n - 1) + elapsed) / n; } // Check latency budget if elapsed > self.config.decision_budget_ns { // Log warning but don't fail - latency budget is advisory // In production, this would trigger monitoring alerts } // Update gate sequence self.gate.increment_sequence(); // Append receipt if enabled if self.config.enable_receipts { let witness_hash = [0u8; 32]; // Would compute proper hash self.receipt_log .append(tile_decision, self.state.tick, elapsed, witness_hash); } Ok(decision) } /// Get the current fabric state. pub fn current_state(&self) -> &FabricState { &self.state } /// Get a snapshot of the current fabric state (cloned). pub fn state_snapshot(&self) -> FabricState { self.state.clone() } /// Get the patch map. pub fn patch_map(&self) -> &PatchMap { &self.patch_map } /// Get the fabric configuration. pub fn config(&self) -> &FabricConfig { &self.config } /// Get the number of worker tiles. pub fn worker_count(&self) -> usize { self.tiles.len() } /// Get a reference to a specific worker tile. pub fn get_tile(&self, tile_id: u8) -> Option<&WorkerTile> { if tile_id == 0 || tile_id as usize > self.tiles.len() { return None; } Some(&self.tiles[(tile_id - 1) as usize]) } /// Get a mutable reference to a specific worker tile. pub fn get_tile_mut(&mut self, tile_id: u8) -> Option<&mut WorkerTile> { if tile_id == 0 || tile_id as usize > self.tiles.len() { return None; } Some(&mut self.tiles[(tile_id - 1) as usize]) } /// Get the TileZero coordinator. pub fn tile_zero(&self) -> &TileZero { &self.tile_zero } /// Get the receipt log. pub fn receipt_log(&self) -> &ReceiptLog { &self.receipt_log } /// Reset all tiles and state. pub fn reset(&mut self) { for tile in &mut self.tiles { tile.reset(); } self.state = FabricState::default(); self.receipt_log = ReceiptLog::new(); } /// Get decision statistics. pub fn decision_stats(&self) -> DecisionStats { DecisionStats { total: self.state.total_decisions(), permits: self.state.permit_count, defers: self.state.defer_count, denies: self.state.deny_count, permit_rate: self.state.permit_rate(), avg_latency_ns: self.state.avg_latency_ns, peak_latency_ns: self.state.peak_latency_ns, } } } /// Statistics about gate decisions. #[derive(Debug, Clone, Default)] pub struct DecisionStats { /// Total decisions made pub total: u64, /// Number of PERMIT decisions pub permits: u64, /// Number of DEFER decisions pub defers: u64, /// Number of DENY decisions pub denies: u64, /// Permit rate (0.0 to 1.0) pub permit_rate: f64, /// Average decision latency (nanoseconds) pub avg_latency_ns: u64, /// Peak decision latency (nanoseconds) pub peak_latency_ns: u64, } // ═══════════════════════════════════════════════════════════════════════════════ // Tests // ═══════════════════════════════════════════════════════════════════════════════ #[cfg(test)] mod tests { use super::*; use crate::syndrome::DetectorBitmap; #[test] fn test_surface_code_d7() { let patch_map = surface_code_d7(); assert_eq!(patch_map.name, "surface_code_d7"); assert_eq!(patch_map.distance, Some(7)); assert!(patch_map.qubit_count > 0); assert!(patch_map.tile_count() > 0); assert!(patch_map.validate().is_ok()); } #[test] fn test_surface_code_various_distances() { for d in [3, 5, 7, 9, 11] { let patch_map = surface_code(d); assert_eq!(patch_map.distance, Some(d)); assert!(patch_map.validate().is_ok()); } } #[test] fn test_linear_patch_map() { let patch_map = linear_patch_map(100, 10); assert_eq!(patch_map.name, "linear"); assert_eq!(patch_map.qubit_count, 100); assert_eq!(patch_map.tile_count(), 10); assert!(patch_map.validate().is_ok()); } #[test] fn test_fabric_builder_default() { let fabric = QuantumFabric::builder().build(); assert!(fabric.is_ok()); let fabric = fabric.unwrap(); assert!(fabric.worker_count() > 0); } #[test] fn test_fabric_builder_with_options() { let fabric = QuantumFabric::builder() .tiles(16) .patch_map(surface_code_d7()) .syndrome_buffer(512) .enable_receipts(true) .build(); assert!(fabric.is_ok()); let fabric = fabric.unwrap(); assert!(fabric.worker_count() <= 15); } #[test] fn test_fabric_ingest_syndromes() { let mut fabric = QuantumFabric::builder().tiles(4).build().unwrap(); let rounds: Vec = (0..10) .map(|i| SyndromeRound::new(i, i, i * 1000, DetectorBitmap::new(64), 0)) .collect(); let result = fabric.ingest_syndromes(&rounds); assert!(result.is_ok()); assert_eq!(fabric.current_state().syndromes_ingested, 10); } #[test] fn test_fabric_tick() { let mut fabric = QuantumFabric::builder().tiles(4).build().unwrap(); // Tick without any syndromes let result = fabric.tick(); assert!(result.is_ok()); let state = fabric.current_state(); assert_eq!(state.tick, 1); assert_eq!(state.total_decisions(), 1); } #[test] fn test_fabric_multiple_ticks() { let mut fabric = QuantumFabric::builder().tiles(8).build().unwrap(); // Run multiple ticks for _ in 0..100 { let _ = fabric.tick(); } let state = fabric.current_state(); assert_eq!(state.tick, 100); assert_eq!(state.total_decisions(), 100); } #[test] fn test_fabric_get_tile() { let fabric = QuantumFabric::builder().tiles(4).build().unwrap(); // Tile 0 (TileZero) should return None assert!(fabric.get_tile(0).is_none()); // Valid tile IDs assert!(fabric.get_tile(1).is_some()); assert!(fabric.get_tile(2).is_some()); assert!(fabric.get_tile(3).is_some()); // Invalid tile ID assert!(fabric.get_tile(100).is_none()); } #[test] fn test_fabric_reset() { let mut fabric = QuantumFabric::builder().tiles(4).build().unwrap(); // Do some work for _ in 0..10 { let _ = fabric.tick(); } assert_eq!(fabric.current_state().tick, 10); // Reset fabric.reset(); assert_eq!(fabric.current_state().tick, 0); assert_eq!(fabric.current_state().total_decisions(), 0); } #[test] fn test_fabric_decision_stats() { let mut fabric = QuantumFabric::builder().tiles(4).build().unwrap(); for _ in 0..50 { let _ = fabric.tick(); } let stats = fabric.decision_stats(); assert_eq!(stats.total, 50); assert!(stats.permits + stats.defers + stats.denies == 50); } #[test] fn test_coherence_gate_evaluate() { let gate = CoherenceGate::with_defaults(); let decision = gate.evaluate(); assert!(decision.is_ok()); } #[test] fn test_coherence_gate_receipt() { let mut gate = CoherenceGate::with_defaults(); // No receipt before first evaluation assert!(gate.receipt().is_none()); // After incrementing sequence gate.increment_sequence(); let receipt = gate.receipt(); assert!(receipt.is_some()); } #[test] fn test_patch_map_find_tile() { let patch_map = surface_code_d7(); // Find tile for first qubit let tile = patch_map.find_tile_for_vertex(0); assert!(tile.is_some()); // Non-existent qubit let tile = patch_map.find_tile_for_vertex(999999); assert!(tile.is_none()); } #[test] fn test_tile_assignment() { let mut assignment = TileAssignment::new(1); assignment.add_vertex(0); assignment.add_vertex(1); assignment.add_vertex(2); assignment.add_boundary(0); assignment.add_neighbor(2); assignment.add_neighbor(2); // Duplicate should be ignored assert_eq!(assignment.vertices.len(), 3); assert_eq!(assignment.boundary_vertices.len(), 1); assert_eq!(assignment.neighbors.len(), 1); assert_eq!(assignment.vertex_count(), 4); } #[test] fn test_fabric_config_validate() { // Valid config let config = FabricConfig::default(); assert!(config.validate().is_ok()); // Invalid: zero tiles let mut config = FabricConfig::default(); config.tile_count = 0; assert!(config.validate().is_err()); // Invalid: too many tiles let mut config = FabricConfig::default(); config.tile_count = 1000; assert!(config.validate().is_err()); // Invalid: zero buffer let mut config = FabricConfig::default(); config.buffer_size = 0; assert!(config.validate().is_err()); } #[test] fn test_fabric_state_metrics() { let mut state = FabricState::default(); assert_eq!(state.total_decisions(), 0); assert_eq!(state.permit_rate(), 0.0); state.permit_count = 80; state.defer_count = 15; state.deny_count = 5; assert_eq!(state.total_decisions(), 100); assert!((state.permit_rate() - 0.8).abs() < 0.001); } #[test] fn test_witness_receipt_creation() { let mut gate = CoherenceGate::with_defaults(); gate.increment_sequence(); let receipt = gate.receipt(); assert!(receipt.is_some()); let receipt = receipt.unwrap(); assert_eq!(receipt.sequence, 1); } }