1use crate::cartan::{CartanDecomposer, CartanDecomposition};
10use crate::error::{QuantRS2Error, QuantRS2Result};
12use crate::gate::{single::*, GateOp};
13use crate::matrix_ops::{matrices_approx_equal, DenseMatrix, QuantumMatrix};
14use crate::qubit::QubitId;
15use ndarray::{Array2, ArrayView2};
16use num_complex::Complex64;
17use std::f64::consts::PI;
18
19#[derive(Debug, Clone)]
21pub struct SingleQubitDecomposition {
22 pub global_phase: f64,
24 pub theta1: f64,
26 pub phi: f64,
28 pub theta2: f64,
30 pub basis: String,
32}
33
34pub fn decompose_single_qubit_zyz(
36 unitary: &ArrayView2<Complex64>,
37) -> QuantRS2Result<SingleQubitDecomposition> {
38 if unitary.shape() != &[2, 2] {
39 return Err(QuantRS2Error::InvalidInput(
40 "Single-qubit unitary must be 2x2".to_string(),
41 ));
42 }
43
44 let matrix = DenseMatrix::new(unitary.to_owned())?;
46 if !matrix.is_unitary(1e-10)? {
47 return Err(QuantRS2Error::InvalidInput(
48 "Matrix is not unitary".to_string(),
49 ));
50 }
51
52 let a = unitary[[0, 0]];
54 let b = unitary[[0, 1]];
55 let c = unitary[[1, 0]];
56 let d = unitary[[1, 1]];
57
58 let det = a * d - b * c;
60 let global_phase = det.arg() / 2.0;
61
62 let det_sqrt = det.sqrt();
64 let a = a / det_sqrt;
65 let b = b / det_sqrt;
66 let c = c / det_sqrt;
67 let d = d / det_sqrt;
68
69 let phi = 2.0 * a.norm().acos();
73
74 let (theta1, theta2) = if phi.abs() < 1e-10 {
75 let phase = if a.norm() > 0.5 {
77 a.arg() * 2.0
78 } else {
79 d.arg() * 2.0
80 };
81 (0.0, phase)
82 } else if (phi - PI).abs() < 1e-10 {
83 (-b.arg() + c.arg() + PI, 0.0)
85 } else {
86 let theta1 = c.arg() - b.arg();
87 let theta2 = a.arg() + b.arg();
88 (theta1, theta2)
89 };
90
91 Ok(SingleQubitDecomposition {
92 global_phase,
93 theta1,
94 phi,
95 theta2,
96 basis: "ZYZ".to_string(),
97 })
98}
99
100pub fn decompose_single_qubit_xyx(
102 unitary: &ArrayView2<Complex64>,
103) -> QuantRS2Result<SingleQubitDecomposition> {
104 let h_gate = Array2::from_shape_vec(
106 (2, 2),
107 vec![
108 Complex64::new(1.0, 0.0),
109 Complex64::new(1.0, 0.0),
110 Complex64::new(1.0, 0.0),
111 Complex64::new(-1.0, 0.0),
112 ],
113 )
114 .unwrap()
115 / Complex64::new(2.0_f64.sqrt(), 0.0);
116
117 let u_transformed = h_gate.dot(unitary).dot(&h_gate);
119 let decomp = decompose_single_qubit_zyz(&u_transformed.view())?;
120
121 Ok(SingleQubitDecomposition {
122 global_phase: decomp.global_phase,
123 theta1: decomp.theta1,
124 phi: decomp.phi,
125 theta2: decomp.theta2,
126 basis: "XYX".to_string(),
127 })
128}
129
130pub fn single_qubit_gates(
132 decomp: &SingleQubitDecomposition,
133 qubit: QubitId,
134) -> Vec<Box<dyn GateOp>> {
135 let mut gates: Vec<Box<dyn GateOp>> = Vec::new();
136
137 match decomp.basis.as_str() {
138 "ZYZ" => {
139 if decomp.theta1.abs() > 1e-10 {
140 gates.push(Box::new(RotationZ {
141 target: qubit,
142 theta: decomp.theta1,
143 }));
144 }
145 if decomp.phi.abs() > 1e-10 {
146 gates.push(Box::new(RotationY {
147 target: qubit,
148 theta: decomp.phi,
149 }));
150 }
151 if decomp.theta2.abs() > 1e-10 {
152 gates.push(Box::new(RotationZ {
153 target: qubit,
154 theta: decomp.theta2,
155 }));
156 }
157 }
158 "XYX" => {
159 if decomp.theta1.abs() > 1e-10 {
160 gates.push(Box::new(RotationX {
161 target: qubit,
162 theta: decomp.theta1,
163 }));
164 }
165 if decomp.phi.abs() > 1e-10 {
166 gates.push(Box::new(RotationY {
167 target: qubit,
168 theta: decomp.phi,
169 }));
170 }
171 if decomp.theta2.abs() > 1e-10 {
172 gates.push(Box::new(RotationX {
173 target: qubit,
174 theta: decomp.theta2,
175 }));
176 }
177 }
178 _ => {} }
180
181 gates
182}
183
184pub type KAKDecomposition = CartanDecomposition;
186
187pub fn decompose_two_qubit_kak(
189 unitary: &ArrayView2<Complex64>,
190) -> QuantRS2Result<KAKDecomposition> {
191 let mut decomposer = CartanDecomposer::new();
193 let owned_unitary = unitary.to_owned();
194 decomposer.decompose(&owned_unitary)
195}
196
197pub fn kak_to_gates(
199 decomp: &KAKDecomposition,
200 qubit1: QubitId,
201 qubit2: QubitId,
202) -> QuantRS2Result<Vec<Box<dyn GateOp>>> {
203 let decomposer = CartanDecomposer::new();
205 let qubit_ids = vec![qubit1, qubit2];
206 decomposer.to_gates(decomp, &qubit_ids)
207}
208
209pub fn synthesize_unitary(
211 unitary: &ArrayView2<Complex64>,
212 qubits: &[QubitId],
213) -> QuantRS2Result<Vec<Box<dyn GateOp>>> {
214 let n = unitary.nrows();
215
216 if n != unitary.ncols() {
217 return Err(QuantRS2Error::InvalidInput(
218 "Matrix must be square".to_string(),
219 ));
220 }
221
222 let num_qubits = (n as f64).log2() as usize;
223 if (1 << num_qubits) != n {
224 return Err(QuantRS2Error::InvalidInput(
225 "Matrix dimension must be a power of 2".to_string(),
226 ));
227 }
228
229 if qubits.len() != num_qubits {
230 return Err(QuantRS2Error::InvalidInput(format!(
231 "Need {} qubits, got {}",
232 num_qubits,
233 qubits.len()
234 )));
235 }
236
237 let matrix = DenseMatrix::new(unitary.to_owned())?;
239 if !matrix.is_unitary(1e-10)? {
240 return Err(QuantRS2Error::InvalidInput(
241 "Matrix is not unitary".to_string(),
242 ));
243 }
244
245 match num_qubits {
246 1 => {
247 let decomp = decompose_single_qubit_zyz(unitary)?;
248 Ok(single_qubit_gates(&decomp, qubits[0]))
249 }
250 2 => {
251 let decomp = decompose_two_qubit_kak(unitary)?;
252 kak_to_gates(&decomp, qubits[0], qubits[1])
253 }
254 _ => {
255 Err(QuantRS2Error::UnsupportedOperation(format!(
258 "Synthesis for {}-qubit gates not yet implemented",
259 num_qubits
260 )))
261 }
262 }
263}
264
265pub fn identify_gate(unitary: &ArrayView2<Complex64>, tolerance: f64) -> Option<String> {
267 let n = unitary.nrows();
268
269 match n {
270 2 => {
271 let gates = vec![
273 ("I", Array2::eye(2)),
274 (
275 "X",
276 Array2::from_shape_vec(
277 (2, 2),
278 vec![
279 Complex64::new(0.0, 0.0),
280 Complex64::new(1.0, 0.0),
281 Complex64::new(1.0, 0.0),
282 Complex64::new(0.0, 0.0),
283 ],
284 )
285 .unwrap(),
286 ),
287 (
288 "Y",
289 Array2::from_shape_vec(
290 (2, 2),
291 vec![
292 Complex64::new(0.0, 0.0),
293 Complex64::new(0.0, -1.0),
294 Complex64::new(0.0, 1.0),
295 Complex64::new(0.0, 0.0),
296 ],
297 )
298 .unwrap(),
299 ),
300 (
301 "Z",
302 Array2::from_shape_vec(
303 (2, 2),
304 vec![
305 Complex64::new(1.0, 0.0),
306 Complex64::new(0.0, 0.0),
307 Complex64::new(0.0, 0.0),
308 Complex64::new(-1.0, 0.0),
309 ],
310 )
311 .unwrap(),
312 ),
313 (
314 "H",
315 Array2::from_shape_vec(
316 (2, 2),
317 vec![
318 Complex64::new(1.0, 0.0),
319 Complex64::new(1.0, 0.0),
320 Complex64::new(1.0, 0.0),
321 Complex64::new(-1.0, 0.0),
322 ],
323 )
324 .unwrap()
325 / Complex64::new(2.0_f64.sqrt(), 0.0),
326 ),
327 ];
328
329 for (name, gate) in gates {
330 if matrices_approx_equal(unitary, &gate.view(), tolerance) {
331 return Some(name.to_string());
332 }
333 }
334 }
335 4 => {
336 let mut cnot = Array2::eye(4);
338 cnot[[2, 2]] = Complex64::new(0.0, 0.0);
339 cnot[[2, 3]] = Complex64::new(1.0, 0.0);
340 cnot[[3, 2]] = Complex64::new(1.0, 0.0);
341 cnot[[3, 3]] = Complex64::new(0.0, 0.0);
342
343 if matrices_approx_equal(unitary, &cnot.view(), tolerance) {
344 return Some("CNOT".to_string());
345 }
346 }
347 _ => {}
348 }
349
350 None
351}
352
353#[cfg(test)]
354mod tests {
355 use super::*;
356
357 #[test]
358 #[ignore] fn test_single_qubit_decomposition() {
360 let h = Array2::from_shape_vec(
362 (2, 2),
363 vec![
364 Complex64::new(1.0, 0.0),
365 Complex64::new(1.0, 0.0),
366 Complex64::new(1.0, 0.0),
367 Complex64::new(-1.0, 0.0),
368 ],
369 )
370 .unwrap()
371 / Complex64::new(2.0_f64.sqrt(), 0.0);
372
373 let decomp = decompose_single_qubit_zyz(&h.view()).unwrap();
374
375 let rz1 = Array2::from_shape_vec(
377 (2, 2),
378 vec![
379 Complex64::new(0.0, -decomp.theta1 / 2.0).exp(),
380 Complex64::new(0.0, 0.0),
381 Complex64::new(0.0, 0.0),
382 Complex64::new(0.0, decomp.theta1 / 2.0).exp(),
383 ],
384 )
385 .unwrap();
386
387 let ry = Array2::from_shape_vec(
388 (2, 2),
389 vec![
390 Complex64::new((decomp.phi / 2.0).cos(), 0.0),
391 Complex64::new(-(decomp.phi / 2.0).sin(), 0.0),
392 Complex64::new((decomp.phi / 2.0).sin(), 0.0),
393 Complex64::new((decomp.phi / 2.0).cos(), 0.0),
394 ],
395 )
396 .unwrap();
397
398 let rz2 = Array2::from_shape_vec(
399 (2, 2),
400 vec![
401 Complex64::new(0.0, -decomp.theta2 / 2.0).exp(),
402 Complex64::new(0.0, 0.0),
403 Complex64::new(0.0, 0.0),
404 Complex64::new(0.0, decomp.theta2 / 2.0).exp(),
405 ],
406 )
407 .unwrap();
408
409 let reconstructed = Complex64::new(0.0, decomp.global_phase).exp() * rz2.dot(&ry).dot(&rz1);
411
412 assert!(matrices_approx_equal(
414 &h.view(),
415 &reconstructed.view(),
416 1e-10
417 ));
418 }
419
420 #[test]
421 fn test_gate_identification() {
422 let x = Array2::from_shape_vec(
423 (2, 2),
424 vec![
425 Complex64::new(0.0, 0.0),
426 Complex64::new(1.0, 0.0),
427 Complex64::new(1.0, 0.0),
428 Complex64::new(0.0, 0.0),
429 ],
430 )
431 .unwrap();
432
433 assert_eq!(identify_gate(&x.view(), 1e-10), Some("X".to_string()));
434 }
435}