use crate::builder::Circuit;
use quantrs2_core::{
error::{QuantRS2Error, QuantRS2Result},
qubit::QubitId,
};
use scirs2_core::Complex64;
use std::collections::HashMap;
use std::time::{Duration, Instant};
#[derive(Debug, Clone)]
struct JobRecord {
status: ExecutionStatus,
backends: Vec<String>,
submitted_at: std::time::Instant,
}
#[derive(Debug)]
pub struct DistributedExecutor {
pub backends: Vec<ExecutionBackend>,
pub load_balancer: LoadBalancer,
pub fault_tolerance: FaultToleranceConfig,
pub scheduler: ExecutionScheduler,
pub resource_manager: ResourceManager,
job_registry: HashMap<String, JobRecord>,
}
#[derive(Debug, Clone)]
pub struct ExecutionBackend {
pub id: String,
pub backend_type: BackendType,
pub status: BackendStatus,
pub performance: BackendPerformance,
pub queue_info: QueueInfo,
pub capabilities: BackendCapabilities,
pub network_config: NetworkConfig,
}
#[derive(Debug, Clone, PartialEq)]
pub enum BackendType {
Hardware {
vendor: String,
model: String,
location: String,
},
Simulator {
simulator_type: SimulatorType,
host: String,
},
CloudService {
provider: String,
service_name: String,
region: String,
},
Hybrid {
quantum_backend: Box<Self>,
classical_resources: ClassicalResources,
},
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SimulatorType {
StateVector,
DensityMatrix,
TensorNetwork,
StabilunerformCode,
MatrixProductState,
Custom(String),
}
#[derive(Debug, Clone, PartialEq)]
pub struct ClassicalResources {
pub cpu_cores: usize,
pub memory_gb: f64,
pub gpus: Vec<GPUInfo>,
pub storage_gb: f64,
}
#[derive(Debug, Clone, PartialEq)]
pub struct GPUInfo {
pub model: String,
pub memory_gb: f64,
pub compute_capability: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BackendStatus {
Available,
Busy,
Unavailable,
Maintenance,
Offline,
Error(String),
}
#[derive(Debug, Clone)]
pub struct BackendPerformance {
pub max_qubits: usize,
pub max_depth: usize,
pub gate_fidelities: HashMap<String, f64>,
pub coherence_times: HashMap<String, f64>,
pub execution_time_model: ExecutionTimeModel,
pub throughput: f64,
}
#[derive(Debug, Clone)]
pub struct ExecutionTimeModel {
pub base_time: f64,
pub time_per_gate: f64,
pub time_per_qubit: f64,
pub time_per_measurement: f64,
pub network_latency: f64,
}
#[derive(Debug, Clone)]
pub struct QueueInfo {
pub queue_length: usize,
pub estimated_wait_time: f64,
pub max_queue_size: usize,
pub priority_levels: Vec<Priority>,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Priority {
Low,
Normal,
High,
Critical,
}
#[derive(Debug, Clone)]
pub struct BackendCapabilities {
pub supported_gates: Vec<String>,
pub mid_circuit_measurements: bool,
pub classical_control: bool,
pub reset_operations: bool,
pub connectivity: ConnectivityGraph,
pub noise_model: Option<NoiseCharacteristics>,
}
#[derive(Debug, Clone)]
pub struct ConnectivityGraph {
pub num_qubits: usize,
pub edges: Vec<(usize, usize)>,
pub topology: TopologyType,
}
#[derive(Debug, Clone, PartialEq)]
pub enum TopologyType {
Linear,
Grid2D { rows: usize, cols: usize },
AllToAll,
Random { density: f64 },
Custom,
}
#[derive(Debug, Clone)]
pub struct NoiseCharacteristics {
pub single_qubit_errors: HashMap<String, f64>,
pub two_qubit_errors: HashMap<String, f64>,
pub measurement_errors: Vec<f64>,
pub decoherence_times: Vec<f64>,
}
#[derive(Debug, Clone)]
pub struct NetworkConfig {
pub endpoint: String,
pub credentials: Credentials,
pub timeouts: TimeoutConfig,
pub retry_policy: RetryPolicy,
}
#[derive(Debug, Clone)]
pub struct Credentials {
pub auth_type: AuthenticationType,
pub api_key: Option<String>,
pub token: Option<String>,
pub username_password: Option<(String, String)>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AuthenticationType {
ApiKey,
Token,
UsernamePassword,
Certificate,
None,
}
#[derive(Debug, Clone)]
pub struct TimeoutConfig {
pub connection_timeout: f64,
pub request_timeout: f64,
pub total_timeout: f64,
}
#[derive(Debug, Clone)]
pub struct RetryPolicy {
pub max_retries: usize,
pub base_delay: f64,
pub backoff_strategy: BackoffStrategy,
pub retryable_errors: Vec<ErrorType>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum BackoffStrategy {
Fixed,
Linear,
Exponential { multiplier: f64 },
Jitter { max_jitter: f64 },
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ErrorType {
NetworkError,
TimeoutError,
ServiceUnavailable,
RateLimited,
InternalServerError,
}
#[derive(Debug, Clone)]
pub struct LoadBalancer {
pub strategy: LoadBalancingStrategy,
pub health_check: HealthCheckConfig,
pub metrics: MetricsConfig,
}
#[derive(Debug, Clone, PartialEq)]
pub enum LoadBalancingStrategy {
RoundRobin,
LeastConnections,
LeastQueueTime,
BestPerformance,
WeightedRoundRobin(HashMap<String, f64>),
Custom(String),
}
#[derive(Debug, Clone)]
pub struct HealthCheckConfig {
pub interval: f64,
pub timeout: f64,
pub failure_threshold: usize,
pub success_threshold: usize,
}
#[derive(Debug, Clone)]
pub struct MetricsConfig {
pub enabled: bool,
pub collection_interval: f64,
pub retention_period: f64,
pub storage_backend: MetricsStorage,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MetricsStorage {
InMemory,
File(String),
Database(String),
CloudStorage(String),
}
#[derive(Debug, Clone)]
pub struct FaultToleranceConfig {
pub enable_failover: bool,
pub redundancy_level: usize,
pub error_correction: ErrorCorrectionStrategy,
pub failure_detection: FailureDetectionConfig,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ErrorCorrectionStrategy {
None,
MajorityVoting,
QuantumErrorCorrection,
ClassicalPostProcessing,
Custom(String),
}
#[derive(Debug, Clone)]
pub struct FailureDetectionConfig {
pub detection_methods: Vec<FailureDetectionMethod>,
pub detection_threshold: f64,
pub detection_window: f64,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum FailureDetectionMethod {
ErrorRateMonitoring,
LatencyMonitoring,
ResultValidation,
HealthCheckFailures,
}
#[derive(Debug, Clone)]
pub struct ExecutionScheduler {
pub policy: SchedulingPolicy,
pub priority_queue: PriorityQueueConfig,
pub resource_allocation: ResourceAllocationStrategy,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SchedulingPolicy {
FCFS,
SJF,
Priority,
FairShare,
DeadlineAware,
Custom(String),
}
#[derive(Debug, Clone)]
pub struct PriorityQueueConfig {
pub max_size_per_priority: HashMap<Priority, usize>,
pub aging_factor: f64,
pub priority_boost_interval: f64,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ResourceAllocationStrategy {
BestFit,
FirstFit,
WorstFit,
NextFit,
Custom(String),
}
#[derive(Debug, Clone)]
pub struct ResourceManager {
pub resource_pool: ResourcePool,
pub allocation_policies: AllocationPolicies,
pub usage_tracking: UsageTracking,
}
#[derive(Debug, Clone)]
pub struct ResourcePool {
pub total_qubits: usize,
pub available_slots: usize,
pub memory_pool: f64,
pub compute_pool: f64,
}
#[derive(Debug, Clone)]
pub struct AllocationPolicies {
pub max_qubits_per_user: Option<usize>,
pub max_execution_time: Option<f64>,
pub fair_share: bool,
pub reserved_resources: f64,
}
#[derive(Debug, Clone)]
pub struct UsageTracking {
pub per_user_tracking: bool,
pub per_project_tracking: bool,
pub reporting_interval: f64,
pub retention_period: f64,
}
#[derive(Debug, Clone)]
pub struct DistributedJob<const N: usize> {
pub id: String,
pub circuit: Circuit<N>,
pub parameters: ExecutionParameters,
pub priority: Priority,
pub target_backends: Option<Vec<String>>,
pub submitted_at: Instant,
pub deadline: Option<Instant>,
}
#[derive(Debug, Clone)]
pub struct ExecutionParameters {
pub shots: usize,
pub optimization_level: usize,
pub error_mitigation: Vec<ErrorMitigation>,
pub result_format: ResultFormat,
pub memory_requirement: Option<f64>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ErrorMitigation {
ReadoutErrorMitigation,
ZeroNoiseExtrapolation,
CliffordDataRegression,
SymmetryVerification,
Custom(String),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ResultFormat {
Counts,
Probabilities,
Statevector,
ExpectationValues,
Custom(String),
}
#[derive(Debug, Clone)]
pub struct DistributedResult {
pub job_id: String,
pub status: ExecutionStatus,
pub backend_results: HashMap<String, BackendResult>,
pub final_result: Option<AggregatedResult>,
pub metadata: ExecutionMetadata,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ExecutionStatus {
Queued,
Running,
Completed,
Failed(String),
Cancelled,
TimedOut,
}
#[derive(Debug, Clone)]
pub struct BackendResult {
pub backend_id: String,
pub status: ExecutionStatus,
pub measurements: Option<Vec<Vec<bool>>>,
pub probabilities: Option<HashMap<String, f64>>,
pub execution_time: Duration,
pub error: Option<String>,
}
#[derive(Debug, Clone)]
pub struct AggregatedResult {
pub combined_measurements: HashMap<String, f64>,
pub error_estimates: HashMap<String, f64>,
pub confidence_intervals: HashMap<String, (f64, f64)>,
pub quality_metrics: QualityMetrics,
}
#[derive(Debug, Clone)]
pub struct QualityMetrics {
pub statistical_significance: f64,
pub consistency_score: f64,
pub estimated_fidelity: f64,
pub mitigation_effectiveness: f64,
}
#[derive(Debug, Clone)]
pub struct ExecutionMetadata {
pub total_time: Duration,
pub queue_time: Duration,
pub backends_used: Vec<String>,
pub resource_usage: ResourceUsage,
pub cost_info: Option<CostInfo>,
}
#[derive(Debug, Clone)]
pub struct ResourceUsage {
pub cpu_hours: f64,
pub memory_hours: f64,
pub qubit_hours: f64,
pub network_usage: f64,
}
#[derive(Debug, Clone)]
pub struct CostInfo {
pub total_cost: f64,
pub cost_breakdown: HashMap<String, f64>,
pub currency: String,
}
impl DistributedExecutor {
#[must_use]
pub fn new() -> Self {
Self {
backends: Vec::new(),
load_balancer: LoadBalancer {
strategy: LoadBalancingStrategy::RoundRobin,
health_check: HealthCheckConfig {
interval: 30.0,
timeout: 5.0,
failure_threshold: 3,
success_threshold: 2,
},
metrics: MetricsConfig {
enabled: true,
collection_interval: 60.0,
retention_period: 3600.0 * 24.0, storage_backend: MetricsStorage::InMemory,
},
},
fault_tolerance: FaultToleranceConfig {
enable_failover: true,
redundancy_level: 1,
error_correction: ErrorCorrectionStrategy::MajorityVoting,
failure_detection: FailureDetectionConfig {
detection_methods: vec![
FailureDetectionMethod::ErrorRateMonitoring,
FailureDetectionMethod::LatencyMonitoring,
],
detection_threshold: 0.1,
detection_window: 300.0,
},
},
scheduler: ExecutionScheduler {
policy: SchedulingPolicy::Priority,
priority_queue: PriorityQueueConfig {
max_size_per_priority: {
let mut map = HashMap::new();
map.insert(Priority::Critical, 10);
map.insert(Priority::High, 50);
map.insert(Priority::Normal, 200);
map.insert(Priority::Low, 1000);
map
},
aging_factor: 0.1,
priority_boost_interval: 3600.0, },
resource_allocation: ResourceAllocationStrategy::BestFit,
},
resource_manager: ResourceManager {
resource_pool: ResourcePool {
total_qubits: 0,
available_slots: 0,
memory_pool: 0.0,
compute_pool: 0.0,
},
allocation_policies: AllocationPolicies {
max_qubits_per_user: Some(100),
max_execution_time: Some(3600.0), fair_share: true,
reserved_resources: 0.1, },
usage_tracking: UsageTracking {
per_user_tracking: true,
per_project_tracking: true,
reporting_interval: 3600.0, retention_period: 3600.0 * 24.0 * 30.0, },
},
job_registry: HashMap::new(),
}
}
pub fn add_backend(&mut self, backend: ExecutionBackend) -> QuantRS2Result<()> {
if backend.id.is_empty() {
return Err(QuantRS2Error::InvalidInput(
"Backend ID cannot be empty".to_string(),
));
}
if self.backends.iter().any(|b| b.id == backend.id) {
return Err(QuantRS2Error::InvalidInput(format!(
"Backend with ID '{}' already exists",
backend.id
)));
}
self.resource_manager.resource_pool.total_qubits += backend.performance.max_qubits;
self.resource_manager.resource_pool.available_slots += 1;
self.backends.push(backend);
Ok(())
}
pub fn submit_job<const N: usize>(&mut self, job: DistributedJob<N>) -> QuantRS2Result<String> {
if job.circuit.num_gates() == 0 {
return Err(QuantRS2Error::InvalidInput(
"Cannot submit empty circuit".to_string(),
));
}
let required_qubits = job.circuit.num_qubits();
if required_qubits > self.resource_manager.resource_pool.total_qubits {
return Err(QuantRS2Error::UnsupportedQubits(
required_qubits,
format!(
"Maximum available qubits: {}",
self.resource_manager.resource_pool.total_qubits
),
));
}
let selected_backends = self.select_backends(&job)?;
if selected_backends.is_empty() {
return Err(QuantRS2Error::BackendExecutionFailed(
"No suitable backends available".to_string(),
));
}
let job_id = job.id.clone();
let submitted_at = job.submitted_at;
self.job_registry.insert(
job_id.clone(),
JobRecord {
status: ExecutionStatus::Queued,
backends: selected_backends,
submitted_at,
},
);
Ok(job_id)
}
fn select_backends<const N: usize>(
&self,
job: &DistributedJob<N>,
) -> QuantRS2Result<Vec<String>> {
let mut suitable_backends = Vec::new();
for backend in &self.backends {
if self.is_backend_suitable(backend, job) {
suitable_backends.push(backend.id.clone());
}
}
match self.load_balancer.strategy {
LoadBalancingStrategy::RoundRobin => {
suitable_backends.truncate(self.fault_tolerance.redundancy_level.max(1));
}
LoadBalancingStrategy::LeastQueueTime => {
suitable_backends.sort_by(|a, b| {
let wait_a = self
.backends
.iter()
.find(|backend| backend.id == *a)
.map(|backend| backend.queue_info.estimated_wait_time)
.unwrap_or(0.0);
let wait_b = self
.backends
.iter()
.find(|backend| backend.id == *b)
.map(|backend| backend.queue_info.estimated_wait_time)
.unwrap_or(0.0);
wait_a
.partial_cmp(&wait_b)
.unwrap_or(std::cmp::Ordering::Equal)
});
suitable_backends.truncate(self.fault_tolerance.redundancy_level.max(1));
}
_ => {
suitable_backends.truncate(1);
}
}
Ok(suitable_backends)
}
fn is_backend_suitable<const N: usize>(
&self,
backend: &ExecutionBackend,
job: &DistributedJob<N>,
) -> bool {
if backend.status != BackendStatus::Available {
return false;
}
if job.circuit.num_qubits() > backend.performance.max_qubits {
return false;
}
if job.circuit.num_gates() > backend.performance.max_depth {
return false;
}
if let Some(ref targets) = job.target_backends {
if !targets.contains(&backend.id) {
return false;
}
}
if backend.queue_info.queue_length >= backend.queue_info.max_queue_size {
return false;
}
true
}
pub fn get_job_status(&self, job_id: &str) -> QuantRS2Result<ExecutionStatus> {
self.job_registry
.get(job_id)
.map(|record| record.status.clone())
.ok_or_else(|| QuantRS2Error::InvalidInput(format!("Unknown job ID: '{job_id}'")))
}
pub fn cancel_job(&mut self, job_id: &str) -> QuantRS2Result<()> {
let record = self
.job_registry
.get_mut(job_id)
.ok_or_else(|| QuantRS2Error::InvalidInput(format!("Unknown job ID: '{job_id}'")))?;
match record.status {
ExecutionStatus::Queued | ExecutionStatus::Running => {
record.status = ExecutionStatus::Cancelled;
}
ExecutionStatus::Completed
| ExecutionStatus::Failed(_)
| ExecutionStatus::Cancelled
| ExecutionStatus::TimedOut => {}
}
Ok(())
}
pub fn get_results(&self, job_id: &str) -> QuantRS2Result<DistributedResult> {
let record = self
.job_registry
.get(job_id)
.ok_or_else(|| QuantRS2Error::InvalidInput(format!("Unknown job ID: '{job_id}'")))?;
let elapsed = record.submitted_at.elapsed();
Ok(DistributedResult {
job_id: job_id.to_string(),
status: record.status.clone(),
backend_results: HashMap::new(),
final_result: None,
metadata: ExecutionMetadata {
total_time: elapsed,
queue_time: Duration::from_secs(0),
backends_used: record.backends.clone(),
resource_usage: ResourceUsage {
cpu_hours: elapsed.as_secs_f64() / 3600.0,
memory_hours: elapsed.as_secs_f64() / 3600.0,
qubit_hours: elapsed.as_secs_f64() / 3600.0,
network_usage: 0.0,
},
cost_info: None,
},
})
}
#[must_use]
pub fn get_health_status(&self) -> SystemHealthStatus {
let available_backends = self
.backends
.iter()
.filter(|b| b.status == BackendStatus::Available)
.count();
let total_qubits = self
.backends
.iter()
.filter(|b| b.status == BackendStatus::Available)
.map(|b| b.performance.max_qubits)
.sum();
SystemHealthStatus {
total_backends: self.backends.len(),
available_backends,
total_qubits,
average_queue_time: self
.backends
.iter()
.map(|b| b.queue_info.estimated_wait_time)
.sum::<f64>()
/ self.backends.len() as f64,
system_load: self.calculate_system_load(),
}
}
fn calculate_system_load(&self) -> f64 {
if self.backends.is_empty() {
return 0.0;
}
let total_capacity: f64 = self
.backends
.iter()
.map(|b| b.queue_info.max_queue_size as f64)
.sum();
let current_load: f64 = self
.backends
.iter()
.map(|b| b.queue_info.queue_length as f64)
.sum();
if total_capacity > 0.0 {
current_load / total_capacity
} else {
0.0
}
}
}
#[derive(Debug, Clone)]
pub struct SystemHealthStatus {
pub total_backends: usize,
pub available_backends: usize,
pub total_qubits: usize,
pub average_queue_time: f64,
pub system_load: f64,
}
impl Default for DistributedExecutor {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_distributed_executor_creation() {
let executor = DistributedExecutor::new();
assert_eq!(executor.backends.len(), 0);
assert_eq!(executor.resource_manager.resource_pool.total_qubits, 0);
}
#[test]
fn test_backend_addition() {
let mut executor = DistributedExecutor::new();
let backend = ExecutionBackend {
id: "test_backend".to_string(),
backend_type: BackendType::Simulator {
simulator_type: SimulatorType::StateVector,
host: "localhost".to_string(),
},
status: BackendStatus::Available,
performance: BackendPerformance {
max_qubits: 10,
max_depth: 1000,
gate_fidelities: HashMap::new(),
coherence_times: HashMap::new(),
execution_time_model: ExecutionTimeModel {
base_time: 0.1,
time_per_gate: 0.001,
time_per_qubit: 0.01,
time_per_measurement: 0.005,
network_latency: 0.05,
},
throughput: 10.0,
},
queue_info: QueueInfo {
queue_length: 0,
estimated_wait_time: 0.0,
max_queue_size: 100,
priority_levels: vec![Priority::Normal, Priority::High],
},
capabilities: BackendCapabilities {
supported_gates: vec!["h".to_string(), "cnot".to_string()],
mid_circuit_measurements: false,
classical_control: false,
reset_operations: false,
connectivity: ConnectivityGraph {
num_qubits: 10,
edges: vec![(0, 1), (1, 2)],
topology: TopologyType::Linear,
},
noise_model: None,
},
network_config: NetworkConfig {
endpoint: "http://localhost:8080".to_string(),
credentials: Credentials {
auth_type: AuthenticationType::None,
api_key: None,
token: None,
username_password: None,
},
timeouts: TimeoutConfig {
connection_timeout: 5.0,
request_timeout: 30.0,
total_timeout: 60.0,
},
retry_policy: RetryPolicy {
max_retries: 3,
base_delay: 1.0,
backoff_strategy: BackoffStrategy::Exponential { multiplier: 2.0 },
retryable_errors: vec![ErrorType::NetworkError, ErrorType::TimeoutError],
},
},
};
executor
.add_backend(backend)
.expect("Failed to add backend to executor");
assert_eq!(executor.backends.len(), 1);
assert_eq!(executor.resource_manager.resource_pool.total_qubits, 10);
}
#[test]
fn test_job_submission() {
let mut executor = DistributedExecutor::new();
let backend = create_test_backend();
executor
.add_backend(backend)
.expect("Failed to add backend to executor");
let mut circuit = Circuit::<2>::new();
circuit.h(QubitId(0)).expect("Failed to add Hadamard gate"); let job = DistributedJob {
id: "test_job".to_string(),
circuit,
parameters: ExecutionParameters {
shots: 1000,
optimization_level: 1,
error_mitigation: vec![],
result_format: ResultFormat::Counts,
memory_requirement: None,
},
priority: Priority::Normal,
target_backends: None,
submitted_at: Instant::now(),
deadline: None,
};
let job_id = executor
.submit_job(job)
.expect("Failed to submit job to executor");
assert_eq!(job_id, "test_job");
}
fn create_test_backend() -> ExecutionBackend {
ExecutionBackend {
id: "test_backend".to_string(),
backend_type: BackendType::Simulator {
simulator_type: SimulatorType::StateVector,
host: "localhost".to_string(),
},
status: BackendStatus::Available,
performance: BackendPerformance {
max_qubits: 10,
max_depth: 1000,
gate_fidelities: HashMap::new(),
coherence_times: HashMap::new(),
execution_time_model: ExecutionTimeModel {
base_time: 0.1,
time_per_gate: 0.001,
time_per_qubit: 0.01,
time_per_measurement: 0.005,
network_latency: 0.05,
},
throughput: 10.0,
},
queue_info: QueueInfo {
queue_length: 0,
estimated_wait_time: 0.0,
max_queue_size: 100,
priority_levels: vec![Priority::Normal, Priority::High],
},
capabilities: BackendCapabilities {
supported_gates: vec!["h".to_string(), "cnot".to_string()],
mid_circuit_measurements: false,
classical_control: false,
reset_operations: false,
connectivity: ConnectivityGraph {
num_qubits: 10,
edges: vec![(0, 1), (1, 2)],
topology: TopologyType::Linear,
},
noise_model: None,
},
network_config: NetworkConfig {
endpoint: "http://localhost:8080".to_string(),
credentials: Credentials {
auth_type: AuthenticationType::None,
api_key: None,
token: None,
username_password: None,
},
timeouts: TimeoutConfig {
connection_timeout: 5.0,
request_timeout: 30.0,
total_timeout: 60.0,
},
retry_policy: RetryPolicy {
max_retries: 3,
base_delay: 1.0,
backoff_strategy: BackoffStrategy::Exponential { multiplier: 2.0 },
retryable_errors: vec![ErrorType::NetworkError, ErrorType::TimeoutError],
},
},
}
}
}