1use crate::error::{Result, SimulatorError};
6use scirs2_core::ndarray::*;
7use scirs2_core::{Complex64, Float};
8
9use super::types::QulacsStateVector;
10
11pub type StateIndex = usize;
13pub type QubitIndex = usize;
15pub mod gates {
17 use super::*;
18 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 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 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 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 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 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 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 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 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 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 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 pub fn s(state: &mut QulacsStateVector, target: QubitIndex) -> Result<()> {
522 phase(state, target, std::f64::consts::FRAC_PI_2)
523 }
524 pub fn sdg(state: &mut QulacsStateVector, target: QubitIndex) -> Result<()> {
533 phase(state, target, -std::f64::consts::FRAC_PI_2)
534 }
535 pub fn t(state: &mut QulacsStateVector, target: QubitIndex) -> Result<()> {
544 phase(state, target, std::f64::consts::FRAC_PI_4)
545 }
546 pub fn tdg(state: &mut QulacsStateVector, target: QubitIndex) -> Result<()> {
555 phase(state, target, -std::f64::consts::FRAC_PI_4)
556 }
557 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 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 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}
747pub 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 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
758 pub enum PauliOperator {
759 I,
761 X,
763 Y,
765 Z,
767 }
768 impl PauliOperator {
769 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 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 pub fn commutes_with_z(&self) -> bool {
815 matches!(self, PauliOperator::I | PauliOperator::Z)
816 }
817 }
818 #[derive(Debug, Clone)]
820 pub struct PauliObservable {
821 pub operators: HashMap<usize, PauliOperator>,
823 pub coefficient: f64,
825 }
826 impl PauliObservable {
827 pub fn new(operators: HashMap<usize, PauliOperator>, coefficient: f64) -> Self {
829 Self {
830 operators,
831 coefficient,
832 }
833 }
834 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 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 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 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 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 pub fn with_coefficient(mut self, coefficient: f64) -> Self {
905 self.coefficient = coefficient;
906 self
907 }
908 pub fn weight(&self) -> usize {
910 self.operators
911 .values()
912 .filter(|&&op| op != PauliOperator::I)
913 .count()
914 }
915 }
916 #[derive(Debug, Clone)]
918 pub struct HermitianObservable {
919 pub matrix: Array2<Complex64>,
921 pub num_qubits: usize,
923 }
924 impl HermitianObservable {
925 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 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 #[derive(Debug, Clone)]
960 pub struct CompositeObservable {
961 pub terms: Vec<PauliObservable>,
963 }
964 impl CompositeObservable {
965 pub fn new() -> Self {
967 Self { terms: Vec::new() }
968 }
969 pub fn add_term(mut self, observable: PauliObservable) -> Self {
971 self.terms.push(observable);
972 self
973 }
974 pub fn expectation_value(&self, state: &QulacsStateVector) -> f64 {
976 self.terms
977 .iter()
978 .map(|term| term.expectation_value(state))
979 .sum()
980 }
981 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}
992pub 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 #[derive(Clone)]
1018 pub struct QulacsCircuit {
1019 num_qubits: usize,
1021 state: QulacsStateVector,
1023 gates: Vec<GateRecord>,
1025 measurements: HashMap<usize, Vec<bool>>,
1027 noise_model: Option<crate::noise_models::NoiseModel>,
1029 }
1030 #[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 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 pub fn num_qubits(&self) -> usize {
1050 self.num_qubits
1051 }
1052 pub fn state(&self) -> &QulacsStateVector {
1054 &self.state
1055 }
1056 pub fn gates(&self) -> &[GateRecord] {
1058 &self.gates
1059 }
1060 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 pub fn measure_all(&mut self) -> Result<Vec<bool>> {
1225 (0..self.num_qubits).map(|q| self.measure(q)).collect()
1226 }
1227 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 pub fn get_measurements(&self, qubit: usize) -> Option<&Vec<bool>> {
1244 self.measurements.get(&qubit)
1245 }
1246 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 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 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 pub fn probabilities(&self) -> Vec<f64> {
1285 self.state
1286 .amplitudes()
1287 .iter()
1288 .map(|amp| amp.norm_sqr())
1289 .collect()
1290 }
1291 pub fn expectation<O: Observable>(&self, observable: &O) -> Result<f64> {
1293 observable.expectation_value(&self.state)
1294 }
1295 pub fn depth(&self) -> usize {
1297 self.gates.len()
1298 }
1299 pub fn gate_count(&self) -> usize {
1301 self.gates.len()
1302 }
1303 pub fn set_noise_model(&mut self, noise_model: crate::noise_models::NoiseModel) {
1322 self.noise_model = Some(noise_model);
1323 }
1324 pub fn clear_noise_model(&mut self) {
1326 self.noise_model = None;
1327 }
1328 pub fn has_noise_model(&self) -> bool {
1330 self.noise_model.is_some()
1331 }
1332 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 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 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}