//! Data Model and Schema for ruQu //! //! Defines the core data types and a versioned binary log format. //! //! ## Binary Format //! //! The log format is designed for speed and compactness: //! - 4-byte magic header: "RUQU" //! - 1-byte version //! - Sequence of variable-length records //! //! Each record: //! - 1-byte record type //! - 4-byte length (little-endian) //! - Payload bytes //! - 4-byte CRC32 checksum use serde::{Deserialize, Serialize}; use std::io::{Read, Write}; /// Current schema version pub const SCHEMA_VERSION: u8 = 1; /// Magic header for binary logs pub const LOG_MAGIC: &[u8; 4] = b"RUQU"; // ============================================================================ // CORE DATA TYPES // ============================================================================ /// A single syndrome measurement round #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct SyndromeRound { /// Round number (monotonically increasing) pub round_id: u64, /// Timestamp in nanoseconds since epoch pub timestamp_ns: u64, /// Code distance pub code_distance: u8, /// Detector events in this round pub events: Vec, /// Optional metadata #[serde(default)] pub metadata: RoundMetadata, } impl SyndromeRound { /// Create a new syndrome round pub fn new(round_id: u64, code_distance: u8) -> Self { Self { round_id, timestamp_ns: std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .map(|d| d.as_nanos() as u64) .unwrap_or(0), code_distance, events: Vec::new(), metadata: RoundMetadata::default(), } } /// Add a detector event pub fn add_event(&mut self, event: DetectorEvent) { self.events.push(event); } /// Get the number of fired detectors pub fn fired_count(&self) -> usize { self.events.iter().filter(|e| e.fired).count() } } /// A detector event within a syndrome round #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct DetectorEvent { /// Detector index pub detector_id: u32, /// Whether the detector fired (syndrome bit = 1) pub fired: bool, /// Measurement confidence (0.0 to 1.0) #[serde(default = "default_confidence")] pub confidence: f32, /// Spatial coordinates (if known) #[serde(default)] pub coords: Option, } fn default_confidence() -> f32 { 1.0 } /// Spatial coordinates of a detector #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)] pub struct DetectorCoords { /// X coordinate (column) pub x: i16, /// Y coordinate (row) pub y: i16, /// Time slice (for 3D codes) pub t: i16, } /// Round metadata #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] pub struct RoundMetadata { /// Source identifier #[serde(default)] pub source: String, /// Error rate at this round (if known) #[serde(default)] pub error_rate: Option, /// Whether this round is from a hardware run #[serde(default)] pub is_hardware: bool, /// Injected fault (if any) #[serde(default)] pub injected_fault: Option, } /// Boundary identifier for surface codes #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)] pub enum BoundaryId { /// Left boundary (X logical) Left, /// Right boundary (X logical) Right, /// Top boundary (Z logical) Top, /// Bottom boundary (Z logical) Bottom, /// Virtual boundary (for matching) Virtual, /// Custom boundary with ID Custom(u32), } /// A permit token issued by the gate #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct PermitToken { /// Unique token ID pub token_id: u64, /// Round at which permit was issued pub issued_at_round: u64, /// Timestamp when issued (ns since epoch) pub issued_at_ns: u64, /// Time-to-live in nanoseconds pub ttl_ns: u64, /// Permitted regions (bitmask) pub region_mask: u64, /// Confidence level pub confidence: f32, /// Min-cut value at issuance pub min_cut_value: f32, } impl PermitToken { /// Check if the token is still valid pub fn is_valid(&self, current_time_ns: u64) -> bool { current_time_ns < self.issued_at_ns.saturating_add(self.ttl_ns) } /// Remaining time-to-live in nanoseconds pub fn remaining_ttl_ns(&self, current_time_ns: u64) -> u64 { self.issued_at_ns .saturating_add(self.ttl_ns) .saturating_sub(current_time_ns) } } /// A gate decision record #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct GateDecision { /// Round ID when decision was made pub round_id: u64, /// Timestamp of decision (ns since epoch) pub timestamp_ns: u64, /// Decision type pub decision: DecisionType, /// Processing latency in nanoseconds pub latency_ns: u64, /// Input metrics pub metrics: GateMetrics, } /// Decision type #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub enum DecisionType { /// Operation permitted with token Permit(PermitToken), /// Operation deferred Defer { /// Wait time in nanoseconds wait_ns: u64, /// Uncertainty level uncertainty: f32, }, /// Operation denied Deny { /// Risk level (0-1) risk_level: f32, /// Affected region bitmask affected_regions: u64, }, } /// Metrics used for gate decision #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] pub struct GateMetrics { /// Min-cut value pub min_cut: f32, /// Cut value standard deviation pub cut_std: f32, /// Shift from baseline pub shift: f32, /// Evidence accumulation pub evidence: f32, /// Number of fired detectors pub fired_count: u32, /// Clustering score pub clustering: f32, } /// A mitigation action taken #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct MitigationAction { /// Action ID pub action_id: u64, /// Timestamp when action was initiated pub timestamp_ns: u64, /// Action type pub action_type: ActionTypeSchema, /// Target regions pub target_regions: Vec, /// Duration in nanoseconds pub duration_ns: u64, /// Result of the action pub result: ActionResult, } /// Action types in schema format #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] pub enum ActionTypeSchema { /// Quarantine a region to prevent error propagation QuarantineRegion, /// Increase syndrome measurement rounds for higher fidelity IncreaseSyndromeRounds, /// Switch decoder mode (e.g., from fast to accurate) SwitchDecodeMode, /// Trigger re-weighting of decoder graph TriggerReweight, /// Pause learning/write operations during instability PauseLearningWrites, /// Log event for audit trail LogEvent, /// Alert human operator AlertOperator, } /// Result of a mitigation action #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub enum ActionResult { /// Action completed successfully Success, /// Action partially completed Partial { /// Fraction completed (0.0 to 1.0) completed: f32, }, /// Action failed Failed { /// Reason for failure reason: String, }, /// Action is pending execution Pending, } // ============================================================================ // BINARY LOG FORMAT // ============================================================================ /// Record types for binary log #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u8)] pub enum RecordType { /// Syndrome round record SyndromeRound = 1, /// Gate decision record GateDecision = 2, /// Mitigation action record MitigationAction = 3, /// Checkpoint for log recovery Checkpoint = 4, /// Configuration snapshot Config = 5, /// Metrics snapshot Metrics = 6, } impl TryFrom for RecordType { type Error = (); fn try_from(value: u8) -> Result { match value { 1 => Ok(RecordType::SyndromeRound), 2 => Ok(RecordType::GateDecision), 3 => Ok(RecordType::MitigationAction), 4 => Ok(RecordType::Checkpoint), 5 => Ok(RecordType::Config), 6 => Ok(RecordType::Metrics), _ => Err(()), } } } /// Binary log writer pub struct LogWriter { writer: W, record_count: u64, } impl LogWriter { /// Create a new log writer pub fn new(mut writer: W) -> std::io::Result { // Write header writer.write_all(LOG_MAGIC)?; writer.write_all(&[SCHEMA_VERSION])?; Ok(Self { writer, record_count: 0, }) } /// Write a syndrome round pub fn write_syndrome(&mut self, round: &SyndromeRound) -> std::io::Result<()> { let payload = serde_json::to_vec(round) .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?; self.write_record(RecordType::SyndromeRound, &payload) } /// Write a gate decision pub fn write_decision(&mut self, decision: &GateDecision) -> std::io::Result<()> { let payload = serde_json::to_vec(decision) .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?; self.write_record(RecordType::GateDecision, &payload) } /// Write a mitigation action pub fn write_action(&mut self, action: &MitigationAction) -> std::io::Result<()> { let payload = serde_json::to_vec(action) .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?; self.write_record(RecordType::MitigationAction, &payload) } fn write_record(&mut self, record_type: RecordType, payload: &[u8]) -> std::io::Result<()> { // Record type (1 byte) self.writer.write_all(&[record_type as u8])?; // Length (4 bytes, little-endian) let len = payload.len() as u32; self.writer.write_all(&len.to_le_bytes())?; // Payload self.writer.write_all(payload)?; // CRC32 checksum let crc = crc32fast::hash(payload); self.writer.write_all(&crc.to_le_bytes())?; self.record_count += 1; Ok(()) } /// Flush and get record count pub fn finish(mut self) -> std::io::Result { self.writer.flush()?; Ok(self.record_count) } } /// Binary log reader pub struct LogReader { reader: R, version: u8, } impl LogReader { /// Open a log for reading pub fn new(mut reader: R) -> std::io::Result { // Read and verify header let mut magic = [0u8; 4]; reader.read_exact(&mut magic)?; if &magic != LOG_MAGIC { return Err(std::io::Error::new( std::io::ErrorKind::InvalidData, "Invalid magic header", )); } let mut version = [0u8; 1]; reader.read_exact(&mut version)?; Ok(Self { reader, version: version[0], }) } /// Get schema version pub fn version(&self) -> u8 { self.version } /// Read next record pub fn read_record(&mut self) -> std::io::Result> { // Read record type let mut type_byte = [0u8; 1]; match self.reader.read_exact(&mut type_byte) { Ok(()) => (), Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => return Ok(None), Err(e) => return Err(e), } let record_type = RecordType::try_from(type_byte[0]).map_err(|_| { std::io::Error::new(std::io::ErrorKind::InvalidData, "Unknown record type") })?; // Read length let mut len_bytes = [0u8; 4]; self.reader.read_exact(&mut len_bytes)?; let len = u32::from_le_bytes(len_bytes) as usize; // Read payload let mut payload = vec![0u8; len]; self.reader.read_exact(&mut payload)?; // Read and verify checksum let mut crc_bytes = [0u8; 4]; self.reader.read_exact(&mut crc_bytes)?; let stored_crc = u32::from_le_bytes(crc_bytes); let computed_crc = crc32fast::hash(&payload); if stored_crc != computed_crc { return Err(std::io::Error::new( std::io::ErrorKind::InvalidData, "CRC mismatch", )); } // Parse payload let record = match record_type { RecordType::SyndromeRound => { let round: SyndromeRound = serde_json::from_slice(&payload) .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?; LogRecord::Syndrome(round) } RecordType::GateDecision => { let decision: GateDecision = serde_json::from_slice(&payload) .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?; LogRecord::Decision(decision) } RecordType::MitigationAction => { let action: MitigationAction = serde_json::from_slice(&payload) .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?; LogRecord::Action(action) } _ => LogRecord::Unknown(payload), }; Ok(Some(record)) } } /// A record from the log #[derive(Debug, Clone)] pub enum LogRecord { /// Syndrome round record Syndrome(SyndromeRound), /// Gate decision record Decision(GateDecision), /// Mitigation action record Action(MitigationAction), /// Unknown record type (for forward compatibility) Unknown(Vec), } #[cfg(test)] mod tests { use super::*; use std::io::Cursor; #[test] fn test_syndrome_round() { let mut round = SyndromeRound::new(1, 5); round.add_event(DetectorEvent { detector_id: 0, fired: true, confidence: 0.99, coords: Some(DetectorCoords { x: 0, y: 0, t: 0 }), }); round.add_event(DetectorEvent { detector_id: 1, fired: false, confidence: 1.0, coords: None, }); assert_eq!(round.fired_count(), 1); } #[test] fn test_permit_token_validity() { let token = PermitToken { token_id: 1, issued_at_round: 100, issued_at_ns: 1000000, ttl_ns: 100000, region_mask: 0xFF, confidence: 0.95, min_cut_value: 5.5, }; assert!(token.is_valid(1050000)); assert!(!token.is_valid(1200000)); assert_eq!(token.remaining_ttl_ns(1050000), 50000); } #[test] fn test_log_roundtrip() { let mut buffer = Vec::new(); // Write { let mut writer = LogWriter::new(&mut buffer).unwrap(); let round = SyndromeRound::new(1, 5); writer.write_syndrome(&round).unwrap(); writer.finish().unwrap(); } // Read { let mut reader = LogReader::new(Cursor::new(&buffer)).unwrap(); assert_eq!(reader.version(), SCHEMA_VERSION); let record = reader.read_record().unwrap().unwrap(); match record { LogRecord::Syndrome(round) => { assert_eq!(round.round_id, 1); assert_eq!(round.code_distance, 5); } _ => panic!("Expected syndrome record"), } } } }