quantrs2_anneal/
dwave.rs

1//! D-Wave Leap cloud service client
2//!
3//! This module provides a comprehensive interface for D-Wave Leap cloud services,
4//! including quantum annealing hardware, hybrid solvers, and advanced features.
5//! It requires the "dwave" feature to be enabled.
6//!
7//! # Features
8//!
9//! - Full D-Wave Leap API integration
10//! - Quantum annealing hardware access
11//! - Hybrid classical-quantum solvers
12//! - Advanced embedding integration
13//! - Problem status tracking and management
14//! - Performance monitoring and metrics
15//! - Custom annealing schedules
16//! - Batch problem submission
17//! - Robust error handling and retry logic
18
19#[cfg(feature = "dwave")]
20mod client {
21    use reqwest::Client;
22    use serde::{Deserialize, Serialize};
23    use std::collections::HashMap;
24    use std::fmt::Write;
25    use std::time::{Duration, Instant};
26    use thiserror::Error;
27    use tokio::runtime::Runtime;
28
29    use crate::embedding::{Embedding, HardwareGraph, MinorMiner};
30    use crate::ising::{IsingError, IsingModel, QuboModel};
31
32    /// Errors that can occur when interacting with D-Wave API
33    #[derive(Error, Debug)]
34    pub enum DWaveError {
35        /// Error in the underlying Ising model
36        #[error("Ising error: {0}")]
37        IsingError(#[from] IsingError),
38
39        /// Error with the network request
40        #[error("Network error: {0}")]
41        NetworkError(#[from] reqwest::Error),
42
43        /// Error parsing the response
44        #[error("Response parsing error: {0}")]
45        ParseError(#[from] serde_json::Error),
46
47        /// Error with the D-Wave API response
48        #[error("D-Wave API error: {0}")]
49        ApiError(String),
50
51        /// Error with the authentication credentials
52        #[error("Authentication error: {0}")]
53        AuthError(String),
54
55        /// Error with the tokio runtime
56        #[error("Runtime error: {0}")]
57        RuntimeError(String),
58
59        /// Error with the problem formulation
60        #[error("Problem formulation error: {0}")]
61        ProblemError(String),
62
63        /// Error with embedding
64        #[error("Embedding error: {0}")]
65        EmbeddingError(String),
66
67        /// Error with hybrid solver
68        #[error("Hybrid solver error: {0}")]
69        HybridSolverError(String),
70
71        /// Error with problem status
72        #[error("Problem status error: {0}")]
73        StatusError(String),
74
75        /// Error with batch operations
76        #[error("Batch operation error: {0}")]
77        BatchError(String),
78
79        /// Error with solver configuration
80        #[error("Solver configuration error: {0}")]
81        SolverConfigError(String),
82
83        /// Timeout error
84        #[error("Operation timed out: {0}")]
85        TimeoutError(String),
86    }
87
88    /// Result type for D-Wave operations
89    pub type DWaveResult<T> = Result<T, DWaveError>;
90
91    /// D-Wave solver information
92    #[derive(Debug, Clone, Serialize, Deserialize)]
93    pub struct SolverInfo {
94        /// ID of the solver
95        pub id: String,
96
97        /// Name of the solver
98        pub name: String,
99
100        /// Description of the solver
101        pub description: String,
102
103        /// Number of qubits
104        pub num_qubits: usize,
105
106        /// Connectivity information
107        pub connectivity: SolverConnectivity,
108
109        /// Properties of the solver
110        pub properties: SolverProperties,
111    }
112
113    /// D-Wave solver connectivity
114    #[derive(Debug, Clone, Serialize, Deserialize)]
115    pub struct SolverConnectivity {
116        /// Type of connectivity (e.g., "chimera", "pegasus")
117        #[serde(rename = "type")]
118        pub type_: String,
119
120        /// Parameters for the connectivity
121        #[serde(flatten)]
122        pub params: serde_json::Value,
123    }
124
125    /// D-Wave solver properties
126    #[derive(Debug, Clone, Serialize, Deserialize)]
127    pub struct SolverProperties {
128        /// Supported parameters
129        pub parameters: serde_json::Value,
130
131        /// Additional properties
132        #[serde(flatten)]
133        pub other: serde_json::Value,
134    }
135
136    /// D-Wave problem submission parameters
137    #[derive(Debug, Clone, Serialize, Deserialize)]
138    pub struct ProblemParams {
139        /// Number of reads/samples to take
140        pub num_reads: usize,
141
142        /// Annealing time in microseconds
143        pub annealing_time: usize,
144
145        /// Programming thermalization in microseconds
146        #[serde(rename = "programming_thermalization")]
147        pub programming_therm: usize,
148
149        /// Read-out thermalization in microseconds
150        #[serde(rename = "readout_thermalization")]
151        pub readout_therm: usize,
152
153        /// Flux biases for each qubit (optional)
154        #[serde(rename = "flux_biases", skip_serializing_if = "Option::is_none")]
155        pub flux_biases: Option<Vec<f64>>,
156
157        /// Per-qubit flux bias values (optional, alternative format)
158        #[serde(rename = "flux_bias", skip_serializing_if = "Option::is_none")]
159        pub flux_bias_map: Option<serde_json::Map<String, serde_json::Value>>,
160
161        /// Additional parameters
162        #[serde(flatten)]
163        pub other: serde_json::Value,
164    }
165
166    impl Default for ProblemParams {
167        fn default() -> Self {
168            Self {
169                num_reads: 1000,
170                annealing_time: 20,
171                programming_therm: 1000,
172                readout_therm: 0,
173                flux_biases: None,
174                flux_bias_map: None,
175                other: serde_json::Value::Object(serde_json::Map::new()),
176            }
177        }
178    }
179
180    /// D-Wave problem submission
181    #[derive(Debug, Clone, Serialize, Deserialize)]
182    pub struct Problem {
183        /// The linear terms (h_i values for Ising, Q_ii for QUBO)
184        #[serde(rename = "linear")]
185        pub linear_terms: serde_json::Value,
186
187        /// The quadratic terms (J_ij values for Ising, Q_ij for QUBO)
188        #[serde(rename = "quadratic")]
189        pub quadratic_terms: serde_json::Value,
190
191        /// The type of problem (ising or qubo)
192        #[serde(rename = "type")]
193        pub type_: String,
194
195        /// The solver to use
196        pub solver: String,
197
198        /// The parameters for the problem
199        pub params: ProblemParams,
200    }
201
202    /// D-Wave problem solution
203    #[derive(Debug, Clone, Serialize, Deserialize)]
204    pub struct Solution {
205        /// The energy of each sample
206        pub energies: Vec<f64>,
207
208        /// The occurrences of each sample
209        pub occurrences: Vec<usize>,
210
211        /// The solutions (spin values for Ising, binary values for QUBO)
212        pub solutions: Vec<Vec<i8>>,
213
214        /// The number of samples
215        pub num_samples: usize,
216
217        /// The problem ID
218        pub problem_id: String,
219
220        /// The solver used
221        pub solver: String,
222
223        /// The timing information
224        pub timing: serde_json::Value,
225    }
226
227    /// Enhanced solver types for Leap
228    #[derive(Debug, Clone, Serialize, Deserialize)]
229    pub enum SolverType {
230        /// Quantum annealing processor
231        #[serde(rename = "qpu")]
232        QuantumProcessor,
233        /// Hybrid classical-quantum solver
234        #[serde(rename = "hybrid")]
235        Hybrid,
236        /// Discrete Quadratic Model solver
237        #[serde(rename = "dqm")]
238        DiscreteQuadraticModel,
239        /// Constrained Quadratic Model solver
240        #[serde(rename = "cqm")]
241        ConstrainedQuadraticModel,
242        /// Software solver/simulator
243        #[serde(rename = "software")]
244        Software,
245    }
246
247    /// Solver category for filtering
248    #[derive(Debug, Clone, PartialEq, Eq)]
249    pub enum SolverCategory {
250        /// Quantum processing units
251        QPU,
252        /// Hybrid solvers
253        Hybrid,
254        /// Software-based solvers
255        Software,
256        /// All solver types
257        All,
258    }
259
260    /// Problem status tracking
261    #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
262    pub enum ProblemStatus {
263        /// Problem is being processed
264        #[serde(rename = "IN_PROGRESS")]
265        InProgress,
266        /// Problem completed successfully
267        #[serde(rename = "COMPLETED")]
268        Completed,
269        /// Problem failed
270        #[serde(rename = "FAILED")]
271        Failed,
272        /// Problem was cancelled
273        #[serde(rename = "CANCELLED")]
274        Cancelled,
275        /// Problem is pending
276        #[serde(rename = "PENDING")]
277        Pending,
278    }
279
280    /// Problem metadata and tracking
281    #[derive(Debug, Clone, Serialize, Deserialize)]
282    pub struct ProblemInfo {
283        /// Problem ID
284        pub id: String,
285        /// Problem status
286        pub status: ProblemStatus,
287        /// Submission time
288        pub submitted_on: String,
289        /// Solver used
290        pub solver: String,
291        /// Problem type
292        #[serde(rename = "type")]
293        pub problem_type: String,
294        /// Parameters used
295        pub params: serde_json::Value,
296        /// Additional metadata
297        #[serde(flatten)]
298        pub metadata: serde_json::Value,
299    }
300
301    /// Enhanced solver information with Leap features
302    #[derive(Debug, Clone, Serialize, Deserialize)]
303    pub struct LeapSolverInfo {
304        /// Solver ID
305        pub id: String,
306        /// Solver name
307        pub name: String,
308        /// Solver description
309        pub description: String,
310        /// Solver type
311        #[serde(rename = "category")]
312        pub solver_type: SolverType,
313        /// Status (online/offline)
314        pub status: String,
315        /// Solver properties
316        pub properties: serde_json::Value,
317        /// Supported problem types
318        pub problem_types: Vec<String>,
319        /// Average queue time
320        pub avg_load: Option<f64>,
321        /// Whether solver is available
322        pub available: bool,
323    }
324
325    /// Custom annealing schedule
326    #[derive(Debug, Clone, Serialize, Deserialize)]
327    pub struct AnnealingSchedule {
328        /// Time points in microseconds
329        pub schedule: Vec<(f64, f64)>,
330    }
331
332    impl AnnealingSchedule {
333        /// Create a linear annealing schedule
334        pub fn linear(annealing_time: f64) -> Self {
335            Self {
336                schedule: vec![
337                    (0.0, 1.0),            // Start with full transverse field
338                    (annealing_time, 0.0), // End with no transverse field
339                ],
340            }
341        }
342
343        /// Create a pause-and-ramp schedule
344        pub fn pause_and_ramp(annealing_time: f64, pause_start: f64, pause_duration: f64) -> Self {
345            Self {
346                schedule: vec![
347                    (0.0, 1.0),
348                    (pause_start, 1.0 - pause_start / annealing_time),
349                    (
350                        pause_start + pause_duration,
351                        1.0 - pause_start / annealing_time,
352                    ),
353                    (annealing_time, 0.0),
354                ],
355            }
356        }
357
358        /// Create a custom schedule from points
359        pub fn custom(points: Vec<(f64, f64)>) -> Self {
360            Self { schedule: points }
361        }
362    }
363
364    /// Advanced problem parameters for Leap
365    #[derive(Debug, Clone, Serialize, Deserialize)]
366    pub struct AdvancedProblemParams {
367        /// Number of reads/samples
368        pub num_reads: usize,
369        /// Custom annealing schedule
370        #[serde(skip_serializing_if = "Option::is_none")]
371        pub anneal_schedule: Option<AnnealingSchedule>,
372        /// Programming thermalization
373        #[serde(skip_serializing_if = "Option::is_none")]
374        pub programming_thermalization: Option<usize>,
375        /// Readout thermalization
376        #[serde(skip_serializing_if = "Option::is_none")]
377        pub readout_thermalization: Option<usize>,
378        /// Auto-scale flag
379        #[serde(skip_serializing_if = "Option::is_none")]
380        pub auto_scale: Option<bool>,
381        /// Chain strength
382        #[serde(skip_serializing_if = "Option::is_none")]
383        pub chain_strength: Option<f64>,
384        /// Flux biases
385        #[serde(skip_serializing_if = "Option::is_none")]
386        pub flux_biases: Option<HashMap<String, f64>>,
387        /// Additional parameters
388        #[serde(flatten)]
389        pub extra: HashMap<String, serde_json::Value>,
390    }
391
392    impl Default for AdvancedProblemParams {
393        fn default() -> Self {
394            Self {
395                num_reads: 1000,
396                anneal_schedule: None,
397                programming_thermalization: Some(1000),
398                readout_thermalization: Some(0),
399                auto_scale: Some(true),
400                chain_strength: None,
401                flux_biases: None,
402                extra: HashMap::new(),
403            }
404        }
405    }
406
407    /// Hybrid solver parameters
408    #[derive(Debug, Clone, Serialize, Deserialize)]
409    pub struct HybridSolverParams {
410        /// Time limit in seconds
411        #[serde(skip_serializing_if = "Option::is_none")]
412        pub time_limit: Option<f64>,
413        /// Maximum number of variables
414        #[serde(skip_serializing_if = "Option::is_none")]
415        pub max_variables: Option<usize>,
416        /// Additional solver-specific parameters
417        #[serde(flatten)]
418        pub extra: HashMap<String, serde_json::Value>,
419    }
420
421    impl Default for HybridSolverParams {
422        fn default() -> Self {
423            Self {
424                time_limit: Some(5.0),
425                max_variables: None,
426                extra: HashMap::new(),
427            }
428        }
429    }
430
431    /// Performance metrics for problems
432    #[derive(Debug, Clone)]
433    pub struct ProblemMetrics {
434        /// Total execution time
435        pub total_time: Duration,
436        /// Queue time
437        pub queue_time: Duration,
438        /// Solver access time
439        pub access_time: Duration,
440        /// Programming time
441        pub programming_time: Duration,
442        /// Sampling time
443        pub sampling_time: Duration,
444        /// Readout time
445        pub readout_time: Duration,
446        /// Best energy found
447        pub best_energy: f64,
448        /// Number of valid solutions
449        pub num_valid_solutions: usize,
450        /// Chain break fraction
451        pub chain_break_fraction: Option<f64>,
452    }
453
454    /// Batch problem submission result
455    #[derive(Debug)]
456    pub struct BatchSubmissionResult {
457        /// List of submitted problem IDs
458        pub problem_ids: Vec<String>,
459        /// Success/failure status for each problem
460        pub statuses: Vec<Result<String, DWaveError>>,
461        /// Total submission time
462        pub submission_time: Duration,
463    }
464
465    /// Solver selection criteria
466    #[derive(Debug, Clone)]
467    pub struct SolverSelector {
468        /// Solver category filter
469        pub category: SolverCategory,
470        /// Minimum number of qubits
471        pub min_qubits: Option<usize>,
472        /// Maximum queue time preference
473        pub max_queue_time: Option<f64>,
474        /// Prefer online solvers
475        pub online_only: bool,
476        /// Specific solver name pattern
477        pub name_pattern: Option<String>,
478        /// Topology preference
479        pub topology_preference: Option<String>,
480    }
481
482    impl Default for SolverSelector {
483        fn default() -> Self {
484            Self {
485                category: SolverCategory::All,
486                min_qubits: None,
487                max_queue_time: None,
488                online_only: true,
489                name_pattern: None,
490                topology_preference: None,
491            }
492        }
493    }
494
495    /// Problem embedding configuration
496    #[derive(Debug, Clone)]
497    pub struct EmbeddingConfig {
498        /// Use automatic embedding
499        pub auto_embed: bool,
500        /// Embedding timeout
501        pub timeout: Duration,
502        /// Chain strength calculation method
503        pub chain_strength_method: ChainStrengthMethod,
504        /// Custom embedding (if not auto-embedding)
505        pub custom_embedding: Option<Embedding>,
506        /// Embedding optimization level
507        pub optimization_level: usize,
508    }
509
510    /// Chain strength calculation methods
511    #[derive(Debug, Clone)]
512    pub enum ChainStrengthMethod {
513        /// Automatic calculation
514        Auto,
515        /// Fixed value
516        Fixed(f64),
517        /// Based on problem coupling strengths
518        Adaptive(f64), // multiplier
519    }
520
521    impl Default for EmbeddingConfig {
522        fn default() -> Self {
523            Self {
524                auto_embed: true,
525                timeout: Duration::from_secs(30),
526                chain_strength_method: ChainStrengthMethod::Auto,
527                custom_embedding: None,
528                optimization_level: 1,
529            }
530        }
531    }
532
533    /// Enhanced D-Wave Leap cloud service client
534    #[derive(Debug)]
535    pub struct DWaveClient {
536        /// The HTTP client for making API requests
537        client: Client,
538
539        /// The API endpoint
540        endpoint: String,
541
542        /// The API token
543        token: String,
544
545        /// The tokio runtime for async requests
546        runtime: Runtime,
547
548        /// Default solver selector
549        default_solver_selector: SolverSelector,
550
551        /// Default embedding configuration
552        default_embedding_config: EmbeddingConfig,
553
554        /// Retry configuration
555        max_retries: usize,
556
557        /// Request timeout
558        request_timeout: Duration,
559
560        /// Default problem timeout
561        problem_timeout: Duration,
562    }
563
564    impl DWaveClient {
565        /// Create a new enhanced D-Wave Leap client
566        pub fn new(token: impl Into<String>, endpoint: Option<String>) -> DWaveResult<Self> {
567            Self::with_config(
568                token,
569                endpoint,
570                SolverSelector::default(),
571                EmbeddingConfig::default(),
572            )
573        }
574
575        /// Create a D-Wave client with custom configuration
576        pub fn with_config(
577            token: impl Into<String>,
578            endpoint: Option<String>,
579            solver_selector: SolverSelector,
580            embedding_config: EmbeddingConfig,
581        ) -> DWaveResult<Self> {
582            // Create HTTP client with appropriate timeout
583            let client = Client::builder()
584                .timeout(Duration::from_secs(300)) // Increased timeout for large problems
585                .build()
586                .map_err(DWaveError::NetworkError)?;
587
588            // Create tokio runtime
589            let runtime = Runtime::new().map_err(|e| DWaveError::RuntimeError(e.to_string()))?;
590
591            // Default Leap endpoint if not provided
592            let endpoint =
593                endpoint.unwrap_or_else(|| "https://cloud.dwavesys.com/sapi/v2".to_string());
594
595            Ok(Self {
596                client,
597                endpoint,
598                token: token.into(),
599                runtime,
600                default_solver_selector: solver_selector,
601                default_embedding_config: embedding_config,
602                max_retries: 3,
603                request_timeout: Duration::from_secs(300),
604                problem_timeout: Duration::from_secs(1800), // 30 minutes
605            })
606        }
607
608        /// Get a list of available solvers
609        pub fn get_solvers(&self) -> DWaveResult<Vec<SolverInfo>> {
610            // Create the URL
611            let url = format!("{}/solvers/remote", self.endpoint);
612
613            // Execute the request
614            self.runtime.block_on(async {
615                let response = self
616                    .client
617                    .get(&url)
618                    .header("Authorization", format!("token {}", self.token))
619                    .send()
620                    .await?;
621
622                // Check for errors
623                if !response.status().is_success() {
624                    let status = response.status();
625                    let error_text = response.text().await?;
626                    return Err(DWaveError::ApiError(format!(
627                        "Error getting solvers: {status} - {error_text}"
628                    )));
629                }
630
631                // Parse the response
632                let solvers: Vec<SolverInfo> = response.json().await?;
633                Ok(solvers)
634            })
635        }
636
637        /// Submit an Ising model to D-Wave
638        pub fn submit_ising(
639            &self,
640            model: &IsingModel,
641            solver_id: &str,
642            params: ProblemParams,
643        ) -> DWaveResult<Solution> {
644            // Convert the Ising model to the format expected by D-Wave
645            let mut linear_terms = serde_json::Map::new();
646            for (qubit, bias) in model.biases() {
647                let value = serde_json::to_value(bias).map_err(|e| {
648                    DWaveError::ProblemError(format!("Failed to serialize bias: {e}"))
649                })?;
650                linear_terms.insert(qubit.to_string(), value);
651            }
652
653            let mut quadratic_terms = serde_json::Map::new();
654            for coupling in model.couplings() {
655                let key = format!("{},{}", coupling.i, coupling.j);
656                let value = serde_json::to_value(coupling.strength).map_err(|e| {
657                    DWaveError::ProblemError(format!("Failed to serialize coupling: {e}"))
658                })?;
659                quadratic_terms.insert(key, value);
660            }
661
662            // Create the problem
663            let problem = Problem {
664                linear_terms: serde_json::Value::Object(linear_terms),
665                quadratic_terms: serde_json::Value::Object(quadratic_terms),
666                type_: "ising".to_string(),
667                solver: solver_id.to_string(),
668                params,
669            };
670
671            // Submit the problem
672            self.submit_problem(&problem)
673        }
674
675        /// Submit a QUBO model to D-Wave
676        pub fn submit_qubo(
677            &self,
678            model: &QuboModel,
679            solver_id: &str,
680            params: ProblemParams,
681        ) -> DWaveResult<Solution> {
682            // Convert the QUBO model to the format expected by D-Wave
683            let mut linear_terms = serde_json::Map::new();
684            for (var, value) in model.linear_terms() {
685                let json_value = serde_json::to_value(value).map_err(|e| {
686                    DWaveError::ProblemError(format!("Failed to serialize linear term: {e}"))
687                })?;
688                linear_terms.insert(var.to_string(), json_value);
689            }
690
691            let mut quadratic_terms = serde_json::Map::new();
692            for (var1, var2, value) in model.quadratic_terms() {
693                let key = format!("{var1},{var2}");
694                let json_value = serde_json::to_value(value).map_err(|e| {
695                    DWaveError::ProblemError(format!("Failed to serialize quadratic term: {e}"))
696                })?;
697                quadratic_terms.insert(key, json_value);
698            }
699
700            // Create the problem
701            let problem = Problem {
702                linear_terms: serde_json::Value::Object(linear_terms),
703                quadratic_terms: serde_json::Value::Object(quadratic_terms),
704                type_: "qubo".to_string(),
705                solver: solver_id.to_string(),
706                params,
707            };
708
709            // Submit the problem
710            self.submit_problem(&problem)
711        }
712
713        /// Submit an Ising model with flux bias optimization
714        pub fn submit_ising_with_flux_bias(
715            &self,
716            model: &IsingModel,
717            solver_id: &str,
718            params: ProblemParams,
719            flux_biases: &std::collections::HashMap<usize, f64>,
720        ) -> DWaveResult<Solution> {
721            let mut params_with_flux = params;
722
723            // Convert flux biases to the format expected by D-Wave
724            let mut flux_map = serde_json::Map::new();
725            for (qubit, &flux_bias) in flux_biases {
726                let value = serde_json::to_value(flux_bias).map_err(|e| {
727                    DWaveError::ProblemError(format!("Failed to serialize flux bias: {e}"))
728                })?;
729                flux_map.insert(qubit.to_string(), value);
730            }
731            params_with_flux.flux_bias_map = Some(flux_map);
732
733            self.submit_ising(model, solver_id, params_with_flux)
734        }
735
736        /// Submit a problem to D-Wave
737        fn submit_problem(&self, problem: &Problem) -> DWaveResult<Solution> {
738            // Create the URL
739            let url = format!("{}/problems", self.endpoint);
740
741            // Execute the request
742            self.runtime.block_on(async {
743                // Submit the problem
744                let response = self
745                    .client
746                    .post(&url)
747                    .header("Authorization", format!("token {}", self.token))
748                    .header("Content-Type", "application/json")
749                    .json(problem)
750                    .send()
751                    .await?;
752
753                // Check for errors
754                if !response.status().is_success() {
755                    let status = response.status();
756                    let error_text = response.text().await?;
757                    return Err(DWaveError::ApiError(format!(
758                        "Error submitting problem: {status} - {error_text}"
759                    )));
760                }
761
762                // Get the problem ID
763                let submit_response: serde_json::Value = response.json().await?;
764                let problem_id = submit_response["id"].as_str().ok_or_else(|| {
765                    // Create the error string first
766                    let error_msg = String::from("Failed to extract problem ID from response");
767                    // Then return the error itself
768                    DWaveError::ApiError(error_msg)
769                })?;
770
771                // Poll for the result
772                let result_url = format!("{}/problems/{}", self.endpoint, problem_id);
773                let mut attempts = 0;
774                const MAX_ATTEMPTS: usize = 60; // 5 minutes with 5-second delay
775
776                while attempts < MAX_ATTEMPTS {
777                    // Get the problem status
778                    let status_response = self
779                        .client
780                        .get(&result_url)
781                        .header("Authorization", format!("token {}", self.token))
782                        .send()
783                        .await?;
784
785                    // Check for errors
786                    if !status_response.status().is_success() {
787                        let status = status_response.status();
788                        let error_text = status_response.text().await?;
789                        return Err(DWaveError::ApiError(format!(
790                            "Error getting problem status: {status} - {error_text}"
791                        )));
792                    }
793
794                    // Parse the response
795                    let status: serde_json::Value = status_response.json().await?;
796
797                    // Check if the problem is done
798                    if let Some(state) = status["state"].as_str() {
799                        if state == "COMPLETED" {
800                            // Get the solution
801                            return Ok(Solution {
802                                energies: serde_json::from_value(status["energies"].clone())?,
803                                occurrences: serde_json::from_value(status["occurrences"].clone())?,
804                                solutions: serde_json::from_value(status["solutions"].clone())?,
805                                num_samples: status["num_samples"].as_u64().unwrap_or(0) as usize,
806                                problem_id: problem_id.to_string(),
807                                solver: problem.solver.clone(),
808                                timing: status["timing"].clone(),
809                            });
810                        } else if state == "FAILED" {
811                            let error = status["error"].as_str().unwrap_or("Unknown error");
812                            return Err(DWaveError::ApiError(format!("Problem failed: {error}")));
813                        }
814                    }
815
816                    // Sleep and try again
817                    tokio::time::sleep(Duration::from_secs(5)).await;
818                    attempts += 1;
819                }
820
821                Err(DWaveError::ApiError(
822                    "Timeout waiting for problem solution".into(),
823                ))
824            })
825        }
826
827        /// Get enhanced Leap solver information
828        pub fn get_leap_solvers(&self) -> DWaveResult<Vec<LeapSolverInfo>> {
829            let url = format!("{}/solvers/remote", self.endpoint);
830
831            self.runtime.block_on(async {
832                let response = self
833                    .client
834                    .get(&url)
835                    .header("Authorization", format!("token {}", self.token))
836                    .send()
837                    .await?;
838
839                if !response.status().is_success() {
840                    let status = response.status();
841                    let error_text = response.text().await?;
842                    return Err(DWaveError::ApiError(format!(
843                        "Error getting Leap solvers: {status} - {error_text}"
844                    )));
845                }
846
847                let solvers: Vec<LeapSolverInfo> = response.json().await?;
848                Ok(solvers)
849            })
850        }
851
852        /// Select optimal solver based on criteria
853        pub fn select_solver(
854            &self,
855            selector: Option<&SolverSelector>,
856        ) -> DWaveResult<LeapSolverInfo> {
857            let selector = selector.unwrap_or(&self.default_solver_selector);
858            let solvers = self.get_leap_solvers()?;
859
860            let filtered_solvers: Vec<_> = solvers
861                .into_iter()
862                .filter(|solver| {
863                    // Filter by category
864                    let category_match = match selector.category {
865                        SolverCategory::QPU => {
866                            matches!(solver.solver_type, SolverType::QuantumProcessor)
867                        }
868                        SolverCategory::Hybrid => matches!(solver.solver_type, SolverType::Hybrid),
869                        SolverCategory::Software => {
870                            matches!(solver.solver_type, SolverType::Software)
871                        }
872                        SolverCategory::All => true,
873                    };
874
875                    // Filter by availability
876                    let availability_match = !selector.online_only || solver.available;
877
878                    // Filter by name pattern
879                    let name_match = selector
880                        .name_pattern
881                        .as_ref()
882                        .map(|pattern| solver.name.contains(pattern))
883                        .unwrap_or(true);
884
885                    // Filter by queue time
886                    let queue_match = selector
887                        .max_queue_time
888                        .map(|max_time| solver.avg_load.unwrap_or(0.0) <= max_time)
889                        .unwrap_or(true);
890
891                    category_match && availability_match && name_match && queue_match
892                })
893                .collect();
894
895            if filtered_solvers.is_empty() {
896                return Err(DWaveError::SolverConfigError(
897                    "No solvers match the selection criteria".to_string(),
898                ));
899            }
900
901            // Sort by preference (lowest queue time first)
902            let mut best_solver = filtered_solvers[0].clone();
903            for solver in &filtered_solvers[1..] {
904                let current_load = best_solver.avg_load.unwrap_or(f64::INFINITY);
905                let candidate_load = solver.avg_load.unwrap_or(f64::INFINITY);
906                if candidate_load < current_load {
907                    best_solver = solver.clone();
908                }
909            }
910
911            Ok(best_solver)
912        }
913
914        /// Submit problem with automatic embedding
915        pub fn submit_ising_with_embedding(
916            &self,
917            model: &IsingModel,
918            solver_id: Option<&str>,
919            params: Option<AdvancedProblemParams>,
920            embedding_config: Option<&EmbeddingConfig>,
921        ) -> DWaveResult<Solution> {
922            let embedding_config = embedding_config.unwrap_or(&self.default_embedding_config);
923
924            // Select solver if not provided
925            let solver = if let Some(id) = solver_id {
926                self.get_leap_solvers()?
927                    .into_iter()
928                    .find(|s| s.id == id)
929                    .ok_or_else(|| {
930                        DWaveError::SolverConfigError(format!("Solver {id} not found"))
931                    })?
932            } else {
933                self.select_solver(None)?
934            };
935
936            // Check if embedding is needed
937            if matches!(solver.solver_type, SolverType::QuantumProcessor) {
938                self.submit_with_auto_embedding(model, &solver, params, embedding_config)
939            } else {
940                // For hybrid solvers, convert to standard submission
941                let legacy_params = if let Some(p) = params {
942                    let flux_bias_map = if let Some(fb) = p.flux_biases {
943                        let mut map = serde_json::Map::new();
944                        for (k, v) in fb {
945                            let value = serde_json::to_value(v).map_err(|e| {
946                                DWaveError::ProblemError(format!(
947                                    "Failed to serialize flux bias: {e}"
948                                ))
949                            })?;
950                            map.insert(k, value);
951                        }
952                        Some(map)
953                    } else {
954                        None
955                    };
956                    ProblemParams {
957                        num_reads: p.num_reads,
958                        annealing_time: 20,
959                        programming_therm: p.programming_thermalization.unwrap_or(1000),
960                        readout_therm: p.readout_thermalization.unwrap_or(0),
961                        flux_biases: None, // Use flux_bias_map instead
962                        flux_bias_map,
963                        other: serde_json::Value::Object(serde_json::Map::new()),
964                    }
965                } else {
966                    ProblemParams::default()
967                };
968
969                self.submit_ising(model, &solver.id, legacy_params)
970            }
971        }
972
973        /// Submit with automatic embedding for QPU solvers
974        fn submit_with_auto_embedding(
975            &self,
976            model: &IsingModel,
977            solver: &LeapSolverInfo,
978            params: Option<AdvancedProblemParams>,
979            embedding_config: &EmbeddingConfig,
980        ) -> DWaveResult<Solution> {
981            let params = params.unwrap_or_default();
982
983            // Create logical problem graph
984            let mut logical_edges = Vec::new();
985            for coupling in model.couplings() {
986                logical_edges.push((coupling.i, coupling.j));
987            }
988
989            // Get solver topology and create hardware graph
990            let hardware_graph = self.get_solver_topology(&solver.id)?;
991
992            // Find embedding
993            let embedding = if let Some(custom_emb) = &embedding_config.custom_embedding {
994                custom_emb.clone()
995            } else {
996                let embedder = MinorMiner {
997                    max_tries: 10 * embedding_config.optimization_level,
998                    ..Default::default()
999                };
1000                embedder
1001                    .find_embedding(&logical_edges, model.num_qubits, &hardware_graph)
1002                    .map_err(|e| DWaveError::EmbeddingError(e.to_string()))?
1003            };
1004
1005            // Calculate chain strength
1006            let chain_strength =
1007                Self::calculate_chain_strength(model, &embedding_config.chain_strength_method);
1008
1009            // Create embedded problem
1010            let embedded_problem = Self::embed_problem(model, &embedding, chain_strength)?;
1011
1012            // Submit embedded problem
1013            self.submit_embedded_problem(&embedded_problem, solver, params)
1014        }
1015
1016        /// Get solver topology information
1017        fn get_solver_topology(&self, solver_id: &str) -> DWaveResult<HardwareGraph> {
1018            let url = format!("{}/solvers/remote/{}", self.endpoint, solver_id);
1019
1020            let topology_info = self.runtime.block_on(async {
1021                let response = self
1022                    .client
1023                    .get(&url)
1024                    .header("Authorization", format!("token {}", self.token))
1025                    .send()
1026                    .await?;
1027
1028                if !response.status().is_success() {
1029                    let status = response.status();
1030                    let error_text = response.text().await?;
1031                    return Err(DWaveError::ApiError(format!(
1032                        "Error getting solver topology: {status} - {error_text}"
1033                    )));
1034                }
1035
1036                let solver_data: serde_json::Value = response.json().await?;
1037                Ok(solver_data)
1038            })?;
1039
1040            // Parse topology information
1041            let properties = &topology_info["properties"];
1042
1043            if let Some(edges) = properties["couplers"].as_array() {
1044                let mut hardware_edges = Vec::new();
1045                for edge in edges {
1046                    if let (Some(i), Some(j)) = (edge[0].as_u64(), edge[1].as_u64()) {
1047                        hardware_edges.push((i as usize, j as usize));
1048                    }
1049                }
1050
1051                let num_qubits = properties["qubits"]
1052                    .as_array()
1053                    .map(|arr| arr.len())
1054                    .unwrap_or(0);
1055
1056                Ok(HardwareGraph::new_custom(num_qubits, hardware_edges))
1057            } else {
1058                Err(DWaveError::SolverConfigError(
1059                    "Could not parse solver topology".to_string(),
1060                ))
1061            }
1062        }
1063
1064        /// Calculate appropriate chain strength
1065        fn calculate_chain_strength(model: &IsingModel, method: &ChainStrengthMethod) -> f64 {
1066            match method {
1067                ChainStrengthMethod::Auto => {
1068                    // Calculate based on maximum coupling strength
1069                    let max_coupling = model
1070                        .couplings()
1071                        .iter()
1072                        .map(|c| c.strength.abs())
1073                        .fold(0.0, f64::max);
1074
1075                    let max_bias = (0..model.num_qubits)
1076                        .filter_map(|i| model.get_bias(i).ok())
1077                        .fold(0.0_f64, |acc, bias| acc.max(bias.abs()));
1078
1079                    2.0 * (max_coupling.max(max_bias))
1080                }
1081                ChainStrengthMethod::Fixed(value) => *value,
1082                ChainStrengthMethod::Adaptive(multiplier) => {
1083                    let avg_coupling = model
1084                        .couplings()
1085                        .iter()
1086                        .map(|c| c.strength.abs())
1087                        .sum::<f64>()
1088                        / model.couplings().len().max(1) as f64;
1089
1090                    multiplier * avg_coupling
1091                }
1092            }
1093        }
1094
1095        /// Embed logical problem onto physical hardware
1096        fn embed_problem(
1097            model: &IsingModel,
1098            embedding: &Embedding,
1099            chain_strength: f64,
1100        ) -> DWaveResult<IsingModel> {
1101            let mut embedded_model = IsingModel::new(0); // Will be resized
1102
1103            // Find maximum physical qubit index
1104            let max_qubit = embedding
1105                .chains
1106                .values()
1107                .flat_map(|chain| chain.iter())
1108                .max()
1109                .copied()
1110                .unwrap_or(0);
1111
1112            embedded_model = IsingModel::new(max_qubit + 1);
1113
1114            // Embed linear terms (biases)
1115            for (var, chain) in &embedding.chains {
1116                if let Ok(bias) = model.get_bias(*var) {
1117                    if bias != 0.0 {
1118                        // Distribute bias evenly across chain
1119                        let bias_per_qubit = bias / chain.len() as f64;
1120                        for &qubit in chain {
1121                            embedded_model
1122                                .set_bias(qubit, bias_per_qubit)
1123                                .map_err(|e| DWaveError::EmbeddingError(e.to_string()))?;
1124                        }
1125                    }
1126                }
1127            }
1128
1129            // Embed quadratic terms (couplings)
1130            for coupling in model.couplings() {
1131                if let (Some(chain1), Some(chain2)) = (
1132                    embedding.chains.get(&coupling.i),
1133                    embedding.chains.get(&coupling.j),
1134                ) {
1135                    // Create couplings between all pairs of qubits in the chains
1136                    for &q1 in chain1 {
1137                        for &q2 in chain2 {
1138                            embedded_model
1139                                .set_coupling(q1, q2, coupling.strength)
1140                                .map_err(|e| DWaveError::EmbeddingError(e.to_string()))?;
1141                        }
1142                    }
1143                }
1144            }
1145
1146            // Add chain couplings (ferromagnetic couplings within chains)
1147            for chain in embedding.chains.values() {
1148                for window in chain.windows(2) {
1149                    if let [q1, q2] = window {
1150                        embedded_model
1151                            .set_coupling(*q1, *q2, -chain_strength)
1152                            .map_err(|e| DWaveError::EmbeddingError(e.to_string()))?;
1153                    }
1154                }
1155            }
1156
1157            Ok(embedded_model)
1158        }
1159
1160        /// Submit an embedded problem
1161        fn submit_embedded_problem(
1162            &self,
1163            embedded_model: &IsingModel,
1164            solver: &LeapSolverInfo,
1165            params: AdvancedProblemParams,
1166        ) -> DWaveResult<Solution> {
1167            // Convert flux biases if present
1168            let flux_bias_map = if let Some(fb) = params.flux_biases {
1169                let mut map = serde_json::Map::new();
1170                for (k, v) in fb {
1171                    let value = serde_json::to_value(v).map_err(|e| {
1172                        DWaveError::ProblemError(format!("Failed to serialize flux bias: {e}"))
1173                    })?;
1174                    map.insert(k, value);
1175                }
1176                Some(map)
1177            } else {
1178                None
1179            };
1180
1181            // Convert advanced parameters to legacy format
1182            let legacy_params = ProblemParams {
1183                num_reads: params.num_reads,
1184                annealing_time: params
1185                    .anneal_schedule
1186                    .as_ref()
1187                    .and_then(|schedule| schedule.schedule.last())
1188                    .map(|(time, _)| *time as usize)
1189                    .unwrap_or(20),
1190                programming_therm: params.programming_thermalization.unwrap_or(1000),
1191                readout_therm: params.readout_thermalization.unwrap_or(0),
1192                flux_biases: None, // Use flux_bias_map instead
1193                flux_bias_map,
1194                other: serde_json::Value::Object(serde_json::Map::new()),
1195            };
1196
1197            self.submit_ising(embedded_model, &solver.id, legacy_params)
1198        }
1199
1200        /// Submit hybrid solver problem
1201        pub fn submit_hybrid(
1202            &self,
1203            model: &IsingModel,
1204            solver_id: Option<&str>,
1205            params: Option<HybridSolverParams>,
1206        ) -> DWaveResult<Solution> {
1207            let params = params.unwrap_or_default();
1208
1209            // Select hybrid solver if not specified
1210            let solver = if let Some(id) = solver_id {
1211                self.get_leap_solvers()?
1212                    .into_iter()
1213                    .find(|s| s.id == id)
1214                    .ok_or_else(|| {
1215                        DWaveError::SolverConfigError(format!("Solver {id} not found"))
1216                    })?
1217            } else {
1218                let hybrid_selector = SolverSelector {
1219                    category: SolverCategory::Hybrid,
1220                    ..Default::default()
1221                };
1222                self.select_solver(Some(&hybrid_selector))?
1223            };
1224
1225            // Convert to appropriate format for hybrid submission
1226            let mut linear_terms = serde_json::Map::new();
1227            for (qubit, bias) in model.biases() {
1228                let value = serde_json::to_value(bias).map_err(|e| {
1229                    DWaveError::ProblemError(format!("Failed to serialize bias: {e}"))
1230                })?;
1231                linear_terms.insert(qubit.to_string(), value);
1232            }
1233
1234            let mut quadratic_terms = serde_json::Map::new();
1235            for coupling in model.couplings() {
1236                let key = format!("{},{}", coupling.i, coupling.j);
1237                let value = serde_json::to_value(coupling.strength).map_err(|e| {
1238                    DWaveError::ProblemError(format!("Failed to serialize coupling: {e}"))
1239                })?;
1240                quadratic_terms.insert(key, value);
1241            }
1242
1243            // Add hybrid-specific parameters
1244            let mut hybrid_params = params.extra.clone();
1245            if let Some(time_limit) = params.time_limit {
1246                let value = serde_json::to_value(time_limit).map_err(|e| {
1247                    DWaveError::ProblemError(format!("Failed to serialize time_limit: {e}"))
1248                })?;
1249                hybrid_params.insert("time_limit".to_string(), value);
1250            }
1251
1252            let problem = Problem {
1253                linear_terms: serde_json::Value::Object(linear_terms),
1254                quadratic_terms: serde_json::Value::Object(quadratic_terms),
1255                type_: "ising".to_string(),
1256                solver: solver.id,
1257                params: ProblemParams {
1258                    num_reads: 1, // Hybrid solvers typically return one solution
1259                    annealing_time: 1,
1260                    programming_therm: 0,
1261                    readout_therm: 0,
1262                    flux_biases: None,
1263                    flux_bias_map: None,
1264                    other: serde_json::Value::Object(
1265                        hybrid_params.into_iter().map(|(k, v)| (k, v)).collect(),
1266                    ),
1267                },
1268            };
1269
1270            self.submit_problem(&problem)
1271        }
1272
1273        /// Get problem status
1274        pub fn get_problem_status(&self, problem_id: &str) -> DWaveResult<ProblemInfo> {
1275            let url = format!("{}/problems/{}", self.endpoint, problem_id);
1276
1277            self.runtime.block_on(async {
1278                let response = self
1279                    .client
1280                    .get(&url)
1281                    .header("Authorization", format!("token {}", self.token))
1282                    .send()
1283                    .await?;
1284
1285                if !response.status().is_success() {
1286                    let status = response.status();
1287                    let error_text = response.text().await?;
1288                    return Err(DWaveError::ApiError(format!(
1289                        "Error getting problem status: {} - {}",
1290                        status, error_text
1291                    )));
1292                }
1293
1294                let problem_info: ProblemInfo = response.json().await?;
1295                Ok(problem_info)
1296            })
1297        }
1298
1299        /// Cancel a running problem
1300        pub fn cancel_problem(&self, problem_id: &str) -> DWaveResult<()> {
1301            let url = format!("{}/problems/{}/cancel", self.endpoint, problem_id);
1302
1303            self.runtime.block_on(async {
1304                let response = self
1305                    .client
1306                    .delete(&url)
1307                    .header("Authorization", format!("token {}", self.token))
1308                    .send()
1309                    .await?;
1310
1311                if !response.status().is_success() {
1312                    let status = response.status();
1313                    let error_text = response.text().await?;
1314                    return Err(DWaveError::ApiError(format!(
1315                        "Error cancelling problem: {} - {}",
1316                        status, error_text
1317                    )));
1318                }
1319
1320                Ok(())
1321            })
1322        }
1323
1324        /// Submit multiple problems in batch
1325        pub fn submit_batch(
1326            &self,
1327            problems: Vec<(&IsingModel, Option<&str>, Option<AdvancedProblemParams>)>,
1328        ) -> DWaveResult<BatchSubmissionResult> {
1329            let start_time = Instant::now();
1330            let mut problem_ids = Vec::new();
1331            let mut statuses = Vec::new();
1332
1333            for (model, solver_id, params) in problems {
1334                match self.submit_ising_with_embedding(model, solver_id, params, None) {
1335                    Ok(solution) => {
1336                        problem_ids.push(solution.problem_id.clone());
1337                        statuses.push(Ok(solution.problem_id));
1338                    }
1339                    Err(e) => {
1340                        problem_ids.push(String::new());
1341                        statuses.push(Err(e));
1342                    }
1343                }
1344            }
1345
1346            Ok(BatchSubmissionResult {
1347                problem_ids,
1348                statuses,
1349                submission_time: start_time.elapsed(),
1350            })
1351        }
1352
1353        /// Get performance metrics for a completed problem
1354        pub fn get_problem_metrics(&self, problem_id: &str) -> DWaveResult<ProblemMetrics> {
1355            let solution = self.get_problem_result(problem_id)?;
1356            let timing = &solution.timing;
1357
1358            // Extract timing information
1359            let queue_time =
1360                Duration::from_micros(timing["qpu_access_overhead_time"].as_u64().unwrap_or(0));
1361            let programming_time =
1362                Duration::from_micros(timing["qpu_programming_time"].as_u64().unwrap_or(0));
1363            let sampling_time =
1364                Duration::from_micros(timing["qpu_sampling_time"].as_u64().unwrap_or(0));
1365            let readout_time =
1366                Duration::from_micros(timing["qpu_readout_time"].as_u64().unwrap_or(0));
1367
1368            let total_time = queue_time + programming_time + sampling_time + readout_time;
1369            let access_time = programming_time + sampling_time + readout_time;
1370
1371            let best_energy = solution
1372                .energies
1373                .iter()
1374                .min_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
1375                .copied()
1376                .unwrap_or(f64::INFINITY);
1377
1378            Ok(ProblemMetrics {
1379                total_time,
1380                queue_time,
1381                access_time,
1382                programming_time,
1383                sampling_time,
1384                readout_time,
1385                best_energy,
1386                num_valid_solutions: solution.solutions.len(),
1387                chain_break_fraction: timing["chain_break_fraction"].as_f64(),
1388            })
1389        }
1390
1391        /// Get problem result (blocking until completion)
1392        pub fn get_problem_result(&self, problem_id: &str) -> DWaveResult<Solution> {
1393            let start_time = Instant::now();
1394
1395            loop {
1396                let status = self.get_problem_status(problem_id)?;
1397
1398                match status.status {
1399                    ProblemStatus::Completed => {
1400                        // Get the full solution
1401                        return self.get_solution_data(problem_id);
1402                    }
1403                    ProblemStatus::Failed => {
1404                        return Err(DWaveError::StatusError(format!(
1405                            "Problem {} failed",
1406                            problem_id
1407                        )));
1408                    }
1409                    ProblemStatus::Cancelled => {
1410                        return Err(DWaveError::StatusError(format!(
1411                            "Problem {} was cancelled",
1412                            problem_id
1413                        )));
1414                    }
1415                    ProblemStatus::InProgress | ProblemStatus::Pending => {
1416                        if start_time.elapsed() > self.problem_timeout {
1417                            return Err(DWaveError::TimeoutError(format!(
1418                                "Timeout waiting for problem {} completion",
1419                                problem_id
1420                            )));
1421                        }
1422                        // Wait before checking again
1423                        std::thread::sleep(Duration::from_secs(2));
1424                    }
1425                }
1426            }
1427        }
1428
1429        /// Get solution data for a completed problem
1430        fn get_solution_data(&self, problem_id: &str) -> DWaveResult<Solution> {
1431            let url = format!("{}/problems/{}", self.endpoint, problem_id);
1432
1433            self.runtime.block_on(async {
1434                let response = self
1435                    .client
1436                    .get(&url)
1437                    .header("Authorization", format!("token {}", self.token))
1438                    .send()
1439                    .await?;
1440
1441                if !response.status().is_success() {
1442                    let status = response.status();
1443                    let error_text = response.text().await?;
1444                    return Err(DWaveError::ApiError(format!(
1445                        "Error getting solution data: {} - {}",
1446                        status, error_text
1447                    )));
1448                }
1449
1450                let data: serde_json::Value = response.json().await?;
1451
1452                Ok(Solution {
1453                    energies: serde_json::from_value(data["energies"].clone())?,
1454                    occurrences: serde_json::from_value(data["occurrences"].clone())?,
1455                    solutions: serde_json::from_value(data["solutions"].clone())?,
1456                    num_samples: data["num_samples"].as_u64().unwrap_or(0) as usize,
1457                    problem_id: problem_id.to_string(),
1458                    solver: data["solver"].as_str().unwrap_or("unknown").to_string(),
1459                    timing: data["timing"].clone(),
1460                })
1461            })
1462        }
1463
1464        /// List recent problems
1465        pub fn list_problems(&self, limit: Option<usize>) -> DWaveResult<Vec<ProblemInfo>> {
1466            let mut url = format!("{}/problems", self.endpoint);
1467            if let Some(limit) = limit {
1468                // Writing to a String is infallible
1469                let _ = write!(url, "?limit={}", limit);
1470            }
1471
1472            self.runtime.block_on(async {
1473                let response = self
1474                    .client
1475                    .get(&url)
1476                    .header("Authorization", format!("token {}", self.token))
1477                    .send()
1478                    .await?;
1479
1480                if !response.status().is_success() {
1481                    let status = response.status();
1482                    let error_text = response.text().await?;
1483                    return Err(DWaveError::ApiError(format!(
1484                        "Error listing problems: {} - {}",
1485                        status, error_text
1486                    )));
1487                }
1488
1489                let problems: Vec<ProblemInfo> = response.json().await?;
1490                Ok(problems)
1491            })
1492        }
1493
1494        /// Get account usage information
1495        pub fn get_usage_info(&self) -> DWaveResult<serde_json::Value> {
1496            let url = format!("{}/usage", self.endpoint);
1497
1498            self.runtime.block_on(async {
1499                let response = self
1500                    .client
1501                    .get(&url)
1502                    .header("Authorization", format!("token {}", self.token))
1503                    .send()
1504                    .await?;
1505
1506                if !response.status().is_success() {
1507                    let status = response.status();
1508                    let error_text = response.text().await?;
1509                    return Err(DWaveError::ApiError(format!(
1510                        "Error getting usage info: {} - {}",
1511                        status, error_text
1512                    )));
1513                }
1514
1515                let usage: serde_json::Value = response.json().await?;
1516                Ok(usage)
1517            })
1518        }
1519    }
1520}
1521
1522#[cfg(feature = "dwave")]
1523pub use client::*;
1524
1525#[cfg(not(feature = "dwave"))]
1526mod placeholder {
1527    use thiserror::Error;
1528
1529    /// Error type for when D-Wave feature is not enabled
1530    #[derive(Error, Debug)]
1531    pub enum DWaveError {
1532        /// Error when trying to use D-Wave without the feature enabled
1533        #[error("D-Wave feature not enabled. Recompile with '--features dwave'")]
1534        NotEnabled,
1535    }
1536
1537    /// Result type for D-Wave operations
1538    pub type DWaveResult<T> = Result<T, DWaveError>;
1539
1540    /// Placeholder for D-Wave client
1541    #[derive(Debug, Clone)]
1542    pub struct DWaveClient {
1543        _private: (),
1544    }
1545
1546    impl DWaveClient {
1547        /// Placeholder for D-Wave client creation
1548        pub fn new(_token: impl Into<String>, _endpoint: Option<String>) -> DWaveResult<Self> {
1549            Err(DWaveError::NotEnabled)
1550        }
1551    }
1552
1553    /// Placeholder for D-Wave problem submission parameters
1554    #[derive(Debug, Clone)]
1555    pub struct ProblemParams {
1556        /// Number of reads/samples to take
1557        pub num_reads: usize,
1558        /// Annealing time in microseconds
1559        pub annealing_time: usize,
1560        /// Programming thermalization in microseconds
1561        pub programming_therm: usize,
1562        /// Read-out thermalization in microseconds
1563        pub readout_therm: usize,
1564    }
1565
1566    /// Placeholder types for enhanced Leap functionality (feature disabled)
1567    #[derive(Debug, Clone)]
1568    pub enum SolverType {
1569        QuantumProcessor,
1570        Hybrid,
1571        Software,
1572    }
1573
1574    #[derive(Debug, Clone)]
1575    pub enum SolverCategory {
1576        QPU,
1577        Hybrid,
1578        Software,
1579        All,
1580    }
1581
1582    #[derive(Debug, Clone)]
1583    pub enum ProblemStatus {
1584        InProgress,
1585        Completed,
1586        Failed,
1587        Cancelled,
1588        Pending,
1589    }
1590
1591    #[derive(Debug, Clone)]
1592    pub struct SolverSelector;
1593
1594    #[derive(Debug, Clone)]
1595    pub struct EmbeddingConfig;
1596
1597    #[derive(Debug, Clone)]
1598    pub struct AdvancedProblemParams;
1599
1600    #[derive(Debug, Clone)]
1601    pub struct HybridSolverParams;
1602
1603    #[derive(Debug, Clone)]
1604    pub struct LeapSolverInfo;
1605
1606    #[derive(Debug, Clone)]
1607    pub struct ProblemInfo;
1608
1609    #[derive(Debug, Clone)]
1610    pub struct AnnealingSchedule;
1611
1612    #[derive(Debug, Clone)]
1613    pub struct ProblemMetrics;
1614
1615    #[derive(Debug, Clone)]
1616    pub struct BatchSubmissionResult;
1617
1618    #[derive(Debug, Clone)]
1619    pub enum ChainStrengthMethod {
1620        Auto,
1621        Fixed(f64),
1622        Adaptive(f64),
1623    }
1624
1625    impl Default for ProblemParams {
1626        fn default() -> Self {
1627            Self {
1628                num_reads: 1000,
1629                annealing_time: 20,
1630                programming_therm: 1000,
1631                readout_therm: 0,
1632            }
1633        }
1634    }
1635}
1636
1637#[cfg(not(feature = "dwave"))]
1638pub use placeholder::*;
1639
1640/// Check if D-Wave API support is enabled
1641#[must_use]
1642pub const fn is_available() -> bool {
1643    cfg!(feature = "dwave")
1644}
1645
1646use std::fmt::Write;