1use crate::{
8 error::{QuantRS2Error, QuantRS2Result},
9 gate::{single, GateOp},
10 qubit::QubitId,
11};
12use ndarray::Array2;
13use num_complex::Complex;
14use rustc_hash::FxHashMap;
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 fn new(op_type: FermionOperatorType, mode: usize, coefficient: Complex64) -> Self {
46 Self {
47 op_type,
48 mode,
49 coefficient,
50 }
51 }
52
53 pub 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 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 fn number(mode: usize) -> Self {
73 Self::new(FermionOperatorType::Number, mode, Complex64::new(1.0, 0.0))
74 }
75
76 pub fn dagger(&self) -> Self {
78 let conj_coeff = self.coefficient.conj();
79 match self.op_type {
80 FermionOperatorType::Creation => {
81 Self::new(FermionOperatorType::Annihilation, self.mode, conj_coeff)
82 }
83 FermionOperatorType::Annihilation => {
84 Self::new(FermionOperatorType::Creation, self.mode, conj_coeff)
85 }
86 FermionOperatorType::Number => {
87 Self::new(FermionOperatorType::Number, self.mode, conj_coeff)
88 }
89 FermionOperatorType::Identity => {
90 Self::new(FermionOperatorType::Identity, self.mode, conj_coeff)
91 }
92 }
93 }
94}
95
96#[derive(Debug, Clone, PartialEq)]
98pub struct FermionTerm {
99 pub operators: Vec<FermionOperator>,
101 pub coefficient: Complex64,
103}
104
105impl FermionTerm {
106 pub fn new(operators: Vec<FermionOperator>, coefficient: Complex64) -> Self {
108 Self {
109 operators,
110 coefficient,
111 }
112 }
113
114 pub fn identity() -> Self {
116 Self {
117 operators: vec![],
118 coefficient: Complex64::new(1.0, 0.0),
119 }
120 }
121
122 pub fn normal_order(&mut self) -> QuantRS2Result<()> {
124 let n = self.operators.len();
126 for i in 0..n {
127 for j in 0..n.saturating_sub(i + 1) {
128 if self.should_swap(j) {
129 self.swap_operators(j)?;
130 }
131 }
132 }
133 Ok(())
134 }
135
136 fn should_swap(&self, idx: usize) -> bool {
138 if idx + 1 >= self.operators.len() {
139 return false;
140 }
141
142 let op1 = &self.operators[idx];
143 let op2 = &self.operators[idx + 1];
144
145 match (op1.op_type, op2.op_type) {
147 (FermionOperatorType::Annihilation, FermionOperatorType::Creation) => {
148 op1.mode > op2.mode
149 }
150 (FermionOperatorType::Creation, FermionOperatorType::Creation) => op1.mode > op2.mode,
151 (FermionOperatorType::Annihilation, FermionOperatorType::Annihilation) => {
152 op1.mode < op2.mode
153 }
154 _ => false,
155 }
156 }
157
158 fn swap_operators(&mut self, idx: usize) -> QuantRS2Result<()> {
160 if idx + 1 >= self.operators.len() {
161 return Err(QuantRS2Error::InvalidInput("Index out of bounds".into()));
162 }
163
164 let op1 = &self.operators[idx];
165 let op2 = &self.operators[idx + 1];
166
167 if op1.mode != op2.mode {
169 self.coefficient *= -1.0;
171 } else {
172 match (op1.op_type, op2.op_type) {
174 (FermionOperatorType::Annihilation, FermionOperatorType::Creation) => {
175 return Err(QuantRS2Error::UnsupportedOperation(
178 "Anticommutation that produces multiple terms not yet supported".into(),
179 ));
180 }
181 _ => {
182 self.coefficient *= -1.0;
183 }
184 }
185 }
186
187 self.operators.swap(idx, idx + 1);
188 Ok(())
189 }
190
191 pub fn dagger(&self) -> Self {
193 let mut conj_ops = self.operators.clone();
194 conj_ops.reverse();
195 conj_ops = conj_ops.into_iter().map(|op| op.dagger()).collect();
196
197 Self {
198 operators: conj_ops,
199 coefficient: self.coefficient.conj(),
200 }
201 }
202}
203
204#[derive(Debug, Clone)]
206pub struct FermionHamiltonian {
207 pub terms: Vec<FermionTerm>,
209 pub n_modes: usize,
211}
212
213impl FermionHamiltonian {
214 pub fn new(n_modes: usize) -> Self {
216 Self {
217 terms: Vec::new(),
218 n_modes,
219 }
220 }
221
222 pub fn add_term(&mut self, term: FermionTerm) {
224 self.terms.push(term);
225 }
226
227 pub fn add_one_body(&mut self, i: usize, j: usize, coefficient: Complex64) {
229 let term = FermionTerm::new(
230 vec![
231 FermionOperator::creation(i),
232 FermionOperator::annihilation(j),
233 ],
234 coefficient,
235 );
236 self.add_term(term);
237 }
238
239 pub fn add_two_body(&mut self, i: usize, j: usize, k: usize, l: usize, coefficient: Complex64) {
241 let term = FermionTerm::new(
242 vec![
243 FermionOperator::creation(i),
244 FermionOperator::creation(j),
245 FermionOperator::annihilation(k),
246 FermionOperator::annihilation(l),
247 ],
248 coefficient,
249 );
250 self.add_term(term);
251 }
252
253 pub fn add_chemical_potential(&mut self, i: usize, mu: f64) {
255 let term = FermionTerm::new(vec![FermionOperator::number(i)], Complex64::new(mu, 0.0));
256 self.add_term(term);
257 }
258
259 pub fn dagger(&self) -> Self {
261 let conj_terms = self.terms.iter().map(|t| t.dagger()).collect();
262 Self {
263 terms: conj_terms,
264 n_modes: self.n_modes,
265 }
266 }
267
268 pub fn is_hermitian(&self, _tolerance: f64) -> bool {
270 let conj = self.dagger();
271
272 if self.terms.len() != conj.terms.len() {
274 return false;
275 }
276
277 true }
280}
281
282pub struct JordanWigner {
284 n_modes: usize,
286}
287
288impl JordanWigner {
289 pub fn new(n_modes: usize) -> Self {
291 Self { n_modes }
292 }
293
294 pub fn transform_operator(&self, op: &FermionOperator) -> QuantRS2Result<Vec<QubitOperator>> {
296 match op.op_type {
297 FermionOperatorType::Creation => self.transform_creation(op.mode, op.coefficient),
298 FermionOperatorType::Annihilation => {
299 self.transform_annihilation(op.mode, op.coefficient)
300 }
301 FermionOperatorType::Number => self.transform_number(op.mode, op.coefficient),
302 FermionOperatorType::Identity => {
303 Ok(vec![QubitOperator::identity(self.n_modes, op.coefficient)])
304 }
305 }
306 }
307
308 fn transform_creation(
310 &self,
311 mode: usize,
312 coeff: Complex64,
313 ) -> QuantRS2Result<Vec<QubitOperator>> {
314 if mode >= self.n_modes {
315 return Err(QuantRS2Error::InvalidInput(format!(
316 "Mode {} out of bounds",
317 mode
318 )));
319 }
320
321 let mut operators = Vec::new();
322
323 let sigma_minus = QubitTerm {
325 operators: vec![(mode, PauliOperator::Minus)],
326 coefficient: coeff,
327 };
328
329 let z_string: Vec<_> = (0..mode).map(|i| (i, PauliOperator::Z)).collect();
331
332 let mut term = sigma_minus;
333 term.operators.extend(z_string);
334
335 operators.push(QubitOperator {
336 terms: vec![term],
337 n_qubits: self.n_modes,
338 });
339
340 Ok(operators)
341 }
342
343 fn transform_annihilation(
345 &self,
346 mode: usize,
347 coeff: Complex64,
348 ) -> QuantRS2Result<Vec<QubitOperator>> {
349 if mode >= self.n_modes {
350 return Err(QuantRS2Error::InvalidInput(format!(
351 "Mode {} out of bounds",
352 mode
353 )));
354 }
355
356 let mut operators = Vec::new();
357
358 let sigma_plus = QubitTerm {
360 operators: vec![(mode, PauliOperator::Plus)],
361 coefficient: coeff,
362 };
363
364 let z_string: Vec<_> = (0..mode).map(|i| (i, PauliOperator::Z)).collect();
366
367 let mut term = sigma_plus;
368 term.operators.extend(z_string);
369
370 operators.push(QubitOperator {
371 terms: vec![term],
372 n_qubits: self.n_modes,
373 });
374
375 Ok(operators)
376 }
377
378 fn transform_number(
380 &self,
381 mode: usize,
382 coeff: Complex64,
383 ) -> QuantRS2Result<Vec<QubitOperator>> {
384 if mode >= self.n_modes {
385 return Err(QuantRS2Error::InvalidInput(format!(
386 "Mode {} out of bounds",
387 mode
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 PauliOperator::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 .unwrap(),
484 PauliOperator::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 .unwrap(),
494 PauliOperator::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 .unwrap(),
504 PauliOperator::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 .unwrap(),
514 PauliOperator::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 .unwrap(),
524 PauliOperator::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 .unwrap(),
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 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 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.transform_operator(&op).unwrap();
724
725 assert_eq!(qubit_ops.len(), 2);
727 }
728
729 #[test]
730 fn test_jordan_wigner_creation_operator() {
731 let jw = JordanWigner::new(4);
732 let op = FermionOperator::creation(2);
733 let qubit_ops = jw.transform_operator(&op).unwrap();
734
735 assert_eq!(qubit_ops.len(), 1);
737 }
738
739 #[test]
740 fn test_fermionic_hamiltonian() {
741 let mut ham = FermionHamiltonian::new(4);
742
743 ham.add_one_body(0, 1, Complex64::new(-1.0, 0.0));
745 ham.add_one_body(1, 0, Complex64::new(-1.0, 0.0));
746
747 ham.add_two_body(0, 1, 1, 0, Complex64::new(2.0, 0.0));
749
750 assert_eq!(ham.terms.len(), 3);
751 }
752
753 #[test]
754 fn test_qubit_operator_operations() {
755 let op1 = QubitOperator::identity(2, Complex64::new(1.0, 0.0));
756 let op2 = QubitOperator::identity(2, Complex64::new(2.0, 0.0));
757
758 let sum = op1.add(&op2).unwrap();
759 assert_eq!(sum.terms.len(), 2);
760
761 let prod = op1.multiply(&op2).unwrap();
762 assert_eq!(prod.terms.len(), 1);
763 assert_eq!(prod.terms[0].coefficient, Complex64::new(2.0, 0.0));
764 }
765}