quantrs2_tytan/sampler/hardware/
ibm_quantum.rs1use scirs2_core::ndarray::{Array, Ix2};
7use scirs2_core::random::{thread_rng, Rng};
8use std::collections::HashMap;
9
10use quantrs2_anneal::QuboModel;
11
12use super::super::{SampleResult, Sampler, SamplerError, SamplerResult};
13
14#[derive(Debug, Clone)]
16pub enum IBMBackend {
17 Simulator,
19 Hardware(String),
21 AnyHardware,
23}
24
25#[derive(Debug, Clone)]
27pub struct IBMQuantumConfig {
28 pub api_token: String,
30 pub backend: IBMBackend,
32 pub max_circuit_depth: usize,
34 pub optimization_level: u8,
36 pub shots: usize,
38 pub error_mitigation: bool,
40}
41
42impl Default for IBMQuantumConfig {
43 fn default() -> Self {
44 Self {
45 api_token: String::new(),
46 backend: IBMBackend::Simulator,
47 max_circuit_depth: 100,
48 optimization_level: 1,
49 shots: 1024,
50 error_mitigation: true,
51 }
52 }
53}
54
55pub struct IBMQuantumSampler {
60 config: IBMQuantumConfig,
61}
62
63impl IBMQuantumSampler {
64 #[must_use]
70 pub const fn new(config: IBMQuantumConfig) -> Self {
71 Self { config }
72 }
73
74 #[must_use]
80 pub fn with_token(api_token: &str) -> Self {
81 Self {
82 config: IBMQuantumConfig {
83 api_token: api_token.to_string(),
84 ..Default::default()
85 },
86 }
87 }
88
89 #[must_use]
91 pub fn with_backend(mut self, backend: IBMBackend) -> Self {
92 self.config.backend = backend;
93 self
94 }
95
96 #[must_use]
98 pub const fn with_error_mitigation(mut self, enabled: bool) -> Self {
99 self.config.error_mitigation = enabled;
100 self
101 }
102
103 #[must_use]
105 pub fn with_optimization_level(mut self, level: u8) -> Self {
106 self.config.optimization_level = level.min(3);
107 self
108 }
109}
110
111impl Sampler for IBMQuantumSampler {
112 fn run_qubo(
113 &self,
114 qubo: &(Array<f64, Ix2>, HashMap<String, usize>),
115 shots: usize,
116 ) -> SamplerResult<Vec<SampleResult>> {
117 let (matrix, var_map) = qubo;
119
120 let n_vars = var_map.len();
122
123 if n_vars > 127 {
125 return Err(SamplerError::InvalidParameter(
126 "IBM Quantum currently supports up to 127 qubits".to_string(),
127 ));
128 }
129
130 let idx_to_var: HashMap<usize, String> = var_map
132 .iter()
133 .map(|(var, &idx)| (idx, var.clone()))
134 .collect();
135
136 let mut qubo_model = QuboModel::new(n_vars);
138
139 for i in 0..n_vars {
141 if matrix[[i, i]] != 0.0 {
142 qubo_model.set_linear(i, matrix[[i, i]])?;
143 }
144
145 for j in (i + 1)..n_vars {
146 if matrix[[i, j]] != 0.0 {
147 qubo_model.set_quadratic(i, j, matrix[[i, j]])?;
148 }
149 }
150 }
151
152 #[cfg(feature = "ibm_quantum")]
154 {
155 let _ibm_result = "placeholder";
163 }
164
165 let mut results = Vec::new();
167 let mut rng = thread_rng();
168
169 let effective_shots = if self.config.error_mitigation {
171 shots * 2 } else {
173 shots
174 };
175
176 let unique_solutions = (effective_shots / 10).max(1).min(100);
178
179 for _ in 0..unique_solutions {
180 let assignments: HashMap<String, bool> = idx_to_var
181 .values()
182 .map(|name| (name.clone(), rng.gen::<bool>()))
183 .collect();
184
185 let mut energy = 0.0;
187 for (var_name, &val) in &assignments {
188 let i = var_map[var_name];
189 if val {
190 energy += matrix[[i, i]];
191 for (other_var, &other_val) in &assignments {
192 let j = var_map[other_var];
193 if i < j && other_val {
194 energy += matrix[[i, j]];
195 }
196 }
197 }
198 }
199
200 let occurrences = rng.gen_range(1..=(effective_shots / unique_solutions + 10));
202
203 results.push(SampleResult {
204 assignments,
205 energy,
206 occurrences,
207 });
208 }
209
210 results.sort_by(|a, b| {
212 a.energy
213 .partial_cmp(&b.energy)
214 .unwrap_or(std::cmp::Ordering::Equal)
215 });
216
217 Ok(results)
218 }
219
220 fn run_hobo(
221 &self,
222 hobo: &(
223 Array<f64, scirs2_core::ndarray::IxDyn>,
224 HashMap<String, usize>,
225 ),
226 shots: usize,
227 ) -> SamplerResult<Vec<SampleResult>> {
228 use scirs2_core::ndarray::Ix2;
229
230 if hobo.0.ndim() <= 2 {
232 let qubo_matrix = hobo.0.clone().into_dimensionality::<Ix2>().map_err(|e| {
234 SamplerError::InvalidParameter(format!(
235 "Failed to convert HOBO to QUBO dimensionality: {e}"
236 ))
237 })?;
238 let qubo = (qubo_matrix, hobo.1.clone());
239 self.run_qubo(&qubo, shots)
240 } else {
241 Err(SamplerError::InvalidParameter(
243 "IBM Quantum doesn't support HOBO problems directly. Use a quadratization technique first.".to_string()
244 ))
245 }
246 }
247}
248
249#[cfg(test)]
250mod tests {
251 use super::*;
252
253 #[test]
254 fn test_ibm_quantum_config() {
255 let config = IBMQuantumConfig::default();
256 assert_eq!(config.optimization_level, 1);
257 assert_eq!(config.shots, 1024);
258 assert!(config.error_mitigation);
259 }
260
261 #[test]
262 fn test_ibm_quantum_sampler_creation() {
263 let sampler = IBMQuantumSampler::with_token("test_token")
264 .with_backend(IBMBackend::Simulator)
265 .with_error_mitigation(true)
266 .with_optimization_level(2);
267
268 assert_eq!(sampler.config.api_token, "test_token");
269 assert_eq!(sampler.config.optimization_level, 2);
270 assert!(sampler.config.error_mitigation);
271 }
272
273 #[test]
274 fn test_ibm_quantum_backend_types() {
275 let simulator = IBMBackend::Simulator;
276 let hardware = IBMBackend::Hardware("ibmq_lima".to_string());
277 let any = IBMBackend::AnyHardware;
278
279 let _sim_clone = simulator;
281 let _hw_clone = hardware;
282 let _any_clone = any;
283 }
284}