quantrs2_symengine_pure/quantum/
operators.rs

1//! Quantum operator algebra.
2
3use crate::error::SymEngineResult;
4use crate::expr::{ExprLang, Expression};
5
6/// Commutator [A, B] = AB - BA
7#[must_use]
8pub fn commutator(a: &Expression, b: &Expression) -> Expression {
9    a.clone() * b.clone() - b.clone() * a.clone()
10}
11
12/// Anticommutator {A, B} = AB + BA
13#[must_use]
14pub fn anticommutator(a: &Expression, b: &Expression) -> Expression {
15    a.clone() * b.clone() + b.clone() * a.clone()
16}
17
18/// Tensor product (Kronecker product) of two operators
19pub fn tensor_product(a: &Expression, b: &Expression) -> Expression {
20    Expression::new(format!("kronecker({a}, {b})"))
21}
22
23/// Matrix trace (sum of diagonal elements)
24pub fn trace(matrix: &Expression) -> Expression {
25    Expression::new(format!("trace({matrix})"))
26}
27
28/// Hermitian conjugate (dagger) operation
29pub fn dagger(expr: &Expression) -> Expression {
30    Expression::new(format!("conjugate(transpose({expr}))"))
31}
32
33/// Check if an operator is Hermitian (A = A†)
34///
35/// Returns the difference A - A†, which should be zero for Hermitian operators.
36pub fn is_hermitian(matrix: &Expression) -> Expression {
37    matrix.clone() - dagger(matrix)
38}
39
40/// Check if an operator is unitary (A†A = I)
41///
42/// Returns the product A†A, which should equal identity for unitary operators.
43pub fn is_unitary(matrix: &Expression) -> Expression {
44    dagger(matrix) * matrix.clone()
45}
46
47/// Matrix transpose
48pub fn transpose(matrix: &Expression) -> Expression {
49    Expression::new(format!("transpose({matrix})"))
50}
51
52/// Matrix determinant
53pub fn determinant(matrix: &Expression) -> Expression {
54    Expression::new(format!("det({matrix})"))
55}
56
57/// Matrix inverse
58pub fn inverse(matrix: &Expression) -> Expression {
59    Expression::new(format!("inverse({matrix})"))
60}
61
62/// Matrix exponential (e^A)
63pub fn matrix_exp(matrix: &Expression) -> Expression {
64    crate::ops::trig::exp(matrix)
65}
66
67/// Bosonic creation operator a†
68#[must_use]
69pub fn creation() -> Expression {
70    Expression::symbol("a_dag")
71}
72
73/// Bosonic annihilation operator a
74#[must_use]
75pub fn annihilation() -> Expression {
76    Expression::symbol("a")
77}
78
79/// Number operator n = a†a
80#[must_use]
81pub fn number_operator() -> Expression {
82    creation() * annihilation()
83}
84
85/// Position operator x = (a + a†)/√2
86#[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/// Momentum operator p = i(a† - a)/√2
95#[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/// Fermionic creation operator c†
104#[must_use]
105pub fn fermionic_creation() -> Expression {
106    Expression::symbol("c_dag")
107}
108
109/// Fermionic annihilation operator c
110#[must_use]
111pub fn fermionic_annihilation() -> Expression {
112    Expression::symbol("c")
113}
114
115/// Fermionic number operator n = c†c
116#[must_use]
117pub fn fermionic_number_operator() -> Expression {
118    fermionic_creation() * fermionic_annihilation()
119}
120
121/// Displacement operator D(α) = exp(αa† - α*a)
122#[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/// Squeezing operator S(ζ) = exp[(ζ*a² - ζa†²)/2]
131#[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/// Spin raising operator S+ = Sx + iSy
143#[must_use]
144pub fn spin_raising() -> Expression {
145    Expression::symbol("S_plus")
146}
147
148/// Spin lowering operator S- = Sx - iSy
149#[must_use]
150pub fn spin_lowering() -> Expression {
151    Expression::symbol("S_minus")
152}
153
154/// Spin x-component operator Sx
155#[must_use]
156pub fn spin_x() -> Expression {
157    Expression::symbol("S_x")
158}
159
160/// Spin y-component operator Sy
161#[must_use]
162pub fn spin_y() -> Expression {
163    Expression::symbol("S_y")
164}
165
166/// Spin z-component operator Sz
167#[must_use]
168pub fn spin_z() -> Expression {
169    Expression::symbol("S_z")
170}
171
172/// Spin squared operator S² = Sx² + Sy² + Sz²
173#[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// =========================================================================
180// Angular Momentum Operators
181// =========================================================================
182
183/// Angular momentum x-component Jx
184#[must_use]
185pub fn angular_momentum_x() -> Expression {
186    Expression::symbol("J_x")
187}
188
189/// Angular momentum y-component Jy
190#[must_use]
191pub fn angular_momentum_y() -> Expression {
192    Expression::symbol("J_y")
193}
194
195/// Angular momentum z-component Jz
196#[must_use]
197pub fn angular_momentum_z() -> Expression {
198    Expression::symbol("J_z")
199}
200
201/// Angular momentum squared J² = Jx² + Jy² + Jz²
202#[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/// Angular momentum raising operator J+ = Jx + iJy
209#[must_use]
210pub fn angular_momentum_raising() -> Expression {
211    angular_momentum_x() + Expression::i() * angular_momentum_y()
212}
213
214/// Angular momentum lowering operator J- = Jx - iJy
215#[must_use]
216pub fn angular_momentum_lowering() -> Expression {
217    angular_momentum_x() - Expression::i() * angular_momentum_y()
218}
219
220// =========================================================================
221// Spin-1/2 Operators (Pauli basis)
222// =========================================================================
223
224/// Spin-1/2 x-operator (σx/2)
225#[must_use]
226pub fn spin_half_x() -> Expression {
227    spin_x() / Expression::int(2)
228}
229
230/// Spin-1/2 y-operator (σy/2)
231#[must_use]
232pub fn spin_half_y() -> Expression {
233    spin_y() / Expression::int(2)
234}
235
236/// Spin-1/2 z-operator (σz/2)
237#[must_use]
238pub fn spin_half_z() -> Expression {
239    spin_z() / Expression::int(2)
240}
241
242/// Spin-1/2 raising operator (σ+/2)
243#[must_use]
244pub fn spin_half_raising() -> Expression {
245    (spin_x() + Expression::i() * spin_y()) / Expression::int(2)
246}
247
248/// Spin-1/2 lowering operator (σ-/2)
249#[must_use]
250pub fn spin_half_lowering() -> Expression {
251    (spin_x() - Expression::i() * spin_y()) / Expression::int(2)
252}
253
254// =========================================================================
255// Fock State Operations
256// =========================================================================
257
258/// Fock state |n⟩
259#[must_use]
260pub fn fock_state(n: u32) -> Expression {
261    Expression::new(format!("ket({n})"))
262}
263
264/// Coherent state |α⟩ = D(α)|0⟩
265#[must_use]
266pub fn coherent_state(alpha: &Expression) -> Expression {
267    Expression::new(format!("coherent({alpha})"))
268}
269
270/// Cat state |cat±⟩ = N(|α⟩ ± |-α⟩)
271#[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/// Squeezed vacuum state S(ζ)|0⟩
278#[must_use]
279pub fn squeezed_vacuum(zeta: &Expression) -> Expression {
280    Expression::new(format!("squeezed_vacuum({zeta})"))
281}
282
283/// Thermal state density matrix
284#[must_use]
285pub fn thermal_state(n_bar: &Expression) -> Expression {
286    Expression::new(format!("thermal({n_bar})"))
287}
288
289// =========================================================================
290// Multi-mode Operators
291// =========================================================================
292
293/// Multi-mode annihilation operator a_k
294#[must_use]
295pub fn annihilation_mode(mode: u32) -> Expression {
296    Expression::symbol(&format!("a_{mode}"))
297}
298
299/// Multi-mode creation operator a†_k
300#[must_use]
301pub fn creation_mode(mode: u32) -> Expression {
302    Expression::symbol(&format!("a_dag_{mode}"))
303}
304
305/// Multi-mode number operator n_k = a†_k a_k
306#[must_use]
307pub fn number_operator_mode(mode: u32) -> Expression {
308    creation_mode(mode) * annihilation_mode(mode)
309}
310
311/// Total number operator N = Σ_k n_k
312pub 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// =========================================================================
321// Fermionic Algebra
322// =========================================================================
323
324/// Multi-mode fermionic annihilation operator c_j
325#[must_use]
326pub fn fermionic_annihilation_mode(mode: u32) -> Expression {
327    Expression::symbol(&format!("c_{mode}"))
328}
329
330/// Multi-mode fermionic creation operator c†_j
331#[must_use]
332pub fn fermionic_creation_mode(mode: u32) -> Expression {
333    Expression::symbol(&format!("c_dag_{mode}"))
334}
335
336/// Fermionic number operator for mode j: n_j = c†_j c_j
337#[must_use]
338pub fn fermionic_number_operator_mode(mode: u32) -> Expression {
339    fermionic_creation_mode(mode) * fermionic_annihilation_mode(mode)
340}
341
342/// Total fermionic number operator N = Σ_j c†_j c_j
343pub 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
351/// Jordan-Wigner string for fermionic mode j: Π_{k<j} (1 - 2n_k)
352pub 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// =========================================================================
367// Hamiltonian Builders
368// =========================================================================
369
370/// Harmonic oscillator Hamiltonian: H = ℏω(n + 1/2)
371#[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
378/// Jaynes-Cummings Hamiltonian (without RWA): H = ωc a†a + ωa σz/2 + g(a† + a)(σ+ + σ-)
379pub 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/// Single-mode Kerr Hamiltonian: H = ℏω a†a + ℏK (a†a)²
400#[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
407/// Beam splitter Hamiltonian: H = θ(a†b + ab†)
408pub 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
417/// Two-mode squeezing Hamiltonian: H = ζ(a†b† - ab)
418pub 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
427/// Hubbard model single-site term: U n_↑ n_↓
428pub 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
434/// Hopping term: -t (c†_i c_j + c†_j c_i)
435pub 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        // [A, B] = AB - BA
458        let _expected = a.clone() * b.clone() - b * a;
459        // Structural comparison
460        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()); // n = a†a is a product
472    }
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        // Fock states are represented as symbolic expressions, not raw symbols
512        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}