Skip to main content

quantrs2_circuit/
distributed.rs

1//! Distributed Circuit Execution Framework
2//!
3//! This module provides infrastructure for executing quantum circuits across
4//! multiple quantum devices, simulators, or cloud services in a distributed manner.
5
6use crate::builder::Circuit;
7use quantrs2_core::{
8    error::{QuantRS2Error, QuantRS2Result},
9    qubit::QubitId,
10};
11use scirs2_core::Complex64;
12use std::collections::HashMap;
13use std::time::{Duration, Instant};
14
15/// Internal record tracking a submitted job's lifecycle.
16#[derive(Debug, Clone)]
17struct JobRecord {
18    /// Current execution status
19    status: ExecutionStatus,
20    /// Identifiers of the backends that were selected for this job
21    backends: Vec<String>,
22    /// Monotonic instant at which the job was submitted
23    submitted_at: std::time::Instant,
24}
25
26/// A distributed quantum circuit execution engine
27///
28/// This manages execution of quantum circuits across multiple backends,
29/// handling load balancing, fault tolerance, and result aggregation.
30#[derive(Debug)]
31pub struct DistributedExecutor {
32    /// Available execution backends
33    pub backends: Vec<ExecutionBackend>,
34    /// Load balancing strategy
35    pub load_balancer: LoadBalancer,
36    /// Fault tolerance configuration
37    pub fault_tolerance: FaultToleranceConfig,
38    /// Execution scheduling policy
39    pub scheduler: ExecutionScheduler,
40    /// Resource management
41    pub resource_manager: ResourceManager,
42    /// In-memory job registry mapping job ID → job record
43    job_registry: HashMap<String, JobRecord>,
44}
45
46/// A quantum execution backend (device, simulator, or cloud service)
47#[derive(Debug, Clone)]
48pub struct ExecutionBackend {
49    /// Unique identifier for the backend
50    pub id: String,
51    /// Type of backend
52    pub backend_type: BackendType,
53    /// Current status
54    pub status: BackendStatus,
55    /// Performance characteristics
56    pub performance: BackendPerformance,
57    /// Queue information
58    pub queue_info: QueueInfo,
59    /// Supported operations
60    pub capabilities: BackendCapabilities,
61    /// Network configuration
62    pub network_config: NetworkConfig,
63}
64
65/// Types of quantum execution backends
66#[derive(Debug, Clone, PartialEq)]
67pub enum BackendType {
68    /// Physical quantum hardware
69    Hardware {
70        vendor: String,
71        model: String,
72        location: String,
73    },
74    /// Quantum simulator
75    Simulator {
76        simulator_type: SimulatorType,
77        host: String,
78    },
79    /// Cloud quantum service
80    CloudService {
81        provider: String,
82        service_name: String,
83        region: String,
84    },
85    /// Hybrid classical-quantum system
86    Hybrid {
87        quantum_backend: Box<Self>,
88        classical_resources: ClassicalResources,
89    },
90}
91
92/// Types of quantum simulators
93#[derive(Debug, Clone, PartialEq, Eq)]
94pub enum SimulatorType {
95    StateVector,
96    DensityMatrix,
97    TensorNetwork,
98    StabilunerformCode,
99    MatrixProductState,
100    Custom(String),
101}
102
103/// Classical computing resources for hybrid systems
104#[derive(Debug, Clone, PartialEq)]
105pub struct ClassicalResources {
106    /// CPU cores available
107    pub cpu_cores: usize,
108    /// Memory in GB
109    pub memory_gb: f64,
110    /// GPU information
111    pub gpus: Vec<GPUInfo>,
112    /// Storage capacity in GB
113    pub storage_gb: f64,
114}
115
116/// GPU information
117#[derive(Debug, Clone, PartialEq)]
118pub struct GPUInfo {
119    /// GPU model
120    pub model: String,
121    /// Memory in GB
122    pub memory_gb: f64,
123    /// Compute capability
124    pub compute_capability: String,
125}
126
127/// Current status of a backend
128#[derive(Debug, Clone, PartialEq, Eq)]
129pub enum BackendStatus {
130    /// Available for execution
131    Available,
132    /// Currently busy
133    Busy,
134    /// Temporarily unavailable
135    Unavailable,
136    /// Under maintenance
137    Maintenance,
138    /// Offline/disconnected
139    Offline,
140    /// Error state
141    Error(String),
142}
143
144/// Performance characteristics of a backend
145#[derive(Debug, Clone)]
146pub struct BackendPerformance {
147    /// Maximum number of qubits
148    pub max_qubits: usize,
149    /// Maximum circuit depth
150    pub max_depth: usize,
151    /// Gate fidelities
152    pub gate_fidelities: HashMap<String, f64>,
153    /// Coherence times (in microseconds)
154    pub coherence_times: HashMap<String, f64>,
155    /// Execution time estimates
156    pub execution_time_model: ExecutionTimeModel,
157    /// Throughput (circuits per second)
158    pub throughput: f64,
159}
160
161/// Model for predicting execution times
162#[derive(Debug, Clone)]
163pub struct ExecutionTimeModel {
164    /// Base execution time (seconds)
165    pub base_time: f64,
166    /// Time per gate (seconds)
167    pub time_per_gate: f64,
168    /// Time per qubit (seconds)
169    pub time_per_qubit: f64,
170    /// Time per measurement (seconds)
171    pub time_per_measurement: f64,
172    /// Network latency (seconds)
173    pub network_latency: f64,
174}
175
176/// Queue information for a backend
177#[derive(Debug, Clone)]
178pub struct QueueInfo {
179    /// Current queue length
180    pub queue_length: usize,
181    /// Estimated wait time (seconds)
182    pub estimated_wait_time: f64,
183    /// Maximum queue size
184    pub max_queue_size: usize,
185    /// Priority levels supported
186    pub priority_levels: Vec<Priority>,
187}
188
189/// Execution priority levels
190#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
191pub enum Priority {
192    Low,
193    Normal,
194    High,
195    Critical,
196}
197
198/// Backend capabilities
199#[derive(Debug, Clone)]
200pub struct BackendCapabilities {
201    /// Supported gate set
202    pub supported_gates: Vec<String>,
203    /// Supports mid-circuit measurements
204    pub mid_circuit_measurements: bool,
205    /// Supports classical control flow
206    pub classical_control: bool,
207    /// Supports reset operations
208    pub reset_operations: bool,
209    /// Connectivity graph
210    pub connectivity: ConnectivityGraph,
211    /// Noise characteristics
212    pub noise_model: Option<NoiseCharacteristics>,
213}
214
215/// Qubit connectivity graph
216#[derive(Debug, Clone)]
217pub struct ConnectivityGraph {
218    /// Number of qubits
219    pub num_qubits: usize,
220    /// Edges representing allowed two-qubit gates
221    pub edges: Vec<(usize, usize)>,
222    /// Connectivity type
223    pub topology: TopologyType,
224}
225
226/// Types of qubit connectivity topologies
227#[derive(Debug, Clone, PartialEq)]
228pub enum TopologyType {
229    /// Linear chain
230    Linear,
231    /// 2D grid
232    Grid2D { rows: usize, cols: usize },
233    /// All-to-all connectivity
234    AllToAll,
235    /// Random graph
236    Random { density: f64 },
237    /// Custom topology
238    Custom,
239}
240
241/// Noise characteristics of a backend
242#[derive(Debug, Clone)]
243pub struct NoiseCharacteristics {
244    /// Single-qubit gate error rates
245    pub single_qubit_errors: HashMap<String, f64>,
246    /// Two-qubit gate error rates
247    pub two_qubit_errors: HashMap<String, f64>,
248    /// Measurement error rates
249    pub measurement_errors: Vec<f64>,
250    /// Decoherence times
251    pub decoherence_times: Vec<f64>,
252}
253
254/// Network configuration for backend communication
255#[derive(Debug, Clone)]
256pub struct NetworkConfig {
257    /// Endpoint URL
258    pub endpoint: String,
259    /// Authentication credentials
260    pub credentials: Credentials,
261    /// Timeout settings
262    pub timeouts: TimeoutConfig,
263    /// Retry policy
264    pub retry_policy: RetryPolicy,
265}
266
267/// Authentication credentials
268#[derive(Debug, Clone)]
269pub struct Credentials {
270    /// Authentication type
271    pub auth_type: AuthenticationType,
272    /// API key (if applicable)
273    pub api_key: Option<String>,
274    /// Token (if applicable)
275    pub token: Option<String>,
276    /// Username/password (if applicable)
277    pub username_password: Option<(String, String)>,
278}
279
280/// Types of authentication
281#[derive(Debug, Clone, PartialEq, Eq)]
282pub enum AuthenticationType {
283    ApiKey,
284    Token,
285    UsernamePassword,
286    Certificate,
287    None,
288}
289
290/// Timeout configuration
291#[derive(Debug, Clone)]
292pub struct TimeoutConfig {
293    /// Connection timeout (seconds)
294    pub connection_timeout: f64,
295    /// Request timeout (seconds)
296    pub request_timeout: f64,
297    /// Total timeout (seconds)
298    pub total_timeout: f64,
299}
300
301/// Retry policy configuration
302#[derive(Debug, Clone)]
303pub struct RetryPolicy {
304    /// Maximum number of retries
305    pub max_retries: usize,
306    /// Base delay between retries (seconds)
307    pub base_delay: f64,
308    /// Backoff strategy
309    pub backoff_strategy: BackoffStrategy,
310    /// Retryable error types
311    pub retryable_errors: Vec<ErrorType>,
312}
313
314/// Backoff strategies for retries
315#[derive(Debug, Clone, PartialEq)]
316pub enum BackoffStrategy {
317    /// Fixed delay
318    Fixed,
319    /// Linear backoff
320    Linear,
321    /// Exponential backoff
322    Exponential { multiplier: f64 },
323    /// Random jitter
324    Jitter { max_jitter: f64 },
325}
326
327/// Types of errors that can be retried
328#[derive(Debug, Clone, PartialEq, Eq)]
329pub enum ErrorType {
330    NetworkError,
331    TimeoutError,
332    ServiceUnavailable,
333    RateLimited,
334    InternalServerError,
335}
336
337/// Load balancing strategies
338#[derive(Debug, Clone)]
339pub struct LoadBalancer {
340    /// Balancing strategy
341    pub strategy: LoadBalancingStrategy,
342    /// Health check configuration
343    pub health_check: HealthCheckConfig,
344    /// Metrics collection
345    pub metrics: MetricsConfig,
346}
347
348/// Load balancing strategies
349#[derive(Debug, Clone, PartialEq)]
350pub enum LoadBalancingStrategy {
351    /// Round robin
352    RoundRobin,
353    /// Least connections
354    LeastConnections,
355    /// Least queue time
356    LeastQueueTime,
357    /// Best performance
358    BestPerformance,
359    /// Weighted round robin
360    WeightedRoundRobin(HashMap<String, f64>),
361    /// Custom strategy
362    Custom(String),
363}
364
365/// Health check configuration
366#[derive(Debug, Clone)]
367pub struct HealthCheckConfig {
368    /// Health check interval (seconds)
369    pub interval: f64,
370    /// Health check timeout (seconds)
371    pub timeout: f64,
372    /// Number of failed checks before marking unhealthy
373    pub failure_threshold: usize,
374    /// Number of successful checks before marking healthy
375    pub success_threshold: usize,
376}
377
378/// Metrics collection configuration
379#[derive(Debug, Clone)]
380pub struct MetricsConfig {
381    /// Enable metrics collection
382    pub enabled: bool,
383    /// Metrics collection interval (seconds)
384    pub collection_interval: f64,
385    /// Metrics retention period (seconds)
386    pub retention_period: f64,
387    /// Metrics storage backend
388    pub storage_backend: MetricsStorage,
389}
390
391/// Metrics storage backends
392#[derive(Debug, Clone, PartialEq, Eq)]
393pub enum MetricsStorage {
394    InMemory,
395    File(String),
396    Database(String),
397    CloudStorage(String),
398}
399
400/// Fault tolerance configuration
401#[derive(Debug, Clone)]
402pub struct FaultToleranceConfig {
403    /// Enable automatic failover
404    pub enable_failover: bool,
405    /// Circuit redundancy level
406    pub redundancy_level: usize,
407    /// Error correction strategy
408    pub error_correction: ErrorCorrectionStrategy,
409    /// Failure detection configuration
410    pub failure_detection: FailureDetectionConfig,
411}
412
413/// Error correction strategies
414#[derive(Debug, Clone, PartialEq, Eq)]
415pub enum ErrorCorrectionStrategy {
416    /// No error correction
417    None,
418    /// Majority voting
419    MajorityVoting,
420    /// Quantum error correction
421    QuantumErrorCorrection,
422    /// Classical post-processing
423    ClassicalPostProcessing,
424    /// Custom strategy
425    Custom(String),
426}
427
428/// Failure detection configuration
429#[derive(Debug, Clone)]
430pub struct FailureDetectionConfig {
431    /// Detection methods
432    pub detection_methods: Vec<FailureDetectionMethod>,
433    /// Detection threshold
434    pub detection_threshold: f64,
435    /// Detection window (seconds)
436    pub detection_window: f64,
437}
438
439/// Failure detection methods
440#[derive(Debug, Clone, PartialEq, Eq)]
441pub enum FailureDetectionMethod {
442    /// Error rate monitoring
443    ErrorRateMonitoring,
444    /// Latency monitoring
445    LatencyMonitoring,
446    /// Result validation
447    ResultValidation,
448    /// Health check failures
449    HealthCheckFailures,
450}
451
452/// Execution scheduler
453#[derive(Debug, Clone)]
454pub struct ExecutionScheduler {
455    /// Scheduling policy
456    pub policy: SchedulingPolicy,
457    /// Priority queue configuration
458    pub priority_queue: PriorityQueueConfig,
459    /// Resource allocation strategy
460    pub resource_allocation: ResourceAllocationStrategy,
461}
462
463/// Scheduling policies
464#[derive(Debug, Clone, PartialEq, Eq)]
465pub enum SchedulingPolicy {
466    /// First-come, first-served
467    FCFS,
468    /// Shortest job first
469    SJF,
470    /// Priority-based scheduling
471    Priority,
472    /// Fair share scheduling
473    FairShare,
474    /// Deadline-aware scheduling
475    DeadlineAware,
476    /// Custom policy
477    Custom(String),
478}
479
480/// Priority queue configuration
481#[derive(Debug, Clone)]
482pub struct PriorityQueueConfig {
483    /// Maximum queue size per priority
484    pub max_size_per_priority: HashMap<Priority, usize>,
485    /// Aging factor for priority adjustment
486    pub aging_factor: f64,
487    /// Priority boost interval (seconds)
488    pub priority_boost_interval: f64,
489}
490
491/// Resource allocation strategies
492#[derive(Debug, Clone, PartialEq, Eq)]
493pub enum ResourceAllocationStrategy {
494    /// Best fit
495    BestFit,
496    /// First fit
497    FirstFit,
498    /// Worst fit
499    WorstFit,
500    /// Next fit
501    NextFit,
502    /// Custom allocation
503    Custom(String),
504}
505
506/// Resource manager
507#[derive(Debug, Clone)]
508pub struct ResourceManager {
509    /// Resource pool
510    pub resource_pool: ResourcePool,
511    /// Allocation policies
512    pub allocation_policies: AllocationPolicies,
513    /// Usage tracking
514    pub usage_tracking: UsageTracking,
515}
516
517/// Resource pool information
518#[derive(Debug, Clone)]
519pub struct ResourcePool {
520    /// Total available qubits across all backends
521    pub total_qubits: usize,
522    /// Available execution slots
523    pub available_slots: usize,
524    /// Memory pool (in GB)
525    pub memory_pool: f64,
526    /// Compute pool (in CPU hours)
527    pub compute_pool: f64,
528}
529
530/// Resource allocation policies
531#[derive(Debug, Clone)]
532pub struct AllocationPolicies {
533    /// Maximum qubits per user
534    pub max_qubits_per_user: Option<usize>,
535    /// Maximum execution time per job
536    pub max_execution_time: Option<f64>,
537    /// Fair share allocation
538    pub fair_share: bool,
539    /// Reserved resources for high-priority jobs
540    pub reserved_resources: f64,
541}
542
543/// Usage tracking configuration
544#[derive(Debug, Clone)]
545pub struct UsageTracking {
546    /// Track per-user usage
547    pub per_user_tracking: bool,
548    /// Track per-project usage
549    pub per_project_tracking: bool,
550    /// Usage reporting interval (seconds)
551    pub reporting_interval: f64,
552    /// Usage data retention (seconds)
553    pub retention_period: f64,
554}
555
556/// Distributed execution job
557#[derive(Debug, Clone)]
558pub struct DistributedJob<const N: usize> {
559    /// Job identifier
560    pub id: String,
561    /// Circuit to execute
562    pub circuit: Circuit<N>,
563    /// Execution parameters
564    pub parameters: ExecutionParameters,
565    /// Job priority
566    pub priority: Priority,
567    /// Target backends (if specified)
568    pub target_backends: Option<Vec<String>>,
569    /// Submission time
570    pub submitted_at: Instant,
571    /// Deadline (if any)
572    pub deadline: Option<Instant>,
573}
574
575/// Execution parameters for a job
576#[derive(Debug, Clone)]
577pub struct ExecutionParameters {
578    /// Number of shots
579    pub shots: usize,
580    /// Optimization level
581    pub optimization_level: usize,
582    /// Error mitigation techniques
583    pub error_mitigation: Vec<ErrorMitigation>,
584    /// Result format
585    pub result_format: ResultFormat,
586    /// Memory requirements
587    pub memory_requirement: Option<f64>,
588}
589
590/// Error mitigation techniques
591#[derive(Debug, Clone, PartialEq, Eq)]
592pub enum ErrorMitigation {
593    /// Readout error mitigation
594    ReadoutErrorMitigation,
595    /// Zero-noise extrapolation
596    ZeroNoiseExtrapolation,
597    /// Clifford data regression
598    CliffordDataRegression,
599    /// Symmetry verification
600    SymmetryVerification,
601    /// Custom mitigation
602    Custom(String),
603}
604
605/// Result format options
606#[derive(Debug, Clone, PartialEq, Eq)]
607pub enum ResultFormat {
608    /// Raw counts
609    Counts,
610    /// Probabilities
611    Probabilities,
612    /// Statevector (if available)
613    Statevector,
614    /// Expectation values
615    ExpectationValues,
616    /// Custom format
617    Custom(String),
618}
619
620/// Execution result from distributed system
621#[derive(Debug, Clone)]
622pub struct DistributedResult {
623    /// Job ID
624    pub job_id: String,
625    /// Execution status
626    pub status: ExecutionStatus,
627    /// Results from each backend
628    pub backend_results: HashMap<String, BackendResult>,
629    /// Aggregated final result
630    pub final_result: Option<AggregatedResult>,
631    /// Execution metadata
632    pub metadata: ExecutionMetadata,
633}
634
635/// Execution status
636#[derive(Debug, Clone, PartialEq, Eq)]
637pub enum ExecutionStatus {
638    /// Job queued
639    Queued,
640    /// Job running
641    Running,
642    /// Job completed successfully
643    Completed,
644    /// Job failed
645    Failed(String),
646    /// Job cancelled
647    Cancelled,
648    /// Job timed out
649    TimedOut,
650}
651
652/// Result from a single backend
653#[derive(Debug, Clone)]
654pub struct BackendResult {
655    /// Backend ID
656    pub backend_id: String,
657    /// Execution status on this backend
658    pub status: ExecutionStatus,
659    /// Raw measurement results
660    pub measurements: Option<Vec<Vec<bool>>>,
661    /// Probability distributions
662    pub probabilities: Option<HashMap<String, f64>>,
663    /// Execution time
664    pub execution_time: Duration,
665    /// Error information (if any)
666    pub error: Option<String>,
667}
668
669/// Aggregated result across multiple backends
670#[derive(Debug, Clone)]
671pub struct AggregatedResult {
672    /// Combined measurement statistics
673    pub combined_measurements: HashMap<String, f64>,
674    /// Error estimates
675    pub error_estimates: HashMap<String, f64>,
676    /// Confidence intervals
677    pub confidence_intervals: HashMap<String, (f64, f64)>,
678    /// Quality metrics
679    pub quality_metrics: QualityMetrics,
680}
681
682/// Quality metrics for results
683#[derive(Debug, Clone)]
684pub struct QualityMetrics {
685    /// Statistical significance
686    pub statistical_significance: f64,
687    /// Consistency across backends
688    pub consistency_score: f64,
689    /// Estimated fidelity
690    pub estimated_fidelity: f64,
691    /// Error mitigation effectiveness
692    pub mitigation_effectiveness: f64,
693}
694
695/// Execution metadata
696#[derive(Debug, Clone)]
697pub struct ExecutionMetadata {
698    /// Total execution time
699    pub total_time: Duration,
700    /// Queue wait time
701    pub queue_time: Duration,
702    /// Backends used
703    pub backends_used: Vec<String>,
704    /// Resource usage
705    pub resource_usage: ResourceUsage,
706    /// Cost information
707    pub cost_info: Option<CostInfo>,
708}
709
710/// Resource usage information
711#[derive(Debug, Clone)]
712pub struct ResourceUsage {
713    /// CPU hours used
714    pub cpu_hours: f64,
715    /// Memory-hours used
716    pub memory_hours: f64,
717    /// Qubit-hours used
718    pub qubit_hours: f64,
719    /// Network bandwidth used (GB)
720    pub network_usage: f64,
721}
722
723/// Cost information
724#[derive(Debug, Clone)]
725pub struct CostInfo {
726    /// Total cost
727    pub total_cost: f64,
728    /// Cost breakdown by resource
729    pub cost_breakdown: HashMap<String, f64>,
730    /// Currency
731    pub currency: String,
732}
733
734impl DistributedExecutor {
735    /// Create a new distributed executor
736    #[must_use]
737    pub fn new() -> Self {
738        Self {
739            backends: Vec::new(),
740            load_balancer: LoadBalancer {
741                strategy: LoadBalancingStrategy::RoundRobin,
742                health_check: HealthCheckConfig {
743                    interval: 30.0,
744                    timeout: 5.0,
745                    failure_threshold: 3,
746                    success_threshold: 2,
747                },
748                metrics: MetricsConfig {
749                    enabled: true,
750                    collection_interval: 60.0,
751                    retention_period: 3600.0 * 24.0, // 24 hours
752                    storage_backend: MetricsStorage::InMemory,
753                },
754            },
755            fault_tolerance: FaultToleranceConfig {
756                enable_failover: true,
757                redundancy_level: 1,
758                error_correction: ErrorCorrectionStrategy::MajorityVoting,
759                failure_detection: FailureDetectionConfig {
760                    detection_methods: vec![
761                        FailureDetectionMethod::ErrorRateMonitoring,
762                        FailureDetectionMethod::LatencyMonitoring,
763                    ],
764                    detection_threshold: 0.1,
765                    detection_window: 300.0,
766                },
767            },
768            scheduler: ExecutionScheduler {
769                policy: SchedulingPolicy::Priority,
770                priority_queue: PriorityQueueConfig {
771                    max_size_per_priority: {
772                        let mut map = HashMap::new();
773                        map.insert(Priority::Critical, 10);
774                        map.insert(Priority::High, 50);
775                        map.insert(Priority::Normal, 200);
776                        map.insert(Priority::Low, 1000);
777                        map
778                    },
779                    aging_factor: 0.1,
780                    priority_boost_interval: 3600.0, // 1 hour
781                },
782                resource_allocation: ResourceAllocationStrategy::BestFit,
783            },
784            resource_manager: ResourceManager {
785                resource_pool: ResourcePool {
786                    total_qubits: 0,
787                    available_slots: 0,
788                    memory_pool: 0.0,
789                    compute_pool: 0.0,
790                },
791                allocation_policies: AllocationPolicies {
792                    max_qubits_per_user: Some(100),
793                    max_execution_time: Some(3600.0), // 1 hour
794                    fair_share: true,
795                    reserved_resources: 0.1, // 10% reserved
796                },
797                usage_tracking: UsageTracking {
798                    per_user_tracking: true,
799                    per_project_tracking: true,
800                    reporting_interval: 3600.0,             // 1 hour
801                    retention_period: 3600.0 * 24.0 * 30.0, // 30 days
802                },
803            },
804            job_registry: HashMap::new(),
805        }
806    }
807
808    /// Add a backend to the distributed executor
809    pub fn add_backend(&mut self, backend: ExecutionBackend) -> QuantRS2Result<()> {
810        // Validate backend configuration
811        if backend.id.is_empty() {
812            return Err(QuantRS2Error::InvalidInput(
813                "Backend ID cannot be empty".to_string(),
814            ));
815        }
816
817        // Check for duplicate IDs
818        if self.backends.iter().any(|b| b.id == backend.id) {
819            return Err(QuantRS2Error::InvalidInput(format!(
820                "Backend with ID '{}' already exists",
821                backend.id
822            )));
823        }
824
825        // Update resource pool
826        self.resource_manager.resource_pool.total_qubits += backend.performance.max_qubits;
827        self.resource_manager.resource_pool.available_slots += 1;
828
829        self.backends.push(backend);
830        Ok(())
831    }
832
833    /// Submit a job for distributed execution
834    pub fn submit_job<const N: usize>(&mut self, job: DistributedJob<N>) -> QuantRS2Result<String> {
835        // Validate job
836        if job.circuit.num_gates() == 0 {
837            return Err(QuantRS2Error::InvalidInput(
838                "Cannot submit empty circuit".to_string(),
839            ));
840        }
841
842        // Check resource requirements
843        let required_qubits = job.circuit.num_qubits();
844        if required_qubits > self.resource_manager.resource_pool.total_qubits {
845            return Err(QuantRS2Error::UnsupportedQubits(
846                required_qubits,
847                format!(
848                    "Maximum available qubits: {}",
849                    self.resource_manager.resource_pool.total_qubits
850                ),
851            ));
852        }
853
854        // Select appropriate backends
855        let selected_backends = self.select_backends(&job)?;
856        if selected_backends.is_empty() {
857            return Err(QuantRS2Error::BackendExecutionFailed(
858                "No suitable backends available".to_string(),
859            ));
860        }
861
862        let job_id = job.id.clone();
863        let submitted_at = job.submitted_at;
864
865        // Register the job as queued and track the selected backends
866        self.job_registry.insert(
867            job_id.clone(),
868            JobRecord {
869                status: ExecutionStatus::Queued,
870                backends: selected_backends,
871                submitted_at,
872            },
873        );
874
875        Ok(job_id)
876    }
877
878    /// Select appropriate backends for a job
879    fn select_backends<const N: usize>(
880        &self,
881        job: &DistributedJob<N>,
882    ) -> QuantRS2Result<Vec<String>> {
883        let mut suitable_backends = Vec::new();
884
885        for backend in &self.backends {
886            if self.is_backend_suitable(backend, job) {
887                suitable_backends.push(backend.id.clone());
888            }
889        }
890
891        // Apply load balancing strategy
892        match self.load_balancer.strategy {
893            LoadBalancingStrategy::RoundRobin => {
894                // Simple round-robin selection
895                suitable_backends.truncate(self.fault_tolerance.redundancy_level.max(1));
896            }
897            LoadBalancingStrategy::LeastQueueTime => {
898                // Sort by queue time; entries without a matching backend retain
899                // their relative order (treated as having zero wait time).
900                suitable_backends.sort_by(|a, b| {
901                    let wait_a = self
902                        .backends
903                        .iter()
904                        .find(|backend| backend.id == *a)
905                        .map(|backend| backend.queue_info.estimated_wait_time)
906                        .unwrap_or(0.0);
907                    let wait_b = self
908                        .backends
909                        .iter()
910                        .find(|backend| backend.id == *b)
911                        .map(|backend| backend.queue_info.estimated_wait_time)
912                        .unwrap_or(0.0);
913                    wait_a
914                        .partial_cmp(&wait_b)
915                        .unwrap_or(std::cmp::Ordering::Equal)
916                });
917                suitable_backends.truncate(self.fault_tolerance.redundancy_level.max(1));
918            }
919            _ => {
920                // Default to first available
921                suitable_backends.truncate(1);
922            }
923        }
924
925        Ok(suitable_backends)
926    }
927
928    /// Check if a backend is suitable for a job
929    fn is_backend_suitable<const N: usize>(
930        &self,
931        backend: &ExecutionBackend,
932        job: &DistributedJob<N>,
933    ) -> bool {
934        // Check status
935        if backend.status != BackendStatus::Available {
936            return false;
937        }
938
939        // Check qubit requirements
940        if job.circuit.num_qubits() > backend.performance.max_qubits {
941            return false;
942        }
943
944        // Check circuit depth
945        if job.circuit.num_gates() > backend.performance.max_depth {
946            return false;
947        }
948
949        // Check target backends (if specified)
950        if let Some(ref targets) = job.target_backends {
951            if !targets.contains(&backend.id) {
952                return false;
953            }
954        }
955
956        // Check queue capacity
957        if backend.queue_info.queue_length >= backend.queue_info.max_queue_size {
958            return false;
959        }
960
961        true
962    }
963
964    /// Get the current execution status for a previously submitted job.
965    ///
966    /// Returns `QuantRS2Error::InvalidInput` if no job with the given ID exists.
967    pub fn get_job_status(&self, job_id: &str) -> QuantRS2Result<ExecutionStatus> {
968        self.job_registry
969            .get(job_id)
970            .map(|record| record.status.clone())
971            .ok_or_else(|| QuantRS2Error::InvalidInput(format!("Unknown job ID: '{job_id}'")))
972    }
973
974    /// Cancel a queued or running job.
975    ///
976    /// If the job is already completed, failed, or cancelled the call succeeds
977    /// but the status is not changed.  Returns `QuantRS2Error::InvalidInput`
978    /// if no job with the given ID exists.
979    pub fn cancel_job(&mut self, job_id: &str) -> QuantRS2Result<()> {
980        let record = self
981            .job_registry
982            .get_mut(job_id)
983            .ok_or_else(|| QuantRS2Error::InvalidInput(format!("Unknown job ID: '{job_id}'")))?;
984
985        match record.status {
986            ExecutionStatus::Queued | ExecutionStatus::Running => {
987                record.status = ExecutionStatus::Cancelled;
988            }
989            // Already in a terminal state — nothing to do
990            ExecutionStatus::Completed
991            | ExecutionStatus::Failed(_)
992            | ExecutionStatus::Cancelled
993            | ExecutionStatus::TimedOut => {}
994        }
995
996        Ok(())
997    }
998
999    /// Retrieve the result record for a job.
1000    ///
1001    /// Returns `QuantRS2Error::InvalidInput` if no job with the given ID exists.
1002    /// For jobs that are still queued or running the returned `status` field
1003    /// will reflect that; callers should check the status before consuming the
1004    /// result fields.
1005    pub fn get_results(&self, job_id: &str) -> QuantRS2Result<DistributedResult> {
1006        let record = self
1007            .job_registry
1008            .get(job_id)
1009            .ok_or_else(|| QuantRS2Error::InvalidInput(format!("Unknown job ID: '{job_id}'")))?;
1010
1011        let elapsed = record.submitted_at.elapsed();
1012
1013        Ok(DistributedResult {
1014            job_id: job_id.to_string(),
1015            status: record.status.clone(),
1016            backend_results: HashMap::new(),
1017            final_result: None,
1018            metadata: ExecutionMetadata {
1019                total_time: elapsed,
1020                queue_time: Duration::from_secs(0),
1021                backends_used: record.backends.clone(),
1022                resource_usage: ResourceUsage {
1023                    cpu_hours: elapsed.as_secs_f64() / 3600.0,
1024                    memory_hours: elapsed.as_secs_f64() / 3600.0,
1025                    qubit_hours: elapsed.as_secs_f64() / 3600.0,
1026                    network_usage: 0.0,
1027                },
1028                cost_info: None,
1029            },
1030        })
1031    }
1032
1033    /// Get system health status
1034    #[must_use]
1035    pub fn get_health_status(&self) -> SystemHealthStatus {
1036        let available_backends = self
1037            .backends
1038            .iter()
1039            .filter(|b| b.status == BackendStatus::Available)
1040            .count();
1041
1042        let total_qubits = self
1043            .backends
1044            .iter()
1045            .filter(|b| b.status == BackendStatus::Available)
1046            .map(|b| b.performance.max_qubits)
1047            .sum();
1048
1049        SystemHealthStatus {
1050            total_backends: self.backends.len(),
1051            available_backends,
1052            total_qubits,
1053            average_queue_time: self
1054                .backends
1055                .iter()
1056                .map(|b| b.queue_info.estimated_wait_time)
1057                .sum::<f64>()
1058                / self.backends.len() as f64,
1059            system_load: self.calculate_system_load(),
1060        }
1061    }
1062
1063    /// Calculate overall system load
1064    fn calculate_system_load(&self) -> f64 {
1065        if self.backends.is_empty() {
1066            return 0.0;
1067        }
1068
1069        let total_capacity: f64 = self
1070            .backends
1071            .iter()
1072            .map(|b| b.queue_info.max_queue_size as f64)
1073            .sum();
1074
1075        let current_load: f64 = self
1076            .backends
1077            .iter()
1078            .map(|b| b.queue_info.queue_length as f64)
1079            .sum();
1080
1081        if total_capacity > 0.0 {
1082            current_load / total_capacity
1083        } else {
1084            0.0
1085        }
1086    }
1087}
1088
1089/// System health status
1090#[derive(Debug, Clone)]
1091pub struct SystemHealthStatus {
1092    /// Total number of backends
1093    pub total_backends: usize,
1094    /// Number of available backends
1095    pub available_backends: usize,
1096    /// Total available qubits
1097    pub total_qubits: usize,
1098    /// Average queue time across all backends
1099    pub average_queue_time: f64,
1100    /// Overall system load (0.0 to 1.0)
1101    pub system_load: f64,
1102}
1103
1104impl Default for DistributedExecutor {
1105    fn default() -> Self {
1106        Self::new()
1107    }
1108}
1109
1110#[cfg(test)]
1111mod tests {
1112    use super::*;
1113
1114    #[test]
1115    fn test_distributed_executor_creation() {
1116        let executor = DistributedExecutor::new();
1117        assert_eq!(executor.backends.len(), 0);
1118        assert_eq!(executor.resource_manager.resource_pool.total_qubits, 0);
1119    }
1120
1121    #[test]
1122    fn test_backend_addition() {
1123        let mut executor = DistributedExecutor::new();
1124
1125        let backend = ExecutionBackend {
1126            id: "test_backend".to_string(),
1127            backend_type: BackendType::Simulator {
1128                simulator_type: SimulatorType::StateVector,
1129                host: "localhost".to_string(),
1130            },
1131            status: BackendStatus::Available,
1132            performance: BackendPerformance {
1133                max_qubits: 10,
1134                max_depth: 1000,
1135                gate_fidelities: HashMap::new(),
1136                coherence_times: HashMap::new(),
1137                execution_time_model: ExecutionTimeModel {
1138                    base_time: 0.1,
1139                    time_per_gate: 0.001,
1140                    time_per_qubit: 0.01,
1141                    time_per_measurement: 0.005,
1142                    network_latency: 0.05,
1143                },
1144                throughput: 10.0,
1145            },
1146            queue_info: QueueInfo {
1147                queue_length: 0,
1148                estimated_wait_time: 0.0,
1149                max_queue_size: 100,
1150                priority_levels: vec![Priority::Normal, Priority::High],
1151            },
1152            capabilities: BackendCapabilities {
1153                supported_gates: vec!["h".to_string(), "cnot".to_string()],
1154                mid_circuit_measurements: false,
1155                classical_control: false,
1156                reset_operations: false,
1157                connectivity: ConnectivityGraph {
1158                    num_qubits: 10,
1159                    edges: vec![(0, 1), (1, 2)],
1160                    topology: TopologyType::Linear,
1161                },
1162                noise_model: None,
1163            },
1164            network_config: NetworkConfig {
1165                endpoint: "http://localhost:8080".to_string(),
1166                credentials: Credentials {
1167                    auth_type: AuthenticationType::None,
1168                    api_key: None,
1169                    token: None,
1170                    username_password: None,
1171                },
1172                timeouts: TimeoutConfig {
1173                    connection_timeout: 5.0,
1174                    request_timeout: 30.0,
1175                    total_timeout: 60.0,
1176                },
1177                retry_policy: RetryPolicy {
1178                    max_retries: 3,
1179                    base_delay: 1.0,
1180                    backoff_strategy: BackoffStrategy::Exponential { multiplier: 2.0 },
1181                    retryable_errors: vec![ErrorType::NetworkError, ErrorType::TimeoutError],
1182                },
1183            },
1184        };
1185
1186        executor
1187            .add_backend(backend)
1188            .expect("Failed to add backend to executor");
1189        assert_eq!(executor.backends.len(), 1);
1190        assert_eq!(executor.resource_manager.resource_pool.total_qubits, 10);
1191    }
1192
1193    #[test]
1194    fn test_job_submission() {
1195        let mut executor = DistributedExecutor::new();
1196
1197        // Add a backend first
1198        let backend = create_test_backend();
1199        executor
1200            .add_backend(backend)
1201            .expect("Failed to add backend to executor");
1202
1203        // Create a test job
1204        let mut circuit = Circuit::<2>::new();
1205        circuit.h(QubitId(0)).expect("Failed to add Hadamard gate"); // Add a gate so it's not empty
1206        let job = DistributedJob {
1207            id: "test_job".to_string(),
1208            circuit,
1209            parameters: ExecutionParameters {
1210                shots: 1000,
1211                optimization_level: 1,
1212                error_mitigation: vec![],
1213                result_format: ResultFormat::Counts,
1214                memory_requirement: None,
1215            },
1216            priority: Priority::Normal,
1217            target_backends: None,
1218            submitted_at: Instant::now(),
1219            deadline: None,
1220        };
1221
1222        let job_id = executor
1223            .submit_job(job)
1224            .expect("Failed to submit job to executor");
1225        assert_eq!(job_id, "test_job");
1226    }
1227
1228    fn create_test_backend() -> ExecutionBackend {
1229        ExecutionBackend {
1230            id: "test_backend".to_string(),
1231            backend_type: BackendType::Simulator {
1232                simulator_type: SimulatorType::StateVector,
1233                host: "localhost".to_string(),
1234            },
1235            status: BackendStatus::Available,
1236            performance: BackendPerformance {
1237                max_qubits: 10,
1238                max_depth: 1000,
1239                gate_fidelities: HashMap::new(),
1240                coherence_times: HashMap::new(),
1241                execution_time_model: ExecutionTimeModel {
1242                    base_time: 0.1,
1243                    time_per_gate: 0.001,
1244                    time_per_qubit: 0.01,
1245                    time_per_measurement: 0.005,
1246                    network_latency: 0.05,
1247                },
1248                throughput: 10.0,
1249            },
1250            queue_info: QueueInfo {
1251                queue_length: 0,
1252                estimated_wait_time: 0.0,
1253                max_queue_size: 100,
1254                priority_levels: vec![Priority::Normal, Priority::High],
1255            },
1256            capabilities: BackendCapabilities {
1257                supported_gates: vec!["h".to_string(), "cnot".to_string()],
1258                mid_circuit_measurements: false,
1259                classical_control: false,
1260                reset_operations: false,
1261                connectivity: ConnectivityGraph {
1262                    num_qubits: 10,
1263                    edges: vec![(0, 1), (1, 2)],
1264                    topology: TopologyType::Linear,
1265                },
1266                noise_model: None,
1267            },
1268            network_config: NetworkConfig {
1269                endpoint: "http://localhost:8080".to_string(),
1270                credentials: Credentials {
1271                    auth_type: AuthenticationType::None,
1272                    api_key: None,
1273                    token: None,
1274                    username_password: None,
1275                },
1276                timeouts: TimeoutConfig {
1277                    connection_timeout: 5.0,
1278                    request_timeout: 30.0,
1279                    total_timeout: 60.0,
1280                },
1281                retry_policy: RetryPolicy {
1282                    max_retries: 3,
1283                    base_delay: 1.0,
1284                    backoff_strategy: BackoffStrategy::Exponential { multiplier: 2.0 },
1285                    retryable_errors: vec![ErrorType::NetworkError, ErrorType::TimeoutError],
1286                },
1287            },
1288        }
1289    }
1290}