1use crate::{DeviceError, DeviceResult};
8use scirs2_core::ndarray::{Array1, Array2, ArrayView1};
9use scirs2_core::random::prelude::*;
10use scirs2_core::Complex64;
11use scirs2_optimize::unconstrained::{minimize, Method, Options};
12
13#[derive(Debug, Clone)]
15pub struct HybridAlgorithmConfig {
16 pub max_iterations: usize,
18 pub tolerance: f64,
20 pub optimization_method: OptimizationMethod,
22 pub learning_rate: f64,
24}
25
26impl Default for HybridAlgorithmConfig {
27 fn default() -> Self {
28 Self {
29 max_iterations: 1000,
30 tolerance: 1e-6,
31 optimization_method: OptimizationMethod::BFGS,
32 learning_rate: 0.01,
33 }
34 }
35}
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39pub enum OptimizationMethod {
40 BFGS,
42 NelderMead,
44 ConjugateGradient,
46 LBFGS,
48 Powell,
50}
51
52#[derive(Debug, Clone)]
54pub struct HybridOptimizationResult {
55 pub optimal_parameters: Array1<f64>,
57 pub optimal_value: f64,
59 pub iterations: usize,
61 pub converged: bool,
63}
64
65pub struct HybridAlgorithmExecutor {
67 config: HybridAlgorithmConfig,
68 rng: StdRng,
70}
71
72impl HybridAlgorithmExecutor {
73 pub fn new(config: HybridAlgorithmConfig) -> Self {
75 Self {
76 config,
77 rng: StdRng::seed_from_u64(42), }
79 }
80
81 pub fn default() -> Self {
83 Self::new(HybridAlgorithmConfig::default())
84 }
85
86 pub fn optimize<F>(
95 &mut self,
96 mut objective: F,
97 initial_params: &Array1<f64>,
98 ) -> DeviceResult<HybridOptimizationResult>
99 where
100 F: FnMut(&ArrayView1<f64>) -> f64 + Clone,
101 {
102 let method = match self.config.optimization_method {
103 OptimizationMethod::BFGS => Method::BFGS,
104 OptimizationMethod::NelderMead => Method::NelderMead,
105 OptimizationMethod::ConjugateGradient => Method::CG,
106 OptimizationMethod::LBFGS => Method::LBFGS,
107 OptimizationMethod::Powell => Method::Powell,
108 };
109
110 let options = Options {
111 max_iter: self.config.max_iterations,
112 ftol: self.config.tolerance,
113 ..Default::default()
114 };
115
116 let x0_slice: Vec<f64> = initial_params.to_vec();
117
118 let result = minimize(objective, &x0_slice, method, Some(options))
119 .map_err(|e| DeviceError::OptimizationError(format!("Optimization failed: {}", e)))?;
120
121 Ok(HybridOptimizationResult {
122 optimal_parameters: result.x,
123 optimal_value: result.fun,
124 iterations: result.nit,
125 converged: result.success,
126 })
127 }
128
129 pub fn generate_initial_parameters(
131 &mut self,
132 dimension: usize,
133 range: (f64, f64),
134 ) -> Array1<f64> {
135 let (low, high) = range;
136 Array1::from_shape_fn(dimension, |_| low + (high - low) * self.rng.gen::<f64>())
137 }
138}
139
140pub struct VQEWithSciRS2 {
142 executor: HybridAlgorithmExecutor,
143 num_parameters: usize,
145}
146
147impl VQEWithSciRS2 {
148 pub fn new(config: HybridAlgorithmConfig, num_parameters: usize) -> Self {
150 Self {
151 executor: HybridAlgorithmExecutor::new(config),
152 num_parameters,
153 }
154 }
155
156 pub fn run<F>(
162 &mut self,
163 energy_function: F,
164 initial_params: Option<&Array1<f64>>,
165 ) -> DeviceResult<HybridOptimizationResult>
166 where
167 F: FnMut(&ArrayView1<f64>) -> f64 + Clone,
168 {
169 let params = if let Some(p) = initial_params {
170 p.clone()
171 } else {
172 self.executor.generate_initial_parameters(
173 self.num_parameters,
174 (-std::f64::consts::PI, std::f64::consts::PI),
175 )
176 };
177
178 self.executor.optimize(energy_function, ¶ms)
179 }
180}
181
182pub struct QAOAWithSciRS2 {
184 executor: HybridAlgorithmExecutor,
185 num_layers: usize,
187}
188
189impl QAOAWithSciRS2 {
190 pub fn new(config: HybridAlgorithmConfig, num_layers: usize) -> Self {
192 Self {
193 executor: HybridAlgorithmExecutor::new(config),
194 num_layers,
195 }
196 }
197
198 pub fn run<F>(&mut self, cost_function: F) -> DeviceResult<HybridOptimizationResult>
203 where
204 F: FnMut(&ArrayView1<f64>) -> f64 + Clone,
205 {
206 let num_params = 2 * self.num_layers;
208 let initial_params = self
209 .executor
210 .generate_initial_parameters(num_params, (0.0, 2.0 * std::f64::consts::PI));
211
212 self.executor.optimize(cost_function, &initial_params)
213 }
214}
215
216#[cfg(test)]
217mod tests {
218 use super::*;
219
220 #[test]
221 fn test_hybrid_executor_creation() {
222 let config = HybridAlgorithmConfig::default();
223 let executor = HybridAlgorithmExecutor::new(config);
224 assert_eq!(executor.config.max_iterations, 1000);
225 }
226
227 #[test]
228 fn test_bfgs_optimization() {
229 let config = HybridAlgorithmConfig {
230 optimization_method: OptimizationMethod::BFGS,
231 max_iterations: 100,
232 tolerance: 1e-6,
233 ..Default::default()
234 };
235
236 let mut executor = HybridAlgorithmExecutor::new(config);
237
238 let objective = |x: &ArrayView1<f64>| (x[0] - 2.0).powi(2);
240
241 let initial = Array1::from(vec![0.0]);
242 let result = executor.optimize(objective, &initial);
243
244 assert!(result.is_ok());
245 let result = result.expect("Optimization failed");
246 assert!(result.converged);
247 assert!((result.optimal_parameters[0] - 2.0).abs() < 0.1);
248 }
249
250 #[test]
251 fn test_nelder_mead_optimization() {
252 let config = HybridAlgorithmConfig {
253 optimization_method: OptimizationMethod::NelderMead,
254 max_iterations: 500,
255 tolerance: 1e-6,
256 ..Default::default()
257 };
258
259 let mut executor = HybridAlgorithmExecutor::new(config);
260
261 let objective = |params: &ArrayView1<f64>| {
263 let x = params[0];
264 let y = params[1];
265 (1.0 - x).powi(2) + 100.0 * (y - x.powi(2)).powi(2)
266 };
267
268 let initial = Array1::from(vec![0.0, 0.0]);
269 let result = executor.optimize(objective, &initial);
270
271 assert!(result.is_ok());
272 let result = result.expect("Optimization failed");
273 assert!((result.optimal_parameters[0] - 1.0).abs() < 0.2);
275 assert!((result.optimal_parameters[1] - 1.0).abs() < 0.2);
276 }
277
278 #[test]
279 fn test_vqe_creation() {
280 let config = HybridAlgorithmConfig::default();
281 let vqe = VQEWithSciRS2::new(config, 4);
282 assert_eq!(vqe.num_parameters, 4);
283 }
284
285 #[test]
286 fn test_qaoa_creation() {
287 let config = HybridAlgorithmConfig::default();
288 let qaoa = QAOAWithSciRS2::new(config, 3);
289 assert_eq!(qaoa.num_layers, 3);
290 }
291
292 #[test]
293 fn test_random_parameter_generation() {
294 let config = HybridAlgorithmConfig::default();
295 let mut executor = HybridAlgorithmExecutor::new(config);
296
297 let params = executor.generate_initial_parameters(5, (-1.0, 1.0));
298 assert_eq!(params.len(), 5);
299
300 for &p in params.iter() {
302 assert!((-1.0..=1.0).contains(&p));
303 }
304 }
305
306 #[test]
307 fn test_conjugate_gradient_optimization() {
308 let config = HybridAlgorithmConfig {
309 optimization_method: OptimizationMethod::ConjugateGradient,
310 max_iterations: 100,
311 tolerance: 1e-6,
312 ..Default::default()
313 };
314
315 let mut executor = HybridAlgorithmExecutor::new(config);
316
317 let objective = |params: &ArrayView1<f64>| params[0].powi(2) + params[1].powi(2);
319
320 let initial = Array1::from(vec![5.0, 5.0]);
321 let result = executor.optimize(objective, &initial);
322
323 assert!(result.is_ok());
324 let result = result.expect("Optimization failed");
325 assert!(result.optimal_parameters[0].abs() < 0.1);
326 assert!(result.optimal_parameters[1].abs() < 0.1);
327 }
328
329 #[test]
330 fn test_vqe_run() {
331 let config = HybridAlgorithmConfig {
332 optimization_method: OptimizationMethod::BFGS,
333 max_iterations: 50,
334 ..Default::default()
335 };
336
337 let mut vqe = VQEWithSciRS2::new(config, 2);
338
339 let energy =
341 |params: &ArrayView1<f64>| (params[0] - 1.0).powi(2) + (params[1] - 2.0).powi(2);
342
343 let result = vqe.run(energy, None);
344 assert!(result.is_ok());
345
346 let result = result.expect("VQE failed");
347 assert!((result.optimal_parameters[0] - 1.0).abs() < 0.1);
348 assert!((result.optimal_parameters[1] - 2.0).abs() < 0.1);
349 }
350}