quantrs2_symengine_pure/quantum/
operators.rs1use crate::error::SymEngineResult;
4use crate::expr::{ExprLang, Expression};
5
6#[must_use]
8pub fn commutator(a: &Expression, b: &Expression) -> Expression {
9 a.clone() * b.clone() - b.clone() * a.clone()
10}
11
12#[must_use]
14pub fn anticommutator(a: &Expression, b: &Expression) -> Expression {
15 a.clone() * b.clone() + b.clone() * a.clone()
16}
17
18pub fn tensor_product(a: &Expression, b: &Expression) -> Expression {
20 Expression::new(format!("kronecker({a}, {b})"))
21}
22
23pub fn trace(matrix: &Expression) -> Expression {
25 Expression::new(format!("trace({matrix})"))
26}
27
28pub fn dagger(expr: &Expression) -> Expression {
30 Expression::new(format!("conjugate(transpose({expr}))"))
31}
32
33pub fn is_hermitian(matrix: &Expression) -> Expression {
37 matrix.clone() - dagger(matrix)
38}
39
40pub fn is_unitary(matrix: &Expression) -> Expression {
44 dagger(matrix) * matrix.clone()
45}
46
47pub fn transpose(matrix: &Expression) -> Expression {
49 Expression::new(format!("transpose({matrix})"))
50}
51
52pub fn determinant(matrix: &Expression) -> Expression {
54 Expression::new(format!("det({matrix})"))
55}
56
57pub fn inverse(matrix: &Expression) -> Expression {
59 Expression::new(format!("inverse({matrix})"))
60}
61
62pub fn matrix_exp(matrix: &Expression) -> Expression {
64 crate::ops::trig::exp(matrix)
65}
66
67#[must_use]
69pub fn creation() -> Expression {
70 Expression::symbol("a_dag")
71}
72
73#[must_use]
75pub fn annihilation() -> Expression {
76 Expression::symbol("a")
77}
78
79#[must_use]
81pub fn number_operator() -> Expression {
82 creation() * annihilation()
83}
84
85#[must_use]
87pub fn position_operator() -> Expression {
88 let a = annihilation();
89 let a_dag = creation();
90 let sqrt2 = crate::ops::trig::sqrt(&Expression::int(2));
91 (a + a_dag) / sqrt2
92}
93
94#[must_use]
96pub fn momentum_operator() -> Expression {
97 let a = annihilation();
98 let a_dag = creation();
99 let sqrt2 = crate::ops::trig::sqrt(&Expression::int(2));
100 Expression::i() * (a_dag - a) / sqrt2
101}
102
103#[must_use]
105pub fn fermionic_creation() -> Expression {
106 Expression::symbol("c_dag")
107}
108
109#[must_use]
111pub fn fermionic_annihilation() -> Expression {
112 Expression::symbol("c")
113}
114
115#[must_use]
117pub fn fermionic_number_operator() -> Expression {
118 fermionic_creation() * fermionic_annihilation()
119}
120
121#[must_use]
123pub fn displacement_operator(alpha: &Expression) -> Expression {
124 let a = annihilation();
125 let a_dag = creation();
126 let arg = alpha.clone() * a_dag - alpha.conjugate() * a;
127 crate::ops::trig::exp(&arg)
128}
129
130#[must_use]
132pub fn squeezing_operator(zeta: &Expression) -> Expression {
133 let a = annihilation();
134 let a_dag = creation();
135 let two = Expression::int(2);
136 let term1 = zeta.clone() * a.pow(&two);
137 let term2 = zeta.conjugate() * a_dag.pow(&two);
138 let arg = (term1 - term2) / two;
139 crate::ops::trig::exp(&arg)
140}
141
142#[must_use]
144pub fn spin_raising() -> Expression {
145 Expression::symbol("S_plus")
146}
147
148#[must_use]
150pub fn spin_lowering() -> Expression {
151 Expression::symbol("S_minus")
152}
153
154#[must_use]
156pub fn spin_x() -> Expression {
157 Expression::symbol("S_x")
158}
159
160#[must_use]
162pub fn spin_y() -> Expression {
163 Expression::symbol("S_y")
164}
165
166#[must_use]
168pub fn spin_z() -> Expression {
169 Expression::symbol("S_z")
170}
171
172#[must_use]
174pub fn spin_squared() -> Expression {
175 let two = Expression::int(2);
176 spin_x().pow(&two) + spin_y().pow(&two) + spin_z().pow(&two)
177}
178
179#[must_use]
185pub fn angular_momentum_x() -> Expression {
186 Expression::symbol("J_x")
187}
188
189#[must_use]
191pub fn angular_momentum_y() -> Expression {
192 Expression::symbol("J_y")
193}
194
195#[must_use]
197pub fn angular_momentum_z() -> Expression {
198 Expression::symbol("J_z")
199}
200
201#[must_use]
203pub fn angular_momentum_squared() -> Expression {
204 let two = Expression::int(2);
205 angular_momentum_x().pow(&two) + angular_momentum_y().pow(&two) + angular_momentum_z().pow(&two)
206}
207
208#[must_use]
210pub fn angular_momentum_raising() -> Expression {
211 angular_momentum_x() + Expression::i() * angular_momentum_y()
212}
213
214#[must_use]
216pub fn angular_momentum_lowering() -> Expression {
217 angular_momentum_x() - Expression::i() * angular_momentum_y()
218}
219
220#[must_use]
226pub fn spin_half_x() -> Expression {
227 spin_x() / Expression::int(2)
228}
229
230#[must_use]
232pub fn spin_half_y() -> Expression {
233 spin_y() / Expression::int(2)
234}
235
236#[must_use]
238pub fn spin_half_z() -> Expression {
239 spin_z() / Expression::int(2)
240}
241
242#[must_use]
244pub fn spin_half_raising() -> Expression {
245 (spin_x() + Expression::i() * spin_y()) / Expression::int(2)
246}
247
248#[must_use]
250pub fn spin_half_lowering() -> Expression {
251 (spin_x() - Expression::i() * spin_y()) / Expression::int(2)
252}
253
254#[must_use]
260pub fn fock_state(n: u32) -> Expression {
261 Expression::new(format!("ket({n})"))
262}
263
264#[must_use]
266pub fn coherent_state(alpha: &Expression) -> Expression {
267 Expression::new(format!("coherent({alpha})"))
268}
269
270#[must_use]
272pub fn cat_state(alpha: &Expression, parity: bool) -> Expression {
273 let sign = if parity { "plus" } else { "minus" };
274 Expression::new(format!("cat_{sign}({alpha})"))
275}
276
277#[must_use]
279pub fn squeezed_vacuum(zeta: &Expression) -> Expression {
280 Expression::new(format!("squeezed_vacuum({zeta})"))
281}
282
283#[must_use]
285pub fn thermal_state(n_bar: &Expression) -> Expression {
286 Expression::new(format!("thermal({n_bar})"))
287}
288
289#[must_use]
295pub fn annihilation_mode(mode: u32) -> Expression {
296 Expression::symbol(&format!("a_{mode}"))
297}
298
299#[must_use]
301pub fn creation_mode(mode: u32) -> Expression {
302 Expression::symbol(&format!("a_dag_{mode}"))
303}
304
305#[must_use]
307pub fn number_operator_mode(mode: u32) -> Expression {
308 creation_mode(mode) * annihilation_mode(mode)
309}
310
311pub fn total_number_operator(num_modes: u32) -> Expression {
313 let mut total = Expression::zero();
314 for k in 0..num_modes {
315 total = total + number_operator_mode(k);
316 }
317 total
318}
319
320#[must_use]
326pub fn fermionic_annihilation_mode(mode: u32) -> Expression {
327 Expression::symbol(&format!("c_{mode}"))
328}
329
330#[must_use]
332pub fn fermionic_creation_mode(mode: u32) -> Expression {
333 Expression::symbol(&format!("c_dag_{mode}"))
334}
335
336#[must_use]
338pub fn fermionic_number_operator_mode(mode: u32) -> Expression {
339 fermionic_creation_mode(mode) * fermionic_annihilation_mode(mode)
340}
341
342pub fn total_fermionic_number_operator(num_modes: u32) -> Expression {
344 let mut total = Expression::zero();
345 for j in 0..num_modes {
346 total = total + fermionic_number_operator_mode(j);
347 }
348 total
349}
350
351pub fn jordan_wigner_string(mode: u32) -> Expression {
353 if mode == 0 {
354 return Expression::one();
355 }
356
357 let mut product = Expression::one();
358 for k in 0..mode {
359 let n_k = fermionic_number_operator_mode(k);
360 let factor = Expression::one() - Expression::int(2) * n_k;
361 product = product * factor;
362 }
363 product
364}
365
366#[must_use]
372pub fn harmonic_oscillator_hamiltonian(omega: &Expression) -> Expression {
373 let n = number_operator();
374 let half = Expression::float_unchecked(0.5);
375 omega.clone() * (n + half)
376}
377
378pub fn jaynes_cummings_hamiltonian(
380 omega_cavity: &Expression,
381 omega_atom: &Expression,
382 coupling: &Expression,
383) -> Expression {
384 let n = number_operator();
385 let a = annihilation();
386 let a_dag = creation();
387 let sz = spin_z();
388 let s_plus = spin_raising();
389 let s_minus = spin_lowering();
390 let half = Expression::float_unchecked(0.5);
391
392 let cavity_term = omega_cavity.clone() * n;
393 let atom_term = omega_atom.clone() * sz * half;
394 let interaction = coupling.clone() * (a_dag + a) * (s_plus + s_minus);
395
396 cavity_term + atom_term + interaction
397}
398
399#[must_use]
401pub fn kerr_hamiltonian(omega: &Expression, kerr_strength: &Expression) -> Expression {
402 let n = number_operator();
403 let two = Expression::int(2);
404 omega.clone() * n.clone() + kerr_strength.clone() * n.pow(&two)
405}
406
407pub fn beam_splitter_hamiltonian(theta: &Expression, mode_a: u32, mode_b: u32) -> Expression {
409 let a = annihilation_mode(mode_a);
410 let a_dag = creation_mode(mode_a);
411 let b = annihilation_mode(mode_b);
412 let b_dag = creation_mode(mode_b);
413
414 theta.clone() * (a_dag * b + a * b_dag)
415}
416
417pub fn two_mode_squeezing_hamiltonian(zeta: &Expression, mode_a: u32, mode_b: u32) -> Expression {
419 let a = annihilation_mode(mode_a);
420 let a_dag = creation_mode(mode_a);
421 let b = annihilation_mode(mode_b);
422 let b_dag = creation_mode(mode_b);
423
424 zeta.clone() * (a_dag * b_dag - a * b)
425}
426
427pub fn hubbard_interaction(u: &Expression, site: u32) -> Expression {
429 let n_up = fermionic_number_operator_mode(2 * site);
430 let n_down = fermionic_number_operator_mode(2 * site + 1);
431 u.clone() * n_up * n_down
432}
433
434pub fn hopping_term(t: &Expression, site_i: u32, site_j: u32, spin: u32) -> Expression {
436 let mode_i = 2 * site_i + spin;
437 let mode_j = 2 * site_j + spin;
438
439 let c_dag_i = fermionic_creation_mode(mode_i);
440 let c_i = fermionic_annihilation_mode(mode_i);
441 let c_dag_j = fermionic_creation_mode(mode_j);
442 let c_j = fermionic_annihilation_mode(mode_j);
443
444 t.clone().neg() * (c_dag_i * c_j + c_dag_j * c_i)
445}
446
447#[cfg(test)]
448mod tests {
449 use super::*;
450
451 #[test]
452 fn test_commutator() {
453 let a = Expression::symbol("A");
454 let b = Expression::symbol("B");
455 let comm = commutator(&a, &b);
456
457 let _expected = a.clone() * b.clone() - b * a;
459 assert!(!comm.is_symbol());
461 }
462
463 #[test]
464 fn test_bosonic_operators() {
465 let a = annihilation();
466 let a_dag = creation();
467 let n = number_operator();
468
469 assert!(a.is_symbol());
470 assert!(a_dag.is_symbol());
471 assert!(!n.is_symbol()); }
473
474 #[test]
475 fn test_displacement_operator() {
476 let alpha = Expression::symbol("alpha");
477 let d = displacement_operator(&alpha);
478
479 assert!(!d.is_symbol());
480 }
481
482 #[test]
483 fn test_angular_momentum() {
484 let jx = angular_momentum_x();
485 let jy = angular_momentum_y();
486 let jz = angular_momentum_z();
487 let j2 = angular_momentum_squared();
488
489 assert!(jx.is_symbol());
490 assert!(jy.is_symbol());
491 assert!(jz.is_symbol());
492 assert!(!j2.is_symbol());
493 }
494
495 #[test]
496 fn test_multi_mode_operators() {
497 let a0 = annihilation_mode(0);
498 let a1 = annihilation_mode(1);
499 let n_total = total_number_operator(3);
500
501 assert!(a0.is_symbol());
502 assert!(a1.is_symbol());
503 assert!(!n_total.is_symbol());
504 }
505
506 #[test]
507 fn test_fock_state() {
508 let ket0 = fock_state(0);
509 let ket5 = fock_state(5);
510
511 let ket0_str = ket0.to_string();
513 let ket5_str = ket5.to_string();
514
515 assert!(ket0_str.contains("ket") || ket0_str.contains('0'));
516 assert!(ket5_str.contains("ket") || ket5_str.contains('5'));
517 }
518
519 #[test]
520 fn test_jordan_wigner_string() {
521 let jw0 = jordan_wigner_string(0);
522 let jw1 = jordan_wigner_string(1);
523 let jw2 = jordan_wigner_string(2);
524
525 assert!(jw0.is_one());
526 assert!(!jw1.is_symbol());
527 assert!(!jw2.is_symbol());
528 }
529
530 #[test]
531 fn test_hamiltonians() {
532 let omega = Expression::symbol("omega");
533 let kerr = Expression::symbol("K");
534
535 let h_ho = harmonic_oscillator_hamiltonian(&omega);
536 let h_kerr = kerr_hamiltonian(&omega, &kerr);
537
538 assert!(!h_ho.is_symbol());
539 assert!(!h_kerr.is_symbol());
540 }
541
542 #[test]
543 fn test_beam_splitter() {
544 let theta = Expression::symbol("theta");
545 let h_bs = beam_splitter_hamiltonian(&theta, 0, 1);
546
547 assert!(!h_bs.is_symbol());
548 }
549}