Skip to main content

quantrs2_device/cloud/
mod.rs

1//! Quantum Cloud Resource Management System
2//!
3//! This module provides comprehensive cloud resource management for quantum computing across
4//! multiple providers (IBM Quantum, AWS Braket, Azure Quantum, Google Quantum AI) with
5//! intelligent allocation, cost optimization, multi-provider coordination, and advanced
6//! analytics using SciRS2's optimization and machine learning capabilities.
7
8use std::collections::{BTreeMap, HashMap, HashSet, VecDeque};
9use std::sync::{Arc, Mutex, RwLock};
10use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
11
12use quantrs2_circuit::prelude::*;
13use quantrs2_core::{
14    error::{QuantRS2Error, QuantRS2Result},
15    gate::GateOp,
16    qubit::QubitId,
17};
18
19// SciRS2 dependencies for advanced cloud analytics and optimization
20#[cfg(feature = "scirs2")]
21use scirs2_linalg::{det, eig, inv, matrix_norm, prelude::*, svd, LinalgError, LinalgResult};
22#[cfg(feature = "scirs2")]
23use scirs2_optimize::{minimize, OptimizeResult};
24use scirs2_stats::ttest::Alternative;
25#[cfg(feature = "scirs2")]
26use scirs2_stats::{corrcoef, distributions, mean, pearsonr, spearmanr, std, var};
27
28// Fallback implementations when SciRS2 is not available
29#[cfg(not(feature = "scirs2"))]
30mod fallback_scirs2 {
31    use scirs2_core::ndarray::{Array1, Array2, ArrayView1, ArrayView2};
32
33    pub fn mean(_data: &ArrayView1<f64>) -> Result<f64, String> {
34        Ok(0.0)
35    }
36    pub fn std(_data: &ArrayView1<f64>, _ddof: i32) -> Result<f64, String> {
37        Ok(1.0)
38    }
39    pub fn pearsonr(
40        _x: &ArrayView1<f64>,
41        _y: &ArrayView1<f64>,
42        _alt: &str,
43    ) -> Result<(f64, f64), String> {
44        Ok((0.0, 0.5))
45    }
46
47    pub struct OptimizeResult {
48        pub x: Array1<f64>,
49        pub fun: f64,
50        pub success: bool,
51        pub nit: usize,
52        pub nfev: usize,
53        pub message: String,
54    }
55
56    pub fn minimize(
57        _func: fn(&Array1<f64>) -> f64,
58        _x0: &Array1<f64>,
59        _method: &str,
60    ) -> Result<OptimizeResult, String> {
61        Ok(OptimizeResult {
62            x: Array1::zeros(2),
63            fun: 0.0,
64            success: true,
65            nit: 0,
66            nfev: 0,
67            message: "Fallback optimization".to_string(),
68        })
69    }
70
71    pub fn genetic_algorithm(
72        _func: fn(&Array1<f64>) -> f64,
73        _bounds: &[(f64, f64)],
74    ) -> Result<OptimizeResult, String> {
75        Ok(OptimizeResult {
76            x: Array1::zeros(2),
77            fun: 0.0,
78            success: true,
79            nit: 0,
80            nfev: 0,
81            message: "Fallback genetic algorithm".to_string(),
82        })
83    }
84
85    pub fn random_forest(_x: &Array2<f64>, _y: &Array1<f64>) -> Result<String, String> {
86        Ok("fallback_model".to_string())
87    }
88}
89
90#[cfg(not(feature = "scirs2"))]
91use fallback_scirs2::*;
92
93#[cfg(feature = "security")]
94use chrono::{DateTime, Duration as ChronoDuration, Utc};
95use scirs2_core::ndarray::{s, Array1, Array2, Array3, Array4, ArrayView1, ArrayView2, Axis};
96use scirs2_core::random::prelude::*;
97use scirs2_core::Complex64;
98use serde::{Deserialize, Serialize};
99use tokio::sync::{broadcast, mpsc, RwLock as TokioRwLock, Semaphore};
100use uuid::Uuid;
101
102use crate::{
103    backend_traits::{query_backend_capabilities, BackendCapabilities},
104    calibration::{CalibrationManager, DeviceCalibration},
105    integrated_device_manager::{
106        DeviceInfo, IntegratedExecutionResult, IntegratedQuantumDeviceManager,
107    },
108    job_scheduling::{JobConfig, JobPriority, JobStatus, QuantumJob, QuantumJobScheduler},
109    noise_model::CalibrationNoiseModel,
110    topology::HardwareTopology,
111    CircuitExecutor, CircuitResult, DeviceError, DeviceResult, QuantumDevice,
112};
113
114// Module declarations
115pub mod allocation;
116pub mod cost_estimation;
117pub mod cost_management;
118pub mod monitoring;
119pub mod orchestration;
120pub mod provider_migration;
121pub mod provider_optimizations;
122pub mod providers;
123
124// Re-exports for public API
125pub use allocation::*;
126pub use cost_estimation::*;
127pub use cost_management::*;
128pub use monitoring::*;
129pub use orchestration::*;
130pub use provider_migration::*;
131pub use provider_optimizations::*;
132pub use providers::*;
133
134// Re-export specific configuration types
135pub use orchestration::load_balancing::CloudLoadBalancingConfig;
136pub use orchestration::performance::AutoScalingConfig;
137pub use orchestration::performance::CloudPerformanceConfig;
138pub use orchestration::CloudSecurityConfig;
139
140/// Configuration for Quantum Cloud Resource Management System
141#[derive(Debug, Clone, Serialize, Deserialize)]
142pub struct QuantumCloudConfig {
143    /// Multi-provider configuration
144    pub provider_config: MultiProviderConfig,
145    /// Resource allocation and optimization
146    pub allocation_config: ResourceAllocationConfig,
147    /// Cost management and optimization
148    pub cost_config: CostManagementConfig,
149    /// Performance optimization settings
150    pub performance_config: CloudPerformanceConfig,
151    /// Load balancing and failover
152    pub load_balancing_config: CloudLoadBalancingConfig,
153    /// Security and compliance
154    pub security_config: CloudSecurityConfig,
155    /// Monitoring and analytics
156    pub monitoring_config: CloudMonitoringConfig,
157    /// Machine learning and prediction
158    pub ml_config: CloudMLConfig,
159    /// Auto-scaling and elasticity
160    pub scaling_config: AutoScalingConfig,
161    /// Budget and quota management
162    pub budget_config: BudgetConfig,
163}
164
165/// Machine learning configuration for cloud optimization
166#[derive(Debug, Clone, Serialize, Deserialize)]
167pub struct CloudMLConfig {
168    /// Enable ML-driven optimization
169    pub enable_ml_optimization: bool,
170    /// ML models for resource optimization
171    pub optimization_models: Vec<String>,
172    /// Predictive analytics for resource planning
173    pub predictive_analytics: bool,
174    /// Automated decision making threshold
175    pub automated_decision_threshold: f64,
176    /// Model training configuration
177    pub model_training_enabled: bool,
178    /// Feature engineering configuration
179    pub feature_engineering_enabled: bool,
180}
181
182impl Default for CloudMLConfig {
183    fn default() -> Self {
184        Self {
185            enable_ml_optimization: false,
186            optimization_models: vec![],
187            predictive_analytics: false,
188            automated_decision_threshold: 0.8,
189            model_training_enabled: false,
190            feature_engineering_enabled: false,
191        }
192    }
193}
194
195/// Unique identifier for a cloud-submitted quantum job.
196#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
197pub struct CloudJobId(pub String);
198
199impl std::fmt::Display for CloudJobId {
200    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
201        write!(f, "{}", self.0)
202    }
203}
204
205/// A job submitted to the quantum cloud.
206///
207/// Wraps a QASM3 source string so the `QuantumCloudManager` is not generic
208/// over the circuit qubit count (which would make it impossible to store
209/// heterogeneous jobs in the same map).
210#[derive(Debug, Clone, Serialize, Deserialize)]
211pub struct CloudJob {
212    /// Human-readable name / label.
213    pub name: String,
214    /// QASM3 source that encodes the quantum program.
215    pub qasm_source: String,
216    /// Preferred execution provider.  `None` means "choose automatically".
217    pub preferred_provider: Option<CloudProvider>,
218    /// Number of shots requested.
219    pub shots: usize,
220    /// Maximum time-to-result the caller is willing to wait.
221    pub timeout: std::time::Duration,
222    /// Extra metadata (key–value pairs) forwarded to the provider.
223    pub metadata: std::collections::HashMap<String, String>,
224}
225
226/// Status of a cloud job.
227#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
228pub enum CloudJobStatus {
229    /// Accepted by the manager, not yet dispatched to a provider.
230    Queued,
231    /// Sent to the provider and awaiting execution.
232    Submitted { provider: CloudProvider },
233    /// Currently running on quantum hardware.
234    Running { provider: CloudProvider },
235    /// Execution finished; results are available.
236    Completed { provider: CloudProvider },
237    /// Execution failed.
238    Failed { reason: String },
239    /// Cancelled by the caller.
240    Cancelled,
241}
242
243/// Summary of estimated execution costs across providers.
244#[derive(Debug, Clone)]
245pub struct CloudCostEstimate {
246    /// Cheapest provider according to the cost model.
247    pub recommended_provider: CloudProvider,
248    /// Estimated cost in USD.
249    pub estimated_usd: f64,
250    /// Per-provider breakdown (`provider` → estimated USD).
251    pub provider_breakdown: std::collections::HashMap<CloudProvider, f64>,
252    /// Confidence level of the estimate (0.0 – 1.0).
253    pub confidence: f64,
254}
255
256/// Internal job record maintained by `QuantumCloudManager`.
257#[derive(Debug, Clone)]
258struct JobRecord {
259    job: CloudJob,
260    status: CloudJobStatus,
261    submitted_at: std::time::SystemTime,
262    completed_at: Option<std::time::SystemTime>,
263}
264
265/// Main cloud resource manager.
266///
267/// Provides a provider-agnostic entry point for submitting quantum jobs,
268/// querying their status, listing available providers, and estimating costs.
269///
270/// # Thread safety
271///
272/// Internally uses `std::sync::RwLock` so the manager can be shared across
273/// threads without `async` overhead at the call site.
274pub struct QuantumCloudManager {
275    config: QuantumCloudConfig,
276    /// Active and historical job records keyed by `CloudJobId`.
277    jobs: std::sync::RwLock<std::collections::HashMap<CloudJobId, JobRecord>>,
278}
279
280impl QuantumCloudManager {
281    /// Create a new cloud manager with the given configuration.
282    pub fn new(config: QuantumCloudConfig) -> DeviceResult<Self> {
283        Ok(Self {
284            config,
285            jobs: std::sync::RwLock::new(std::collections::HashMap::new()),
286        })
287    }
288
289    /// Submit a quantum job and return its assigned identifier.
290    ///
291    /// The manager validates the job, selects the best provider according to
292    /// the configured selection strategy, and records the submission.
293    pub fn submit_job(&self, job: CloudJob) -> DeviceResult<CloudJobId> {
294        if job.qasm_source.trim().is_empty() {
295            return Err(DeviceError::InvalidInput(
296                "CloudJob.qasm_source must not be empty".to_string(),
297            ));
298        }
299        let provider = self.select_provider(&job)?;
300        let id = CloudJobId(Uuid::new_v4().to_string());
301        let record = JobRecord {
302            status: CloudJobStatus::Submitted {
303                provider: provider.clone(),
304            },
305            job,
306            submitted_at: std::time::SystemTime::now(),
307            completed_at: None,
308        };
309        self.jobs
310            .write()
311            .map_err(|e| DeviceError::LockError(format!("jobs write lock: {e}")))?
312            .insert(id.clone(), record);
313        Ok(id)
314    }
315
316    /// Query the current status of a previously submitted job.
317    pub fn get_job_status(&self, id: &CloudJobId) -> DeviceResult<CloudJobStatus> {
318        let jobs = self
319            .jobs
320            .read()
321            .map_err(|e| DeviceError::LockError(format!("jobs read lock: {e}")))?;
322        jobs.get(id)
323            .map(|r| r.status.clone())
324            .ok_or_else(|| DeviceError::DeviceNotFound(format!("job '{}' not found", id)))
325    }
326
327    /// Cancel a queued or submitted job.
328    pub fn cancel_job(&self, id: &CloudJobId) -> DeviceResult<()> {
329        let mut jobs = self
330            .jobs
331            .write()
332            .map_err(|e| DeviceError::LockError(format!("jobs write lock: {e}")))?;
333        let record = jobs
334            .get_mut(id)
335            .ok_or_else(|| DeviceError::DeviceNotFound(format!("job '{}' not found", id)))?;
336        match &record.status {
337            CloudJobStatus::Queued | CloudJobStatus::Submitted { .. } => {
338                record.status = CloudJobStatus::Cancelled;
339                record.completed_at = Some(std::time::SystemTime::now());
340                Ok(())
341            }
342            other => Err(DeviceError::InvalidInput(format!(
343                "cannot cancel job '{}' in state {:?}",
344                id, other
345            ))),
346        }
347    }
348
349    /// List all configured and enabled cloud providers.
350    pub fn list_providers(&self) -> Vec<CloudProvider> {
351        self.config.provider_config.enabled_providers.clone()
352    }
353
354    /// Estimate the cost of executing `job` across all enabled providers.
355    ///
356    /// The estimate is based on shot count and a simple per-shot pricing
357    /// model.  Real providers typically expose more nuanced pricing APIs;
358    /// this implementation serves as a deterministic baseline.
359    pub fn estimate_cost(&self, job: &CloudJob) -> DeviceResult<CloudCostEstimate> {
360        let providers = self.list_providers();
361        if providers.is_empty() {
362            return Err(DeviceError::UnsupportedOperation(
363                "no cloud providers configured".to_string(),
364            ));
365        }
366
367        // Simple per-shot cost model (USD per shot).
368        // Values are illustrative defaults; a production manager would
369        // read these from the per-provider configuration.
370        let cost_per_shot = |p: &CloudProvider| -> f64 {
371            match p {
372                CloudProvider::IBM => 0.000_05,
373                CloudProvider::AWS => 0.000_075,
374                CloudProvider::Azure => 0.000_065,
375                CloudProvider::Google => 0.000_08,
376                CloudProvider::IonQ => 0.000_01,
377                CloudProvider::Rigetti => 0.000_04,
378                CloudProvider::Xanadu => 0.000_03,
379                CloudProvider::DWave => 0.000_02,
380                CloudProvider::Custom(_) => 0.000_05,
381            }
382        };
383
384        let shots = job.shots.max(1) as f64;
385        let mut breakdown = std::collections::HashMap::new();
386        let mut cheapest_provider = providers[0].clone();
387        let mut cheapest_cost = f64::MAX;
388
389        for provider in &providers {
390            let cost = cost_per_shot(provider) * shots;
391            if cost < cheapest_cost {
392                cheapest_cost = cost;
393                cheapest_provider = provider.clone();
394            }
395            breakdown.insert(provider.clone(), cost);
396        }
397
398        Ok(CloudCostEstimate {
399            recommended_provider: cheapest_provider,
400            estimated_usd: cheapest_cost,
401            provider_breakdown: breakdown,
402            confidence: 0.8,
403        })
404    }
405
406    // ------------------------------------------------------------------
407    // Private helpers
408    // ------------------------------------------------------------------
409
410    fn select_provider(&self, job: &CloudJob) -> DeviceResult<CloudProvider> {
411        // If the caller expressed a preference and the provider is enabled, honour it.
412        if let Some(preferred) = &job.preferred_provider {
413            if self
414                .config
415                .provider_config
416                .enabled_providers
417                .contains(preferred)
418            {
419                return Ok(preferred.clone());
420            }
421        }
422        // Otherwise pick the cheapest enabled provider.
423        let estimate = self.estimate_cost(job)?;
424        Ok(estimate.recommended_provider)
425    }
426}