1use crate::{
8 error::{QuantRS2Error, QuantRS2Result},
9 gate::{single, GateOp},
10 qubit::QubitId,
11};
12use rustc_hash::FxHashMap;
13use scirs2_core::ndarray::Array2;
14use scirs2_core::Complex;
15
16type Complex64 = Complex<f64>;
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
21pub enum FermionOperatorType {
22 Creation,
24 Annihilation,
26 Number,
28 Identity,
30}
31
32#[derive(Debug, Clone, PartialEq)]
34pub struct FermionOperator {
35 pub op_type: FermionOperatorType,
37 pub mode: usize,
39 pub coefficient: Complex64,
41}
42
43impl FermionOperator {
44 pub const fn new(op_type: FermionOperatorType, mode: usize, coefficient: Complex64) -> Self {
46 Self {
47 op_type,
48 mode,
49 coefficient,
50 }
51 }
52
53 pub const fn creation(mode: usize) -> Self {
55 Self::new(
56 FermionOperatorType::Creation,
57 mode,
58 Complex64::new(1.0, 0.0),
59 )
60 }
61
62 pub const fn annihilation(mode: usize) -> Self {
64 Self::new(
65 FermionOperatorType::Annihilation,
66 mode,
67 Complex64::new(1.0, 0.0),
68 )
69 }
70
71 pub const fn number(mode: usize) -> Self {
73 Self::new(FermionOperatorType::Number, mode, Complex64::new(1.0, 0.0))
74 }
75
76 #[must_use]
78 pub fn dagger(&self) -> Self {
79 let conj_coeff = self.coefficient.conj();
80 match self.op_type {
81 FermionOperatorType::Creation => {
82 Self::new(FermionOperatorType::Annihilation, self.mode, conj_coeff)
83 }
84 FermionOperatorType::Annihilation => {
85 Self::new(FermionOperatorType::Creation, self.mode, conj_coeff)
86 }
87 FermionOperatorType::Number => {
88 Self::new(FermionOperatorType::Number, self.mode, conj_coeff)
89 }
90 FermionOperatorType::Identity => {
91 Self::new(FermionOperatorType::Identity, self.mode, conj_coeff)
92 }
93 }
94 }
95}
96
97#[derive(Debug, Clone, PartialEq)]
99pub struct FermionTerm {
100 pub operators: Vec<FermionOperator>,
102 pub coefficient: Complex64,
104}
105
106impl FermionTerm {
107 pub const fn new(operators: Vec<FermionOperator>, coefficient: Complex64) -> Self {
109 Self {
110 operators,
111 coefficient,
112 }
113 }
114
115 pub const fn identity() -> Self {
117 Self {
118 operators: vec![],
119 coefficient: Complex64::new(1.0, 0.0),
120 }
121 }
122
123 pub fn normal_order(&mut self) -> QuantRS2Result<()> {
125 let n = self.operators.len();
127 for i in 0..n {
128 for j in 0..n.saturating_sub(i + 1) {
129 if self.should_swap(j) {
130 self.swap_operators(j)?;
131 }
132 }
133 }
134 Ok(())
135 }
136
137 fn should_swap(&self, idx: usize) -> bool {
139 if idx + 1 >= self.operators.len() {
140 return false;
141 }
142
143 let op1 = &self.operators[idx];
144 let op2 = &self.operators[idx + 1];
145
146 match (op1.op_type, op2.op_type) {
148 (FermionOperatorType::Annihilation, FermionOperatorType::Creation) => {
149 op1.mode > op2.mode
150 }
151 (FermionOperatorType::Creation, FermionOperatorType::Creation) => op1.mode > op2.mode,
152 (FermionOperatorType::Annihilation, FermionOperatorType::Annihilation) => {
153 op1.mode < op2.mode
154 }
155 _ => false,
156 }
157 }
158
159 fn swap_operators(&mut self, idx: usize) -> QuantRS2Result<()> {
161 if idx + 1 >= self.operators.len() {
162 return Err(QuantRS2Error::InvalidInput("Index out of bounds".into()));
163 }
164
165 let op1 = &self.operators[idx];
166 let op2 = &self.operators[idx + 1];
167
168 if op1.mode == op2.mode {
170 match (op1.op_type, op2.op_type) {
172 (FermionOperatorType::Annihilation, FermionOperatorType::Creation) => {
173 return Err(QuantRS2Error::UnsupportedOperation(
176 "Anticommutation that produces multiple terms not yet supported".into(),
177 ));
178 }
179 _ => {
180 self.coefficient *= -1.0;
181 }
182 }
183 } else {
184 self.coefficient *= -1.0;
186 }
187
188 self.operators.swap(idx, idx + 1);
189 Ok(())
190 }
191
192 #[must_use]
194 pub fn dagger(&self) -> Self {
195 let mut conj_ops = self.operators.clone();
196 conj_ops.reverse();
197 conj_ops = conj_ops.into_iter().map(|op| op.dagger()).collect();
198
199 Self {
200 operators: conj_ops,
201 coefficient: self.coefficient.conj(),
202 }
203 }
204}
205
206#[derive(Debug, Clone)]
208pub struct FermionHamiltonian {
209 pub terms: Vec<FermionTerm>,
211 pub n_modes: usize,
213}
214
215impl FermionHamiltonian {
216 pub const fn new(n_modes: usize) -> Self {
218 Self {
219 terms: Vec::new(),
220 n_modes,
221 }
222 }
223
224 pub fn add_term(&mut self, term: FermionTerm) {
226 self.terms.push(term);
227 }
228
229 pub fn add_one_body(&mut self, i: usize, j: usize, coefficient: Complex64) {
231 let term = FermionTerm::new(
232 vec![
233 FermionOperator::creation(i),
234 FermionOperator::annihilation(j),
235 ],
236 coefficient,
237 );
238 self.add_term(term);
239 }
240
241 pub fn add_two_body(&mut self, i: usize, j: usize, k: usize, l: usize, coefficient: Complex64) {
243 let term = FermionTerm::new(
244 vec![
245 FermionOperator::creation(i),
246 FermionOperator::creation(j),
247 FermionOperator::annihilation(k),
248 FermionOperator::annihilation(l),
249 ],
250 coefficient,
251 );
252 self.add_term(term);
253 }
254
255 pub fn add_chemical_potential(&mut self, i: usize, mu: f64) {
257 let term = FermionTerm::new(vec![FermionOperator::number(i)], Complex64::new(mu, 0.0));
258 self.add_term(term);
259 }
260
261 #[must_use]
263 pub fn dagger(&self) -> Self {
264 let conj_terms = self.terms.iter().map(|t| t.dagger()).collect();
265 Self {
266 terms: conj_terms,
267 n_modes: self.n_modes,
268 }
269 }
270
271 pub fn is_hermitian(&self, _tolerance: f64) -> bool {
273 let conj = self.dagger();
274
275 if self.terms.len() != conj.terms.len() {
277 return false;
278 }
279
280 true }
283}
284
285pub struct JordanWigner {
287 n_modes: usize,
289}
290
291impl JordanWigner {
292 pub const fn new(n_modes: usize) -> Self {
294 Self { n_modes }
295 }
296
297 pub fn transform_operator(&self, op: &FermionOperator) -> QuantRS2Result<Vec<QubitOperator>> {
299 match op.op_type {
300 FermionOperatorType::Creation => self.transform_creation(op.mode, op.coefficient),
301 FermionOperatorType::Annihilation => {
302 self.transform_annihilation(op.mode, op.coefficient)
303 }
304 FermionOperatorType::Number => self.transform_number(op.mode, op.coefficient),
305 FermionOperatorType::Identity => {
306 Ok(vec![QubitOperator::identity(self.n_modes, op.coefficient)])
307 }
308 }
309 }
310
311 fn transform_creation(
313 &self,
314 mode: usize,
315 coeff: Complex64,
316 ) -> QuantRS2Result<Vec<QubitOperator>> {
317 if mode >= self.n_modes {
318 return Err(QuantRS2Error::InvalidInput(format!(
319 "Mode {mode} out of bounds"
320 )));
321 }
322
323 let mut operators = Vec::new();
324
325 let sigma_minus = QubitTerm {
327 operators: vec![(mode, PauliOperator::Minus)],
328 coefficient: coeff,
329 };
330
331 let z_string: Vec<_> = (0..mode).map(|i| (i, PauliOperator::Z)).collect();
333
334 let mut term = sigma_minus;
335 term.operators.extend(z_string);
336
337 operators.push(QubitOperator {
338 terms: vec![term],
339 n_qubits: self.n_modes,
340 });
341
342 Ok(operators)
343 }
344
345 fn transform_annihilation(
347 &self,
348 mode: usize,
349 coeff: Complex64,
350 ) -> QuantRS2Result<Vec<QubitOperator>> {
351 if mode >= self.n_modes {
352 return Err(QuantRS2Error::InvalidInput(format!(
353 "Mode {mode} out of bounds"
354 )));
355 }
356
357 let mut operators = Vec::new();
358
359 let sigma_plus = QubitTerm {
361 operators: vec![(mode, PauliOperator::Plus)],
362 coefficient: coeff,
363 };
364
365 let z_string: Vec<_> = (0..mode).map(|i| (i, PauliOperator::Z)).collect();
367
368 let mut term = sigma_plus;
369 term.operators.extend(z_string);
370
371 operators.push(QubitOperator {
372 terms: vec![term],
373 n_qubits: self.n_modes,
374 });
375
376 Ok(operators)
377 }
378
379 fn transform_number(
381 &self,
382 mode: usize,
383 coeff: Complex64,
384 ) -> QuantRS2Result<Vec<QubitOperator>> {
385 if mode >= self.n_modes {
386 return Err(QuantRS2Error::InvalidInput(format!(
387 "Mode {mode} out of bounds"
388 )));
389 }
390
391 let mut operators = Vec::new();
392
393 operators.push(QubitOperator {
395 terms: vec![QubitTerm {
396 operators: vec![],
397 coefficient: coeff * 0.5,
398 }],
399 n_qubits: self.n_modes,
400 });
401
402 operators.push(QubitOperator {
404 terms: vec![QubitTerm {
405 operators: vec![(mode, PauliOperator::Z)],
406 coefficient: -coeff * 0.5,
407 }],
408 n_qubits: self.n_modes,
409 });
410
411 Ok(operators)
412 }
413
414 pub fn transform_term(&self, term: &FermionTerm) -> QuantRS2Result<QubitOperator> {
416 if term.operators.is_empty() {
417 return Ok(QubitOperator::identity(self.n_modes, term.coefficient));
418 }
419
420 let mut result = QubitOperator::identity(self.n_modes, term.coefficient);
422
423 for op in &term.operators {
424 let transformed = self.transform_operator(op)?;
425
426 let mut new_result = QubitOperator::zero(self.n_modes);
428 for t in transformed {
429 new_result = new_result.add(&result.multiply(&t)?)?;
430 }
431 result = new_result;
432 }
433
434 Ok(result)
435 }
436
437 pub fn transform_hamiltonian(
439 &self,
440 hamiltonian: &FermionHamiltonian,
441 ) -> QuantRS2Result<QubitOperator> {
442 let mut qubit_ham = QubitOperator::zero(self.n_modes);
443
444 for term in &hamiltonian.terms {
445 let transformed = self.transform_term(term)?;
446 qubit_ham = qubit_ham.add(&transformed)?;
447 }
448
449 Ok(qubit_ham)
450 }
451}
452
453#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
455pub enum PauliOperator {
456 I,
458 X,
460 Y,
462 Z,
464 Plus,
466 Minus,
468}
469
470impl PauliOperator {
471 pub fn matrix(&self) -> Array2<Complex64> {
473 match self {
474 Self::I => Array2::from_shape_vec(
475 (2, 2),
476 vec![
477 Complex64::new(1.0, 0.0),
478 Complex64::new(0.0, 0.0),
479 Complex64::new(0.0, 0.0),
480 Complex64::new(1.0, 0.0),
481 ],
482 )
483 .expect("Pauli I matrix construction should succeed"),
484 Self::X => Array2::from_shape_vec(
485 (2, 2),
486 vec![
487 Complex64::new(0.0, 0.0),
488 Complex64::new(1.0, 0.0),
489 Complex64::new(1.0, 0.0),
490 Complex64::new(0.0, 0.0),
491 ],
492 )
493 .expect("Pauli X matrix construction should succeed"),
494 Self::Y => Array2::from_shape_vec(
495 (2, 2),
496 vec![
497 Complex64::new(0.0, 0.0),
498 Complex64::new(0.0, -1.0),
499 Complex64::new(0.0, 1.0),
500 Complex64::new(0.0, 0.0),
501 ],
502 )
503 .expect("Pauli Y matrix construction should succeed"),
504 Self::Z => Array2::from_shape_vec(
505 (2, 2),
506 vec![
507 Complex64::new(1.0, 0.0),
508 Complex64::new(0.0, 0.0),
509 Complex64::new(0.0, 0.0),
510 Complex64::new(-1.0, 0.0),
511 ],
512 )
513 .expect("Pauli Z matrix construction should succeed"),
514 Self::Plus => Array2::from_shape_vec(
515 (2, 2),
516 vec![
517 Complex64::new(0.0, 0.0),
518 Complex64::new(1.0, 0.0),
519 Complex64::new(0.0, 0.0),
520 Complex64::new(0.0, 0.0),
521 ],
522 )
523 .expect("Pauli Plus matrix construction should succeed"),
524 Self::Minus => Array2::from_shape_vec(
525 (2, 2),
526 vec![
527 Complex64::new(0.0, 0.0),
528 Complex64::new(0.0, 0.0),
529 Complex64::new(1.0, 0.0),
530 Complex64::new(0.0, 0.0),
531 ],
532 )
533 .expect("Pauli Minus matrix construction should succeed"),
534 }
535 }
536}
537
538#[derive(Debug, Clone)]
540pub struct QubitTerm {
541 pub operators: Vec<(usize, PauliOperator)>,
543 pub coefficient: Complex64,
545}
546
547#[derive(Debug, Clone)]
549pub struct QubitOperator {
550 pub terms: Vec<QubitTerm>,
552 pub n_qubits: usize,
554}
555
556impl QubitOperator {
557 pub const fn zero(n_qubits: usize) -> Self {
559 Self {
560 terms: vec![],
561 n_qubits,
562 }
563 }
564
565 pub fn identity(n_qubits: usize, coefficient: Complex64) -> Self {
567 Self {
568 terms: vec![QubitTerm {
569 operators: vec![],
570 coefficient,
571 }],
572 n_qubits,
573 }
574 }
575
576 pub fn add(&self, other: &Self) -> QuantRS2Result<Self> {
578 if self.n_qubits != other.n_qubits {
579 return Err(QuantRS2Error::InvalidInput(
580 "Operators must have same number of qubits".into(),
581 ));
582 }
583
584 let mut result = self.clone();
585 result.terms.extend(other.terms.clone());
586 Ok(result)
587 }
588
589 pub fn multiply(&self, other: &Self) -> QuantRS2Result<Self> {
591 if self.n_qubits != other.n_qubits {
592 return Err(QuantRS2Error::InvalidInput(
593 "Operators must have same number of qubits".into(),
594 ));
595 }
596
597 let mut result_terms = Vec::new();
598
599 for term1 in &self.terms {
600 for term2 in &other.terms {
601 let coeff = term1.coefficient * term2.coefficient;
603
604 let mut combined_ops = term1.operators.clone();
606 combined_ops.extend(&term2.operators);
607
608 result_terms.push(QubitTerm {
609 operators: combined_ops,
610 coefficient: coeff,
611 });
612 }
613 }
614
615 Ok(Self {
616 terms: result_terms,
617 n_qubits: self.n_qubits,
618 })
619 }
620
621 pub fn simplify(&mut self) {
623 let mut grouped: FxHashMap<Vec<(usize, PauliOperator)>, Complex64> = FxHashMap::default();
625
626 for term in &self.terms {
627 let key = term.operators.clone();
628 *grouped.entry(key).or_insert(Complex64::new(0.0, 0.0)) += term.coefficient;
629 }
630
631 self.terms = grouped
633 .into_iter()
634 .filter(|(_, coeff)| coeff.norm() > 1e-12)
635 .map(|(ops, coeff)| QubitTerm {
636 operators: ops,
637 coefficient: coeff,
638 })
639 .collect();
640 }
641}
642
643pub fn qubit_operator_to_gates(op: &QubitOperator) -> QuantRS2Result<Vec<Box<dyn GateOp>>> {
645 let mut gates = Vec::new();
646
647 for term in &op.terms {
648 for (qubit, pauli) in &term.operators {
653 let gate: Box<dyn GateOp> = match pauli {
654 PauliOperator::I => continue, PauliOperator::X => Box::new(single::PauliX {
656 target: QubitId(*qubit as u32),
657 }),
658 PauliOperator::Y => Box::new(single::PauliY {
659 target: QubitId(*qubit as u32),
660 }),
661 PauliOperator::Z => Box::new(single::PauliZ {
662 target: QubitId(*qubit as u32),
663 }),
664 PauliOperator::Plus | PauliOperator::Minus => {
665 return Err(QuantRS2Error::UnsupportedOperation(
666 "Ladder operators require decomposition".into(),
667 ));
668 }
669 };
670 gates.push(gate);
671 }
672 }
673
674 Ok(gates)
675}
676
677pub struct BravyiKitaev {
679 #[allow(dead_code)]
680 n_modes: usize,
681}
682
683impl BravyiKitaev {
684 pub const fn new(n_modes: usize) -> Self {
686 Self { n_modes }
687 }
688
689 pub fn transform_operator(&self, _op: &FermionOperator) -> QuantRS2Result<Vec<QubitOperator>> {
691 Err(QuantRS2Error::UnsupportedOperation(
694 "Bravyi-Kitaev transformation not yet implemented".into(),
695 ))
696 }
697}
698
699#[cfg(test)]
700mod tests {
701 use super::*;
702
703 #[test]
704 fn test_fermion_operator_creation() {
705 let op = FermionOperator::creation(0);
706 assert_eq!(op.op_type, FermionOperatorType::Creation);
707 assert_eq!(op.mode, 0);
708 assert_eq!(op.coefficient, Complex64::new(1.0, 0.0));
709 }
710
711 #[test]
712 fn test_fermion_operator_dagger() {
713 let op = FermionOperator::creation(0);
714 let dag = op.dagger();
715 assert_eq!(dag.op_type, FermionOperatorType::Annihilation);
716 assert_eq!(dag.mode, 0);
717 }
718
719 #[test]
720 fn test_jordan_wigner_number_operator() {
721 let jw = JordanWigner::new(4);
722 let op = FermionOperator::number(1);
723 let qubit_ops = jw
724 .transform_operator(&op)
725 .expect("Jordan-Wigner transformation should succeed");
726
727 assert_eq!(qubit_ops.len(), 2);
729 }
730
731 #[test]
732 fn test_jordan_wigner_creation_operator() {
733 let jw = JordanWigner::new(4);
734 let op = FermionOperator::creation(2);
735 let qubit_ops = jw
736 .transform_operator(&op)
737 .expect("Jordan-Wigner transformation should succeed");
738
739 assert_eq!(qubit_ops.len(), 1);
741 }
742
743 #[test]
744 fn test_fermionic_hamiltonian() {
745 let mut ham = FermionHamiltonian::new(4);
746
747 ham.add_one_body(0, 1, Complex64::new(-1.0, 0.0));
749 ham.add_one_body(1, 0, Complex64::new(-1.0, 0.0));
750
751 ham.add_two_body(0, 1, 1, 0, Complex64::new(2.0, 0.0));
753
754 assert_eq!(ham.terms.len(), 3);
755 }
756
757 #[test]
758 fn test_qubit_operator_operations() {
759 let op1 = QubitOperator::identity(2, Complex64::new(1.0, 0.0));
760 let op2 = QubitOperator::identity(2, Complex64::new(2.0, 0.0));
761
762 let sum = op1
763 .add(&op2)
764 .expect("QubitOperator addition should succeed");
765 assert_eq!(sum.terms.len(), 2);
766
767 let prod = op1
768 .multiply(&op2)
769 .expect("QubitOperator multiplication should succeed");
770 assert_eq!(prod.terms.len(), 1);
771 assert_eq!(prod.terms[0].coefficient, Complex64::new(2.0, 0.0));
772 }
773}