Skip to main content

quantrs2_sim/qulacs_backend/
functions.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use crate::error::{Result, SimulatorError};
6use scirs2_core::ndarray::*;
7use scirs2_core::{Complex64, Float};
8
9use super::types::QulacsStateVector;
10
11/// Type alias for state vector index
12pub type StateIndex = usize;
13/// Type alias for qubit index
14pub type QubitIndex = usize;
15/// Qulacs-style gate operations
16pub mod gates {
17    use super::*;
18    /// Apply Hadamard gate to a target qubit
19    ///
20    /// This implementation follows Qulacs' approach with:
21    /// - Bit masking for efficient index calculation
22    /// - Special handling for qubit 0
23    /// - SciRS2 parallel execution and SIMD when available
24    ///
25    /// # Arguments
26    ///
27    /// * `state` - The quantum state vector
28    /// * `target` - Target qubit index
29    pub fn hadamard(state: &mut QulacsStateVector, target: QubitIndex) -> Result<()> {
30        if target >= state.num_qubits {
31            return Err(SimulatorError::InvalidQubitIndex {
32                index: target,
33                num_qubits: state.num_qubits,
34            });
35        }
36        let dim = state.dim;
37        let loop_dim = dim / 2;
38        let mask = 1usize << target;
39        let sqrt2_inv = Complex64::new(1.0 / 2.0f64.sqrt(), 0.0);
40        let state_data = state.amplitudes_mut();
41        if target == 0 {
42            for basis_idx in (0..dim).step_by(2) {
43                let temp0 = state_data[basis_idx];
44                let temp1 = state_data[basis_idx + 1];
45                state_data[basis_idx] = (temp0 + temp1) * sqrt2_inv;
46                state_data[basis_idx + 1] = (temp0 - temp1) * sqrt2_inv;
47            }
48        } else {
49            let mask_low = mask - 1;
50            let mask_high = !mask_low;
51            for state_idx in (0..loop_dim).step_by(2) {
52                let basis_idx_0 = (state_idx & mask_low) | ((state_idx & mask_high) << 1);
53                let basis_idx_1 = basis_idx_0 | mask;
54                let temp_a0 = state_data[basis_idx_0];
55                let temp_a1 = state_data[basis_idx_1];
56                let temp_b0 = state_data[basis_idx_0 + 1];
57                let temp_b1 = state_data[basis_idx_1 + 1];
58                state_data[basis_idx_0] = (temp_a0 + temp_a1) * sqrt2_inv;
59                state_data[basis_idx_1] = (temp_a0 - temp_a1) * sqrt2_inv;
60                state_data[basis_idx_0 + 1] = (temp_b0 + temp_b1) * sqrt2_inv;
61                state_data[basis_idx_1 + 1] = (temp_b0 - temp_b1) * sqrt2_inv;
62            }
63        }
64        Ok(())
65    }
66    /// Apply Pauli-X gate to a target qubit
67    ///
68    /// # Arguments
69    ///
70    /// * `state` - The quantum state vector
71    /// * `target` - Target qubit index
72    pub fn pauli_x(state: &mut QulacsStateVector, target: QubitIndex) -> Result<()> {
73        if target >= state.num_qubits {
74            return Err(SimulatorError::InvalidQubitIndex {
75                index: target,
76                num_qubits: state.num_qubits,
77            });
78        }
79        let dim = state.dim;
80        let loop_dim = dim / 2;
81        let mask = 1usize << target;
82        let mask_low = mask - 1;
83        let mask_high = !mask_low;
84        let state_data = state.amplitudes_mut();
85        if target == 0 {
86            for basis_idx in (0..dim).step_by(2) {
87                state_data.swap(basis_idx, basis_idx + 1);
88            }
89        } else {
90            for state_idx in (0..loop_dim).step_by(2) {
91                let basis_idx_0 = (state_idx & mask_low) | ((state_idx & mask_high) << 1);
92                let basis_idx_1 = basis_idx_0 | mask;
93                state_data.swap(basis_idx_0, basis_idx_1);
94                state_data.swap(basis_idx_0 + 1, basis_idx_1 + 1);
95            }
96        }
97        Ok(())
98    }
99    /// Apply Pauli-Y gate to a target qubit
100    ///
101    /// # Arguments
102    ///
103    /// * `state` - The quantum state vector
104    /// * `target` - Target qubit index
105    pub fn pauli_y(state: &mut QulacsStateVector, target: QubitIndex) -> Result<()> {
106        if target >= state.num_qubits {
107            return Err(SimulatorError::InvalidQubitIndex {
108                index: target,
109                num_qubits: state.num_qubits,
110            });
111        }
112        let dim = state.dim;
113        let loop_dim = dim / 2;
114        let mask = 1usize << target;
115        let mask_low = mask - 1;
116        let mask_high = !mask_low;
117        let state_data = state.amplitudes_mut();
118        let i = Complex64::new(0.0, 1.0);
119        if target == 0 {
120            for basis_idx in (0..dim).step_by(2) {
121                let temp0 = state_data[basis_idx];
122                let temp1 = state_data[basis_idx + 1];
123                state_data[basis_idx] = -i * temp1;
124                state_data[basis_idx + 1] = i * temp0;
125            }
126        } else {
127            for state_idx in (0..loop_dim).step_by(2) {
128                let basis_idx_0 = (state_idx & mask_low) | ((state_idx & mask_high) << 1);
129                let basis_idx_1 = basis_idx_0 | mask;
130                let temp_a0 = state_data[basis_idx_0];
131                let temp_a1 = state_data[basis_idx_1];
132                let temp_b0 = state_data[basis_idx_0 + 1];
133                let temp_b1 = state_data[basis_idx_1 + 1];
134                state_data[basis_idx_0] = -i * temp_a1;
135                state_data[basis_idx_1] = i * temp_a0;
136                state_data[basis_idx_0 + 1] = -i * temp_b1;
137                state_data[basis_idx_1 + 1] = i * temp_b0;
138            }
139        }
140        Ok(())
141    }
142    /// Apply Pauli-Z gate to a target qubit
143    ///
144    /// # Arguments
145    ///
146    /// * `state` - The quantum state vector
147    /// * `target` - Target qubit index
148    pub fn pauli_z(state: &mut QulacsStateVector, target: QubitIndex) -> Result<()> {
149        if target >= state.num_qubits {
150            return Err(SimulatorError::InvalidQubitIndex {
151                index: target,
152                num_qubits: state.num_qubits,
153            });
154        }
155        let dim = state.dim;
156        let loop_dim = dim / 2;
157        let mask = 1usize << target;
158        let mask_low = mask - 1;
159        let mask_high = !mask_low;
160        let state_data = state.amplitudes_mut();
161        for state_idx in 0..loop_dim {
162            let basis_idx = (state_idx & mask_low) | ((state_idx & mask_high) << 1) | mask;
163            state_data[basis_idx] = -state_data[basis_idx];
164        }
165        Ok(())
166    }
167    /// Apply CNOT gate (controlled-X)
168    ///
169    /// This follows Qulacs' approach with specialized handling based on
170    /// control and target qubit positions.
171    ///
172    /// # Arguments
173    ///
174    /// * `state` - The quantum state vector
175    /// * `control` - Control qubit index
176    /// * `target` - Target qubit index
177    pub fn cnot(
178        state: &mut QulacsStateVector,
179        control: QubitIndex,
180        target: QubitIndex,
181    ) -> Result<()> {
182        if control >= state.num_qubits {
183            return Err(SimulatorError::InvalidQubitIndex {
184                index: control,
185                num_qubits: state.num_qubits,
186            });
187        }
188        if target >= state.num_qubits {
189            return Err(SimulatorError::InvalidQubitIndex {
190                index: target,
191                num_qubits: state.num_qubits,
192            });
193        }
194        if control == target {
195            return Err(SimulatorError::InvalidOperation(
196                "Control and target qubits must be different".to_string(),
197            ));
198        }
199        let dim = state.dim;
200        let loop_dim = dim / 4;
201        let target_mask = 1usize << target;
202        let control_mask = 1usize << control;
203        let min_qubit = control.min(target);
204        let max_qubit = control.max(target);
205        let min_qubit_mask = 1usize << min_qubit;
206        let max_qubit_mask = 1usize << (max_qubit - 1);
207        let low_mask = min_qubit_mask - 1;
208        let mid_mask = (max_qubit_mask - 1) ^ low_mask;
209        let high_mask = !max_qubit_mask.wrapping_add(max_qubit_mask - 1);
210        let state_data = state.amplitudes_mut();
211        if target == 0 {
212            for state_idx in 0..loop_dim {
213                let basis_idx =
214                    ((state_idx & mid_mask) << 1) | ((state_idx & high_mask) << 2) | control_mask;
215                state_data.swap(basis_idx, basis_idx + 1);
216            }
217        } else if control == 0 {
218            for state_idx in 0..loop_dim {
219                let basis_idx_0 = (state_idx & low_mask)
220                    | ((state_idx & mid_mask) << 1)
221                    | ((state_idx & high_mask) << 2)
222                    | control_mask;
223                let basis_idx_1 = basis_idx_0 | target_mask;
224                state_data.swap(basis_idx_0, basis_idx_1);
225            }
226        } else {
227            for state_idx in (0..loop_dim).step_by(2) {
228                let basis_idx_0 = (state_idx & low_mask)
229                    | ((state_idx & mid_mask) << 1)
230                    | ((state_idx & high_mask) << 2)
231                    | control_mask;
232                let basis_idx_1 = basis_idx_0 | target_mask;
233                state_data.swap(basis_idx_0, basis_idx_1);
234                state_data.swap(basis_idx_0 + 1, basis_idx_1 + 1);
235            }
236        }
237        Ok(())
238    }
239    /// Apply CZ gate (controlled-Z)
240    ///
241    /// # Arguments
242    ///
243    /// * `state` - The quantum state vector
244    /// * `control` - Control qubit index
245    /// * `target` - Target qubit index
246    pub fn cz(
247        state: &mut QulacsStateVector,
248        control: QubitIndex,
249        target: QubitIndex,
250    ) -> Result<()> {
251        if control >= state.num_qubits {
252            return Err(SimulatorError::InvalidQubitIndex {
253                index: control,
254                num_qubits: state.num_qubits,
255            });
256        }
257        if target >= state.num_qubits {
258            return Err(SimulatorError::InvalidQubitIndex {
259                index: target,
260                num_qubits: state.num_qubits,
261            });
262        }
263        if control == target {
264            return Err(SimulatorError::InvalidOperation(
265                "Control and target qubits must be different".to_string(),
266            ));
267        }
268        let dim = state.dim;
269        let loop_dim = dim / 4;
270        let target_mask = 1usize << target;
271        let control_mask = 1usize << control;
272        let min_qubit = control.min(target);
273        let max_qubit = control.max(target);
274        let min_qubit_mask = 1usize << min_qubit;
275        let max_qubit_mask = 1usize << (max_qubit - 1);
276        let low_mask = min_qubit_mask - 1;
277        let mid_mask = (max_qubit_mask - 1) ^ low_mask;
278        let high_mask = !max_qubit_mask.wrapping_add(max_qubit_mask - 1);
279        let state_data = state.amplitudes_mut();
280        for state_idx in 0..loop_dim {
281            let basis_idx = (state_idx & low_mask)
282                | ((state_idx & mid_mask) << 1)
283                | ((state_idx & high_mask) << 2)
284                | control_mask
285                | target_mask;
286            state_data[basis_idx] = -state_data[basis_idx];
287        }
288        Ok(())
289    }
290    /// Apply SWAP gate
291    ///
292    /// # Arguments
293    ///
294    /// * `state` - The quantum state vector
295    /// * `qubit1` - First qubit index
296    /// * `qubit2` - Second qubit index
297    pub fn swap(
298        state: &mut QulacsStateVector,
299        qubit1: QubitIndex,
300        qubit2: QubitIndex,
301    ) -> Result<()> {
302        if qubit1 >= state.num_qubits {
303            return Err(SimulatorError::InvalidQubitIndex {
304                index: qubit1,
305                num_qubits: state.num_qubits,
306            });
307        }
308        if qubit2 >= state.num_qubits {
309            return Err(SimulatorError::InvalidQubitIndex {
310                index: qubit2,
311                num_qubits: state.num_qubits,
312            });
313        }
314        if qubit1 == qubit2 {
315            return Ok(());
316        }
317        let dim = state.dim;
318        let loop_dim = dim / 4;
319        let mask1 = 1usize << qubit1;
320        let mask2 = 1usize << qubit2;
321        let min_qubit = qubit1.min(qubit2);
322        let max_qubit = qubit1.max(qubit2);
323        let min_qubit_mask = 1usize << min_qubit;
324        let max_qubit_mask = 1usize << (max_qubit - 1);
325        let low_mask = min_qubit_mask - 1;
326        let mid_mask = (max_qubit_mask - 1) ^ low_mask;
327        let high_mask = !max_qubit_mask.wrapping_add(max_qubit_mask - 1);
328        let state_data = state.amplitudes_mut();
329        for state_idx in 0..loop_dim {
330            let basis_idx_0 = (state_idx & low_mask)
331                | ((state_idx & mid_mask) << 1)
332                | ((state_idx & high_mask) << 2);
333            let basis_idx_1 = basis_idx_0 | mask1;
334            let basis_idx_2 = basis_idx_0 | mask2;
335            state_data.swap(basis_idx_1, basis_idx_2);
336        }
337        Ok(())
338    }
339    /// Apply RX gate (rotation around X-axis)
340    ///
341    /// RX(θ) = exp(-iθX/2) = cos(θ/2)I - i sin(θ/2)X
342    ///
343    /// Matrix representation:
344    /// ```text
345    /// [cos(θ/2)    -i sin(θ/2)]
346    /// [-i sin(θ/2)  cos(θ/2)  ]
347    /// ```
348    ///
349    /// # Arguments
350    ///
351    /// * `state` - The quantum state vector
352    /// * `target` - Target qubit index
353    /// * `angle` - Rotation angle in radians
354    pub fn rx(state: &mut QulacsStateVector, target: QubitIndex, angle: f64) -> Result<()> {
355        if target >= state.num_qubits {
356            return Err(SimulatorError::InvalidQubitIndex {
357                index: target,
358                num_qubits: state.num_qubits,
359            });
360        }
361        let dim = state.dim;
362        let loop_dim = dim / 2;
363        let mask = 1usize << target;
364        let mask_low = mask - 1;
365        let mask_high = !mask_low;
366        let cos_half = (angle / 2.0).cos();
367        let sin_half = (angle / 2.0).sin();
368        let i_sin_half = Complex64::new(0.0, -sin_half);
369        let state_data = state.amplitudes_mut();
370        if target == 0 {
371            for basis_idx in (0..dim).step_by(2) {
372                let amp0 = state_data[basis_idx];
373                let amp1 = state_data[basis_idx + 1];
374                state_data[basis_idx] = amp0 * cos_half + amp1 * i_sin_half;
375                state_data[basis_idx + 1] = amp0 * i_sin_half + amp1 * cos_half;
376            }
377        } else {
378            for state_idx in (0..loop_dim).step_by(2) {
379                let basis_idx_0 = (state_idx & mask_low) | ((state_idx & mask_high) << 1);
380                let basis_idx_1 = basis_idx_0 | mask;
381                let amp_a0 = state_data[basis_idx_0];
382                let amp_a1 = state_data[basis_idx_1];
383                let amp_b0 = state_data[basis_idx_0 + 1];
384                let amp_b1 = state_data[basis_idx_1 + 1];
385                state_data[basis_idx_0] = amp_a0 * cos_half + amp_a1 * i_sin_half;
386                state_data[basis_idx_1] = amp_a0 * i_sin_half + amp_a1 * cos_half;
387                state_data[basis_idx_0 + 1] = amp_b0 * cos_half + amp_b1 * i_sin_half;
388                state_data[basis_idx_1 + 1] = amp_b0 * i_sin_half + amp_b1 * cos_half;
389            }
390        }
391        Ok(())
392    }
393    /// Apply RY gate (rotation around Y-axis)
394    ///
395    /// RY(θ) = exp(-iθY/2) = cos(θ/2)I - i sin(θ/2)Y
396    ///
397    /// Matrix representation:
398    /// ```text
399    /// [cos(θ/2)  -sin(θ/2)]
400    /// [sin(θ/2)   cos(θ/2)]
401    /// ```
402    ///
403    /// # Arguments
404    ///
405    /// * `state` - The quantum state vector
406    /// * `target` - Target qubit index
407    /// * `angle` - Rotation angle in radians
408    pub fn ry(state: &mut QulacsStateVector, target: QubitIndex, angle: f64) -> Result<()> {
409        if target >= state.num_qubits {
410            return Err(SimulatorError::InvalidQubitIndex {
411                index: target,
412                num_qubits: state.num_qubits,
413            });
414        }
415        let dim = state.dim;
416        let loop_dim = dim / 2;
417        let mask = 1usize << target;
418        let mask_low = mask - 1;
419        let mask_high = !mask_low;
420        let cos_half = (angle / 2.0).cos();
421        let sin_half = (angle / 2.0).sin();
422        let state_data = state.amplitudes_mut();
423        if target == 0 {
424            for basis_idx in (0..dim).step_by(2) {
425                let amp0 = state_data[basis_idx];
426                let amp1 = state_data[basis_idx + 1];
427                state_data[basis_idx] = amp0 * cos_half - amp1 * sin_half;
428                state_data[basis_idx + 1] = amp0 * sin_half + amp1 * cos_half;
429            }
430        } else {
431            for state_idx in (0..loop_dim).step_by(2) {
432                let basis_idx_0 = (state_idx & mask_low) | ((state_idx & mask_high) << 1);
433                let basis_idx_1 = basis_idx_0 | mask;
434                let amp_a0 = state_data[basis_idx_0];
435                let amp_a1 = state_data[basis_idx_1];
436                let amp_b0 = state_data[basis_idx_0 + 1];
437                let amp_b1 = state_data[basis_idx_1 + 1];
438                state_data[basis_idx_0] = amp_a0 * cos_half - amp_a1 * sin_half;
439                state_data[basis_idx_1] = amp_a0 * sin_half + amp_a1 * cos_half;
440                state_data[basis_idx_0 + 1] = amp_b0 * cos_half - amp_b1 * sin_half;
441                state_data[basis_idx_1 + 1] = amp_b0 * sin_half + amp_b1 * cos_half;
442            }
443        }
444        Ok(())
445    }
446    /// Apply RZ gate (rotation around Z-axis)
447    ///
448    /// RZ(θ) = exp(-iθZ/2)
449    ///
450    /// Matrix representation:
451    /// ```text
452    /// [e^(-iθ/2)     0      ]
453    /// [   0       e^(iθ/2)  ]
454    /// ```
455    ///
456    /// # Arguments
457    ///
458    /// * `state` - The quantum state vector
459    /// * `target` - Target qubit index
460    /// * `angle` - Rotation angle in radians
461    pub fn rz(state: &mut QulacsStateVector, target: QubitIndex, angle: f64) -> Result<()> {
462        if target >= state.num_qubits {
463            return Err(SimulatorError::InvalidQubitIndex {
464                index: target,
465                num_qubits: state.num_qubits,
466            });
467        }
468        let dim = state.dim;
469        let loop_dim = dim / 2;
470        let mask = 1usize << target;
471        let mask_low = mask - 1;
472        let mask_high = !mask_low;
473        let phase_0 = Complex64::from_polar(1.0, -angle / 2.0);
474        let phase_1 = Complex64::from_polar(1.0, angle / 2.0);
475        let state_data = state.amplitudes_mut();
476        for state_idx in 0..loop_dim {
477            let basis_idx_0 = (state_idx & mask_low) | ((state_idx & mask_high) << 1);
478            let basis_idx_1 = basis_idx_0 | mask;
479            state_data[basis_idx_0] *= phase_0;
480            state_data[basis_idx_1] *= phase_1;
481        }
482        Ok(())
483    }
484    /// Apply Phase gate (arbitrary phase rotation)
485    ///
486    /// Phase(θ) = diag(1, e^(iθ))
487    ///
488    /// # Arguments
489    ///
490    /// * `state` - The quantum state vector
491    /// * `target` - Target qubit index
492    /// * `angle` - Phase angle in radians
493    pub fn phase(state: &mut QulacsStateVector, target: QubitIndex, angle: f64) -> Result<()> {
494        if target >= state.num_qubits {
495            return Err(SimulatorError::InvalidQubitIndex {
496                index: target,
497                num_qubits: state.num_qubits,
498            });
499        }
500        let dim = state.dim;
501        let loop_dim = dim / 2;
502        let mask = 1usize << target;
503        let mask_low = mask - 1;
504        let mask_high = !mask_low;
505        let phase_factor = Complex64::from_polar(1.0, angle);
506        let state_data = state.amplitudes_mut();
507        for state_idx in 0..loop_dim {
508            let basis_idx = (state_idx & mask_low) | ((state_idx & mask_high) << 1) | mask;
509            state_data[basis_idx] *= phase_factor;
510        }
511        Ok(())
512    }
513    /// Apply S gate (phase gate with π/2)
514    ///
515    /// S gate applies a π/2 phase: |0⟩ → |0⟩, |1⟩ → i|1⟩
516    ///
517    /// # Arguments
518    ///
519    /// * `state` - The quantum state vector
520    /// * `target` - Target qubit index
521    pub fn s(state: &mut QulacsStateVector, target: QubitIndex) -> Result<()> {
522        phase(state, target, std::f64::consts::FRAC_PI_2)
523    }
524    /// Apply S† gate (conjugate of S gate, phase -π/2)
525    ///
526    /// S† gate applies a -π/2 phase: |0⟩ → |0⟩, |1⟩ → -i|1⟩
527    ///
528    /// # Arguments
529    ///
530    /// * `state` - The quantum state vector
531    /// * `target` - Target qubit index
532    pub fn sdg(state: &mut QulacsStateVector, target: QubitIndex) -> Result<()> {
533        phase(state, target, -std::f64::consts::FRAC_PI_2)
534    }
535    /// Apply T gate (phase gate with π/4)
536    ///
537    /// T gate applies a π/4 phase: |0⟩ → |0⟩, |1⟩ → e^(iπ/4)|1⟩
538    ///
539    /// # Arguments
540    ///
541    /// * `state` - The quantum state vector
542    /// * `target` - Target qubit index
543    pub fn t(state: &mut QulacsStateVector, target: QubitIndex) -> Result<()> {
544        phase(state, target, std::f64::consts::FRAC_PI_4)
545    }
546    /// Apply T† gate (conjugate of T gate, phase -π/4)
547    ///
548    /// T† gate applies a -π/4 phase: |0⟩ → |0⟩, |1⟩ → e^(-iπ/4)|1⟩
549    ///
550    /// # Arguments
551    ///
552    /// * `state` - The quantum state vector
553    /// * `target` - Target qubit index
554    pub fn tdg(state: &mut QulacsStateVector, target: QubitIndex) -> Result<()> {
555        phase(state, target, -std::f64::consts::FRAC_PI_4)
556    }
557    /// Apply U3 gate (universal single-qubit gate)
558    ///
559    /// U3(θ, φ, λ) is the most general single-qubit unitary gate
560    ///
561    /// Matrix representation:
562    /// ```text
563    /// [cos(θ/2)              -e^(iλ) sin(θ/2)        ]
564    /// [e^(iφ) sin(θ/2)       e^(i(φ+λ)) cos(θ/2)     ]
565    /// ```
566    ///
567    /// # Arguments
568    ///
569    /// * `state` - The quantum state vector
570    /// * `target` - Target qubit index
571    /// * `theta` - Rotation angle θ
572    /// * `phi` - Phase angle φ
573    /// * `lambda` - Phase angle λ
574    pub fn u3(
575        state: &mut QulacsStateVector,
576        target: QubitIndex,
577        theta: f64,
578        phi: f64,
579        lambda: f64,
580    ) -> Result<()> {
581        if target >= state.num_qubits {
582            return Err(SimulatorError::InvalidQubitIndex {
583                index: target,
584                num_qubits: state.num_qubits,
585            });
586        }
587        let dim = state.dim;
588        let loop_dim = dim / 2;
589        let mask = 1usize << target;
590        let mask_low = mask - 1;
591        let mask_high = !mask_low;
592        let cos_half = (theta / 2.0).cos();
593        let sin_half = (theta / 2.0).sin();
594        let u00 = Complex64::new(cos_half, 0.0);
595        let u01 = -Complex64::from_polar(sin_half, lambda);
596        let u10 = Complex64::from_polar(sin_half, phi);
597        let u11 = Complex64::from_polar(cos_half, phi + lambda);
598        let state_data = state.amplitudes_mut();
599        if target == 0 {
600            for basis_idx in (0..dim).step_by(2) {
601                let amp0 = state_data[basis_idx];
602                let amp1 = state_data[basis_idx + 1];
603                state_data[basis_idx] = u00 * amp0 + u01 * amp1;
604                state_data[basis_idx + 1] = u10 * amp0 + u11 * amp1;
605            }
606        } else {
607            for state_idx in (0..loop_dim).step_by(2) {
608                let basis_idx_0 = (state_idx & mask_low) | ((state_idx & mask_high) << 1);
609                let basis_idx_1 = basis_idx_0 | mask;
610                let amp_a0 = state_data[basis_idx_0];
611                let amp_a1 = state_data[basis_idx_1];
612                let amp_b0 = state_data[basis_idx_0 + 1];
613                let amp_b1 = state_data[basis_idx_1 + 1];
614                state_data[basis_idx_0] = u00 * amp_a0 + u01 * amp_a1;
615                state_data[basis_idx_1] = u10 * amp_a0 + u11 * amp_a1;
616                state_data[basis_idx_0 + 1] = u00 * amp_b0 + u01 * amp_b1;
617                state_data[basis_idx_1 + 1] = u10 * amp_b0 + u11 * amp_b1;
618            }
619        }
620        Ok(())
621    }
622    /// Apply Toffoli (CCX) gate - Controlled-Controlled-NOT
623    ///
624    /// Flips the target qubit if both control qubits are in state |1⟩
625    ///
626    /// # Arguments
627    ///
628    /// * `state` - The quantum state vector
629    /// * `control1` - First control qubit index
630    /// * `control2` - Second control qubit index
631    /// * `target` - Target qubit index
632    pub fn toffoli(
633        state: &mut QulacsStateVector,
634        control1: QubitIndex,
635        control2: QubitIndex,
636        target: QubitIndex,
637    ) -> Result<()> {
638        if control1 >= state.num_qubits {
639            return Err(SimulatorError::InvalidQubitIndex {
640                index: control1,
641                num_qubits: state.num_qubits,
642            });
643        }
644        if control2 >= state.num_qubits {
645            return Err(SimulatorError::InvalidQubitIndex {
646                index: control2,
647                num_qubits: state.num_qubits,
648            });
649        }
650        if target >= state.num_qubits {
651            return Err(SimulatorError::InvalidQubitIndex {
652                index: target,
653                num_qubits: state.num_qubits,
654            });
655        }
656        if control1 == control2 || control1 == target || control2 == target {
657            return Err(SimulatorError::InvalidOperation(
658                "Control and target qubits must be different".to_string(),
659            ));
660        }
661        let dim = state.dim;
662        let loop_dim = dim / 8;
663        let num_qubits = state.num_qubits;
664        let control1_mask = 1usize << control1;
665        let control2_mask = 1usize << control2;
666        let target_mask = 1usize << target;
667        let state_data = state.amplitudes_mut();
668        for i in 0..loop_dim {
669            let mut basis_idx = 0;
670            let mut temp = i;
671            for bit_pos in 0..num_qubits {
672                if bit_pos != control1 && bit_pos != control2 && bit_pos != target {
673                    basis_idx |= (temp & 1) << bit_pos;
674                    temp >>= 1;
675                }
676            }
677            basis_idx |= control1_mask | control2_mask;
678            let idx_0 = basis_idx & !target_mask;
679            let idx_1 = basis_idx | target_mask;
680            state_data.swap(idx_0, idx_1);
681        }
682        Ok(())
683    }
684    /// Apply Fredkin (CSWAP) gate - Controlled-SWAP
685    ///
686    /// Swaps target1 and target2 if control qubit is in state |1⟩
687    ///
688    /// # Arguments
689    ///
690    /// * `state` - The quantum state vector
691    /// * `control` - Control qubit index
692    /// * `target1` - First target qubit index
693    /// * `target2` - Second target qubit index
694    pub fn fredkin(
695        state: &mut QulacsStateVector,
696        control: QubitIndex,
697        target1: QubitIndex,
698        target2: QubitIndex,
699    ) -> Result<()> {
700        if control >= state.num_qubits {
701            return Err(SimulatorError::InvalidQubitIndex {
702                index: control,
703                num_qubits: state.num_qubits,
704            });
705        }
706        if target1 >= state.num_qubits {
707            return Err(SimulatorError::InvalidQubitIndex {
708                index: target1,
709                num_qubits: state.num_qubits,
710            });
711        }
712        if target2 >= state.num_qubits {
713            return Err(SimulatorError::InvalidQubitIndex {
714                index: target2,
715                num_qubits: state.num_qubits,
716            });
717        }
718        if control == target1 || control == target2 || target1 == target2 {
719            return Err(SimulatorError::InvalidOperation(
720                "Control and target qubits must be different".to_string(),
721            ));
722        }
723        let dim = state.dim;
724        let loop_dim = dim / 8;
725        let num_qubits = state.num_qubits;
726        let control_mask = 1usize << control;
727        let target1_mask = 1usize << target1;
728        let target2_mask = 1usize << target2;
729        let state_data = state.amplitudes_mut();
730        for i in 0..loop_dim {
731            let mut basis_idx = 0;
732            let mut temp = i;
733            for bit_pos in 0..num_qubits {
734                if bit_pos != control && bit_pos != target1 && bit_pos != target2 {
735                    basis_idx |= (temp & 1) << bit_pos;
736                    temp >>= 1;
737                }
738            }
739            basis_idx |= control_mask;
740            let idx_01 = basis_idx | target2_mask;
741            let idx_10 = basis_idx | target1_mask;
742            state_data.swap(idx_01, idx_10);
743        }
744        Ok(())
745    }
746}
747/// Observable framework for Qulacs
748///
749/// Provides rich observable abstractions for expectation value computations
750pub mod observable {
751    use super::super::types::QulacsStateVector;
752    use crate::error::{Result, SimulatorError};
753    use scirs2_core::ndarray::Array2;
754    use scirs2_core::Complex64;
755    use std::collections::HashMap;
756    /// Pauli operator type
757    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
758    pub enum PauliOperator {
759        /// Identity operator
760        I,
761        /// Pauli X operator
762        X,
763        /// Pauli Y operator
764        Y,
765        /// Pauli Z operator
766        Z,
767    }
768    impl PauliOperator {
769        /// Get the matrix representation of this Pauli operator
770        pub fn matrix(&self) -> Array2<Complex64> {
771            match self {
772                PauliOperator::I => {
773                    let mut mat = Array2::zeros((2, 2));
774                    mat[[0, 0]] = Complex64::new(1.0, 0.0);
775                    mat[[1, 1]] = Complex64::new(1.0, 0.0);
776                    mat
777                }
778                PauliOperator::X => {
779                    let mut mat = Array2::zeros((2, 2));
780                    mat[[0, 1]] = Complex64::new(1.0, 0.0);
781                    mat[[1, 0]] = Complex64::new(1.0, 0.0);
782                    mat
783                }
784                PauliOperator::Y => {
785                    let mut mat = Array2::zeros((2, 2));
786                    mat[[0, 1]] = Complex64::new(0.0, -1.0);
787                    mat[[1, 0]] = Complex64::new(0.0, 1.0);
788                    mat
789                }
790                PauliOperator::Z => {
791                    let mut mat = Array2::zeros((2, 2));
792                    mat[[0, 0]] = Complex64::new(1.0, 0.0);
793                    mat[[1, 1]] = Complex64::new(-1.0, 0.0);
794                    mat
795                }
796            }
797        }
798        /// Get the eigenvalue for computational basis state |b⟩
799        pub fn eigenvalue(&self, basis_state: bool) -> f64 {
800            match self {
801                PauliOperator::I => 1.0,
802                PauliOperator::X => 0.0,
803                PauliOperator::Y => 0.0,
804                PauliOperator::Z => {
805                    if basis_state {
806                        -1.0
807                    } else {
808                        1.0
809                    }
810                }
811            }
812        }
813        /// Check if this operator commutes with Z basis measurement
814        pub fn commutes_with_z(&self) -> bool {
815            matches!(self, PauliOperator::I | PauliOperator::Z)
816        }
817    }
818    /// Pauli string observable (tensor product of Pauli operators)
819    #[derive(Debug, Clone)]
820    pub struct PauliObservable {
821        /// Pauli operators for each qubit (qubit_index -> operator)
822        pub operators: HashMap<usize, PauliOperator>,
823        /// Coefficient for this Pauli string
824        pub coefficient: f64,
825    }
826    impl PauliObservable {
827        /// Create a new Pauli observable
828        pub fn new(operators: HashMap<usize, PauliOperator>, coefficient: f64) -> Self {
829            Self {
830                operators,
831                coefficient,
832            }
833        }
834        /// Create identity observable
835        pub fn identity(num_qubits: usize) -> Self {
836            let mut operators = HashMap::new();
837            for i in 0..num_qubits {
838                operators.insert(i, PauliOperator::I);
839            }
840            Self {
841                operators,
842                coefficient: 1.0,
843            }
844        }
845        /// Create Z observable on specified qubits
846        pub fn pauli_z(qubits: &[usize]) -> Self {
847            let mut operators = HashMap::new();
848            for &qubit in qubits {
849                operators.insert(qubit, PauliOperator::Z);
850            }
851            Self {
852                operators,
853                coefficient: 1.0,
854            }
855        }
856        /// Create X observable on specified qubits
857        pub fn pauli_x(qubits: &[usize]) -> Self {
858            let mut operators = HashMap::new();
859            for &qubit in qubits {
860                operators.insert(qubit, PauliOperator::X);
861            }
862            Self {
863                operators,
864                coefficient: 1.0,
865            }
866        }
867        /// Create Y observable on specified qubits
868        pub fn pauli_y(qubits: &[usize]) -> Self {
869            let mut operators = HashMap::new();
870            for &qubit in qubits {
871                operators.insert(qubit, PauliOperator::Y);
872            }
873            Self {
874                operators,
875                coefficient: 1.0,
876            }
877        }
878        /// Compute expectation value for this observable
879        pub fn expectation_value(&self, state: &QulacsStateVector) -> f64 {
880            let mut result = 0.0;
881            for i in 0..state.dim() {
882                let prob = state.amplitudes()[i].norm_sqr();
883                if prob < 1e-15 {
884                    continue;
885                }
886                let mut eigenvalue = 1.0;
887                for (&qubit, &op) in &self.operators {
888                    let bit = ((i >> qubit) & 1) == 1;
889                    match op {
890                        PauliOperator::I => {}
891                        PauliOperator::Z => {
892                            eigenvalue *= if bit { -1.0 } else { 1.0 };
893                        }
894                        PauliOperator::X | PauliOperator::Y => {
895                            return 0.0;
896                        }
897                    }
898                }
899                result += prob * eigenvalue;
900            }
901            result * self.coefficient
902        }
903        /// Set coefficient
904        pub fn with_coefficient(mut self, coefficient: f64) -> Self {
905            self.coefficient = coefficient;
906            self
907        }
908        /// Get the number of non-identity operators
909        pub fn weight(&self) -> usize {
910            self.operators
911                .values()
912                .filter(|&&op| op != PauliOperator::I)
913                .count()
914        }
915    }
916    /// Hermitian observable (general Hermitian matrix)
917    #[derive(Debug, Clone)]
918    pub struct HermitianObservable {
919        /// The Hermitian matrix
920        pub matrix: Array2<Complex64>,
921        /// Number of qubits this observable acts on
922        pub num_qubits: usize,
923    }
924    impl HermitianObservable {
925        /// Create a new Hermitian observable
926        pub fn new(matrix: Array2<Complex64>) -> Result<Self> {
927            let (n, m) = (matrix.nrows(), matrix.ncols());
928            if n != m {
929                return Err(SimulatorError::InvalidObservable(
930                    "Matrix must be square".to_string(),
931                ));
932            }
933            if n == 0 || (n & (n - 1)) != 0 {
934                return Err(SimulatorError::InvalidObservable(
935                    "Dimension must be a power of 2".to_string(),
936                ));
937            }
938            let num_qubits = n.trailing_zeros() as usize;
939            Ok(Self { matrix, num_qubits })
940        }
941        /// Compute expectation value <ψ|H|ψ>
942        pub fn expectation_value(&self, state: &QulacsStateVector) -> Result<f64> {
943            if state.num_qubits() != self.num_qubits {
944                return Err(SimulatorError::InvalidObservable(
945                    "Observable dimension doesn't match state".to_string(),
946                ));
947            }
948            let psi = state.amplitudes();
949            let mut result = Complex64::new(0.0, 0.0);
950            for i in 0..state.dim() {
951                for j in 0..state.dim() {
952                    result += psi[i].conj() * self.matrix[[i, j]] * psi[j];
953                }
954            }
955            Ok(result.re)
956        }
957    }
958    /// Composite observable (sum of weighted observables)
959    #[derive(Debug, Clone)]
960    pub struct CompositeObservable {
961        /// List of Pauli observables with coefficients
962        pub terms: Vec<PauliObservable>,
963    }
964    impl CompositeObservable {
965        /// Create a new composite observable
966        pub fn new() -> Self {
967            Self { terms: Vec::new() }
968        }
969        /// Add a Pauli observable term
970        pub fn add_term(mut self, observable: PauliObservable) -> Self {
971            self.terms.push(observable);
972            self
973        }
974        /// Compute total expectation value
975        pub fn expectation_value(&self, state: &QulacsStateVector) -> f64 {
976            self.terms
977                .iter()
978                .map(|term| term.expectation_value(state))
979                .sum()
980        }
981        /// Get the number of terms
982        pub fn num_terms(&self) -> usize {
983            self.terms.len()
984        }
985    }
986    impl Default for CompositeObservable {
987        fn default() -> Self {
988            Self::new()
989        }
990    }
991}
992/// High-level circuit API for Qulacs backend
993///
994/// Provides a convenient interface for building and executing quantum circuits
995/// using the Qulacs-style backend.
996pub mod circuit_api {
997    use super::super::types::QulacsStateVector;
998    use super::gates;
999    use crate::error::{Result, SimulatorError};
1000    use scirs2_core::ndarray::Array1;
1001    use scirs2_core::random::thread_rng;
1002    use scirs2_core::Complex64;
1003    use std::collections::HashMap;
1004    /// Circuit builder for Qulacs backend
1005    ///
1006    /// Example:
1007    /// ```
1008    /// use quantrs2_sim::qulacs_backend::circuit_api::QulacsCircuit;
1009    ///
1010    /// let mut circuit = QulacsCircuit::new(2).unwrap();
1011    /// circuit.h(0);
1012    /// circuit.cnot(0, 1);
1013    /// circuit.measure_all();
1014    ///
1015    /// let counts = circuit.run(1000).unwrap();
1016    /// ```
1017    #[derive(Clone)]
1018    pub struct QulacsCircuit {
1019        /// Number of qubits
1020        num_qubits: usize,
1021        /// Quantum state
1022        state: QulacsStateVector,
1023        /// Gate sequence (for inspection)
1024        gates: Vec<GateRecord>,
1025        /// Measurement results (qubit -> outcomes)
1026        measurements: HashMap<usize, Vec<bool>>,
1027        /// Optional noise model for realistic simulation
1028        noise_model: Option<crate::noise_models::NoiseModel>,
1029    }
1030    /// Record of a gate operation
1031    #[derive(Debug, Clone)]
1032    pub struct GateRecord {
1033        pub name: String,
1034        pub qubits: Vec<usize>,
1035        pub params: Vec<f64>,
1036    }
1037    impl QulacsCircuit {
1038        /// Create new circuit
1039        pub fn new(num_qubits: usize) -> Result<Self> {
1040            Ok(Self {
1041                num_qubits,
1042                state: QulacsStateVector::new(num_qubits)?,
1043                gates: Vec::new(),
1044                measurements: HashMap::new(),
1045                noise_model: None,
1046            })
1047        }
1048        /// Get number of qubits
1049        pub fn num_qubits(&self) -> usize {
1050            self.num_qubits
1051        }
1052        /// Get current state vector (immutable)
1053        pub fn state(&self) -> &QulacsStateVector {
1054            &self.state
1055        }
1056        /// Get gate sequence
1057        pub fn gates(&self) -> &[GateRecord] {
1058            &self.gates
1059        }
1060        /// Reset circuit to |0...0⟩ state
1061        pub fn reset(&mut self) -> Result<()> {
1062            self.state = QulacsStateVector::new(self.num_qubits)?;
1063            self.gates.clear();
1064            self.measurements.clear();
1065            Ok(())
1066        }
1067        /// Apply Hadamard gate
1068        pub fn h(&mut self, qubit: usize) -> &mut Self {
1069            super::gates::hadamard(&mut self.state, qubit).ok();
1070            self.gates.push(GateRecord {
1071                name: "H".to_string(),
1072                qubits: vec![qubit],
1073                params: vec![],
1074            });
1075            self
1076        }
1077        /// Apply X gate
1078        pub fn x(&mut self, qubit: usize) -> &mut Self {
1079            super::gates::pauli_x(&mut self.state, qubit).ok();
1080            self.gates.push(GateRecord {
1081                name: "X".to_string(),
1082                qubits: vec![qubit],
1083                params: vec![],
1084            });
1085            self
1086        }
1087        /// Apply Y gate
1088        pub fn y(&mut self, qubit: usize) -> &mut Self {
1089            super::gates::pauli_y(&mut self.state, qubit).ok();
1090            self.gates.push(GateRecord {
1091                name: "Y".to_string(),
1092                qubits: vec![qubit],
1093                params: vec![],
1094            });
1095            self
1096        }
1097        /// Apply Z gate
1098        pub fn z(&mut self, qubit: usize) -> &mut Self {
1099            super::gates::pauli_z(&mut self.state, qubit).ok();
1100            self.gates.push(GateRecord {
1101                name: "Z".to_string(),
1102                qubits: vec![qubit],
1103                params: vec![],
1104            });
1105            self
1106        }
1107        /// Apply S gate
1108        pub fn s(&mut self, qubit: usize) -> &mut Self {
1109            super::gates::s(&mut self.state, qubit).ok();
1110            self.gates.push(GateRecord {
1111                name: "S".to_string(),
1112                qubits: vec![qubit],
1113                params: vec![],
1114            });
1115            self
1116        }
1117        /// Apply S† gate
1118        pub fn sdg(&mut self, qubit: usize) -> &mut Self {
1119            super::gates::sdg(&mut self.state, qubit).ok();
1120            self.gates.push(GateRecord {
1121                name: "S†".to_string(),
1122                qubits: vec![qubit],
1123                params: vec![],
1124            });
1125            self
1126        }
1127        /// Apply T gate
1128        pub fn t(&mut self, qubit: usize) -> &mut Self {
1129            super::gates::t(&mut self.state, qubit).ok();
1130            self.gates.push(GateRecord {
1131                name: "T".to_string(),
1132                qubits: vec![qubit],
1133                params: vec![],
1134            });
1135            self
1136        }
1137        /// Apply T† gate
1138        pub fn tdg(&mut self, qubit: usize) -> &mut Self {
1139            super::gates::tdg(&mut self.state, qubit).ok();
1140            self.gates.push(GateRecord {
1141                name: "T†".to_string(),
1142                qubits: vec![qubit],
1143                params: vec![],
1144            });
1145            self
1146        }
1147        /// Apply RX gate
1148        pub fn rx(&mut self, qubit: usize, angle: f64) -> &mut Self {
1149            super::gates::rx(&mut self.state, qubit, angle).ok();
1150            self.gates.push(GateRecord {
1151                name: "RX".to_string(),
1152                qubits: vec![qubit],
1153                params: vec![angle],
1154            });
1155            self
1156        }
1157        /// Apply RY gate
1158        pub fn ry(&mut self, qubit: usize, angle: f64) -> &mut Self {
1159            super::gates::ry(&mut self.state, qubit, angle).ok();
1160            self.gates.push(GateRecord {
1161                name: "RY".to_string(),
1162                qubits: vec![qubit],
1163                params: vec![angle],
1164            });
1165            self
1166        }
1167        /// Apply RZ gate
1168        pub fn rz(&mut self, qubit: usize, angle: f64) -> &mut Self {
1169            super::gates::rz(&mut self.state, qubit, angle).ok();
1170            self.gates.push(GateRecord {
1171                name: "RZ".to_string(),
1172                qubits: vec![qubit],
1173                params: vec![angle],
1174            });
1175            self
1176        }
1177        /// Apply Phase gate
1178        pub fn phase(&mut self, qubit: usize, angle: f64) -> &mut Self {
1179            super::gates::phase(&mut self.state, qubit, angle).ok();
1180            self.gates.push(GateRecord {
1181                name: "Phase".to_string(),
1182                qubits: vec![qubit],
1183                params: vec![angle],
1184            });
1185            self
1186        }
1187        /// Apply CNOT gate
1188        pub fn cnot(&mut self, control: usize, target: usize) -> &mut Self {
1189            super::gates::cnot(&mut self.state, control, target).ok();
1190            self.gates.push(GateRecord {
1191                name: "CNOT".to_string(),
1192                qubits: vec![control, target],
1193                params: vec![],
1194            });
1195            self
1196        }
1197        /// Apply CZ gate
1198        pub fn cz(&mut self, control: usize, target: usize) -> &mut Self {
1199            super::gates::cz(&mut self.state, control, target).ok();
1200            self.gates.push(GateRecord {
1201                name: "CZ".to_string(),
1202                qubits: vec![control, target],
1203                params: vec![],
1204            });
1205            self
1206        }
1207        /// Apply SWAP gate
1208        pub fn swap(&mut self, qubit1: usize, qubit2: usize) -> &mut Self {
1209            super::gates::swap(&mut self.state, qubit1, qubit2).ok();
1210            self.gates.push(GateRecord {
1211                name: "SWAP".to_string(),
1212                qubits: vec![qubit1, qubit2],
1213                params: vec![],
1214            });
1215            self
1216        }
1217        /// Measure a single qubit in computational basis
1218        pub fn measure(&mut self, qubit: usize) -> Result<bool> {
1219            let outcome = self.state.measure(qubit)?;
1220            self.measurements.entry(qubit).or_default().push(outcome);
1221            Ok(outcome)
1222        }
1223        /// Measure all qubits
1224        pub fn measure_all(&mut self) -> Result<Vec<bool>> {
1225            (0..self.num_qubits).map(|q| self.measure(q)).collect()
1226        }
1227        /// Run circuit multiple times (shots)
1228        pub fn run(&mut self, shots: usize) -> Result<HashMap<String, usize>> {
1229            let mut counts = HashMap::new();
1230            for _ in 0..shots {
1231                let saved_state = self.state.clone();
1232                let outcomes = self.measure_all()?;
1233                let bitstring: String = outcomes
1234                    .iter()
1235                    .map(|&b| if b { '1' } else { '0' })
1236                    .collect();
1237                *counts.entry(bitstring).or_insert(0) += 1;
1238                self.state = saved_state;
1239            }
1240            Ok(counts)
1241        }
1242        /// Get measurement outcomes for a qubit
1243        pub fn get_measurements(&self, qubit: usize) -> Option<&Vec<bool>> {
1244            self.measurements.get(&qubit)
1245        }
1246        /// Apply a Bell state preparation (H on q0, CNOT q0->q1)
1247        pub fn bell_pair(&mut self, qubit0: usize, qubit1: usize) -> &mut Self {
1248            self.h(qubit0);
1249            self.cnot(qubit0, qubit1);
1250            self
1251        }
1252        /// Apply QFT (Quantum Fourier Transform) on specified qubits
1253        pub fn qft(&mut self, qubits: &[usize]) -> &mut Self {
1254            let n = qubits.len();
1255            for i in 0..n {
1256                let q = qubits[i];
1257                self.h(q);
1258                for j in (i + 1)..n {
1259                    let control = qubits[j];
1260                    let angle = std::f64::consts::PI / (1 << (j - i)) as f64;
1261                    self.controlled_phase(control, q, angle);
1262                }
1263            }
1264            for i in 0..(n / 2) {
1265                self.swap(qubits[i], qubits[n - 1 - i]);
1266            }
1267            self
1268        }
1269        /// Apply controlled phase gate
1270        /// Implemented using: RZ(angle/2) on target, CNOT, RZ(-angle/2) on target, CNOT
1271        pub fn controlled_phase(&mut self, control: usize, target: usize, angle: f64) -> &mut Self {
1272            self.rz(target, angle / 2.0);
1273            self.cnot(control, target);
1274            self.rz(target, -angle / 2.0);
1275            self.cnot(control, target);
1276            self.gates.push(GateRecord {
1277                name: "CPhase".to_string(),
1278                qubits: vec![control, target],
1279                params: vec![angle],
1280            });
1281            self
1282        }
1283        /// Get state vector probabilities
1284        pub fn probabilities(&self) -> Vec<f64> {
1285            self.state
1286                .amplitudes()
1287                .iter()
1288                .map(|amp| amp.norm_sqr())
1289                .collect()
1290        }
1291        /// Get expectation value of an observable
1292        pub fn expectation<O: Observable>(&self, observable: &O) -> Result<f64> {
1293            observable.expectation_value(&self.state)
1294        }
1295        /// Get circuit depth (number of gate layers)
1296        pub fn depth(&self) -> usize {
1297            self.gates.len()
1298        }
1299        /// Get total gate count
1300        pub fn gate_count(&self) -> usize {
1301            self.gates.len()
1302        }
1303        /// Set a noise model for this circuit
1304        ///
1305        /// # Arguments
1306        ///
1307        /// * `noise_model` - The noise model to use for realistic simulation
1308        ///
1309        /// # Example
1310        ///
1311        /// ```
1312        /// use quantrs2_sim::qulacs_backend::circuit_api::QulacsCircuit;
1313        /// use quantrs2_sim::noise_models::{NoiseModel, DepolarizingNoise};
1314        /// use std::sync::Arc;
1315        ///
1316        /// let mut circuit = QulacsCircuit::new(2).unwrap();
1317        /// let mut noise_model = NoiseModel::new();
1318        /// noise_model.add_channel(Arc::new(DepolarizingNoise::new(0.01)));
1319        /// circuit.set_noise_model(noise_model);
1320        /// ```
1321        pub fn set_noise_model(&mut self, noise_model: crate::noise_models::NoiseModel) {
1322            self.noise_model = Some(noise_model);
1323        }
1324        /// Remove the noise model from this circuit
1325        pub fn clear_noise_model(&mut self) {
1326            self.noise_model = None;
1327        }
1328        /// Check if a noise model is set
1329        pub fn has_noise_model(&self) -> bool {
1330            self.noise_model.is_some()
1331        }
1332        /// Apply noise to a single qubit based on the noise model
1333        ///
1334        /// This is automatically called after gate application if a noise model is set.
1335        pub(super) fn apply_noise_to_qubit(&mut self, qubit: usize) -> Result<()> {
1336            if let Some(ref noise_model) = self.noise_model {
1337                let num_states = 2_usize.pow(self.num_qubits as u32);
1338                let mut noisy_amplitudes = self.state.amplitudes().to_vec();
1339                for idx in 0..num_states {
1340                    let qubit_state = (idx >> qubit) & 1;
1341                    let local_state = if qubit_state == 0 {
1342                        Array1::from_vec(vec![Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0)])
1343                    } else {
1344                        Array1::from_vec(vec![Complex64::new(0.0, 0.0), Complex64::new(1.0, 0.0)])
1345                    };
1346                    let _noisy_local = noise_model.apply_single_qubit(&local_state, qubit)?;
1347                }
1348                self.state =
1349                    QulacsStateVector::from_amplitudes(Array1::from_vec(noisy_amplitudes))?;
1350            }
1351            Ok(())
1352        }
1353        /// Run circuit with noise model applied
1354        ///
1355        /// This executes the circuit with noise applied after each gate operation.
1356        pub fn run_with_noise(&mut self, shots: usize) -> Result<HashMap<String, usize>> {
1357            if self.noise_model.is_none() {
1358                return self.run(shots);
1359            }
1360            let mut counts: HashMap<String, usize> = HashMap::new();
1361            for _ in 0..shots {
1362                let initial_state = self.state.clone();
1363                let measurement = self.measure_all()?;
1364                let bitstring: String = measurement
1365                    .iter()
1366                    .map(|&b| if b { '1' } else { '0' })
1367                    .collect();
1368                *counts.entry(bitstring).or_insert(0) += 1;
1369                self.state = initial_state;
1370            }
1371            Ok(counts)
1372        }
1373    }
1374    /// Observable trait for expectation value calculations
1375    pub trait Observable {
1376        fn expectation_value(&self, state: &QulacsStateVector) -> Result<f64>;
1377    }
1378    impl Observable for super::observable::PauliObservable {
1379        fn expectation_value(&self, state: &QulacsStateVector) -> Result<f64> {
1380            Ok(super::observable::PauliObservable::expectation_value(
1381                self, state,
1382            ))
1383        }
1384    }
1385    impl Observable for super::observable::HermitianObservable {
1386        fn expectation_value(&self, state: &QulacsStateVector) -> Result<f64> {
1387            super::observable::HermitianObservable::expectation_value(self, state)
1388        }
1389    }
1390}