use crate::syndrome::DetectorBitmap;
use std::time::Duration;
#[derive(Debug, Clone, thiserror::Error)]
pub enum TraitError {
#[error("Source exhausted")]
SourceExhausted,
#[error("Hardware error: {0}")]
HardwareError(String),
#[error("Configuration error: {0}")]
ConfigError(String),
#[error("Timeout after {0:?}")]
Timeout(Duration),
}
pub type TraitResult<T> = Result<T, TraitError>;
pub trait SyndromeSource: Send {
fn sample(&mut self) -> TraitResult<DetectorBitmap>;
fn num_detectors(&self) -> usize;
fn code_distance(&self) -> Option<usize> {
None
}
fn is_exhausted(&self) -> bool {
false
}
fn reset(&mut self) -> TraitResult<()> {
Err(TraitError::ConfigError("Reset not supported".into()))
}
fn metadata(&self) -> SourceMetadata {
SourceMetadata::default()
}
}
#[derive(Debug, Clone, Default)]
pub struct SourceMetadata {
pub name: String,
pub code_distance: Option<usize>,
pub error_rate: Option<f64>,
pub total_rounds: Option<u64>,
pub version: String,
}
pub trait TelemetrySource: Send {
fn snapshot(&self) -> TelemetrySnapshot;
fn has_alert(&self) -> bool {
false
}
}
#[derive(Debug, Clone, Default)]
pub struct TelemetrySnapshot {
pub timestamp_ns: u64,
pub fridge_temp_k: Option<f64>,
pub qubit_temps: Vec<f64>,
pub readout_fidelity: Vec<f64>,
pub gate_errors: Vec<f64>,
pub custom: Vec<(String, f64)>,
}
pub trait GateEngine: Send {
fn process(&mut self, syndrome: &DetectorBitmap) -> GateDecision;
fn risk_assessment(&self) -> RiskAssessment;
fn update_config(&mut self, config: GateConfig) -> TraitResult<()>;
fn statistics(&self) -> EngineStatistics;
fn reset(&mut self);
}
#[derive(Debug, Clone, PartialEq)]
pub enum GateDecision {
Permit {
confidence: f64,
ttl_ns: u64,
reason: Option<String>,
},
Defer {
wait_ns: u64,
uncertainty: f64,
},
Deny {
risk_level: f64,
recommended_action: String,
affected_regions: Vec<u32>,
},
}
impl Default for GateDecision {
fn default() -> Self {
GateDecision::Defer {
wait_ns: 1000,
uncertainty: 1.0,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct RiskAssessment {
pub overall_risk: f64,
pub structural_risk: f64,
pub temporal_risk: f64,
pub spatial_risk: f64,
pub region_risks: Vec<(u32, f64)>,
pub confidence: f64,
}
#[derive(Debug, Clone)]
pub struct GateConfig {
pub min_cut_threshold: f64,
pub max_shift: f64,
pub tau_permit: f64,
pub tau_deny: f64,
pub permit_ttl_ns: u64,
}
impl Default for GateConfig {
fn default() -> Self {
Self {
min_cut_threshold: 5.0,
max_shift: 0.2,
tau_permit: 0.3,
tau_deny: 0.7,
permit_ttl_ns: 100_000,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct EngineStatistics {
pub total_rounds: u64,
pub permits: u64,
pub defers: u64,
pub denies: u64,
pub avg_process_ns: f64,
pub p99_process_ns: u64,
pub p999_process_ns: u64,
pub max_process_ns: u64,
}
pub trait ActionSink: Send {
fn execute(&mut self, action: &MitigationAction) -> TraitResult<ActionResult>;
fn supports(&self, action_type: ActionType) -> bool;
fn capabilities(&self) -> ActionCapabilities;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ActionType {
QuarantineRegion,
IncreaseSyndromeRounds,
SwitchDecodeMode,
TriggerReweight,
PauseLearningWrites,
LogEvent,
AlertOperator,
InjectTestError,
}
#[derive(Debug, Clone)]
pub struct MitigationAction {
pub action_type: ActionType,
pub target_regions: Vec<u32>,
pub parameters: ActionParameters,
pub priority: u8,
pub preconditions: Vec<Precondition>,
pub estimated_cost: ActionCost,
pub expected_effect: String,
}
#[derive(Debug, Clone, Default)]
pub struct ActionParameters {
pub duration_ns: Option<u64>,
pub intensity: Option<f64>,
pub custom: Vec<(String, String)>,
}
#[derive(Debug, Clone)]
pub enum Precondition {
RiskAbove(f64),
RiskBelow(f64),
RegionState(u32, String),
TimeSinceLastAction(ActionType, Duration),
Custom(String),
}
#[derive(Debug, Clone, Default)]
pub struct ActionCost {
pub time_ns: u64,
pub qubit_overhead: u32,
pub fidelity_impact: f64,
pub throughput_impact: f64,
}
#[derive(Debug, Clone)]
pub struct ActionResult {
pub success: bool,
pub actual_cost: ActionCost,
pub notes: Vec<String>,
}
#[derive(Debug, Clone, Default)]
pub struct ActionCapabilities {
pub supported_actions: Vec<ActionType>,
pub max_concurrent: u32,
pub min_interval_ns: u64,
}
pub struct NullSyndromeSource {
num_detectors: usize,
}
impl NullSyndromeSource {
pub fn new(num_detectors: usize) -> Self {
Self { num_detectors }
}
}
impl SyndromeSource for NullSyndromeSource {
fn sample(&mut self) -> TraitResult<DetectorBitmap> {
Ok(DetectorBitmap::new(self.num_detectors))
}
fn num_detectors(&self) -> usize {
self.num_detectors
}
}
pub struct LoggingActionSink {
log_prefix: String,
}
impl LoggingActionSink {
pub fn new(prefix: &str) -> Self {
Self {
log_prefix: prefix.to_string(),
}
}
}
impl ActionSink for LoggingActionSink {
fn execute(&mut self, action: &MitigationAction) -> TraitResult<ActionResult> {
println!(
"{}: {:?} on regions {:?}",
self.log_prefix, action.action_type, action.target_regions
);
Ok(ActionResult {
success: true,
actual_cost: ActionCost::default(),
notes: vec![],
})
}
fn supports(&self, _action_type: ActionType) -> bool {
true
}
fn capabilities(&self) -> ActionCapabilities {
ActionCapabilities {
supported_actions: vec![
ActionType::LogEvent,
ActionType::AlertOperator,
],
max_concurrent: 100,
min_interval_ns: 0,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_null_syndrome_source() {
let mut source = NullSyndromeSource::new(100);
let syndrome = source.sample().unwrap();
assert_eq!(syndrome.fired_count(), 0);
assert_eq!(source.num_detectors(), 100);
}
#[test]
fn test_gate_decision_default() {
let decision = GateDecision::default();
match decision {
GateDecision::Defer { .. } => (),
_ => panic!("Default should be Defer"),
}
}
#[test]
fn test_logging_action_sink() {
let mut sink = LoggingActionSink::new("[TEST]");
let action = MitigationAction {
action_type: ActionType::LogEvent,
target_regions: vec![1, 2, 3],
parameters: ActionParameters::default(),
priority: 5,
preconditions: vec![],
estimated_cost: ActionCost::default(),
expected_effect: "Log the event".into(),
};
let result = sink.execute(&action).unwrap();
assert!(result.success);
}
}