"""Fermionic quantum operators.""" from sympy.core.numbers import Integer from sympy.core.singleton import S from sympy.physics.quantum import Operator from sympy.physics.quantum import HilbertSpace, Ket, Bra from sympy.functions.special.tensor_functions import KroneckerDelta __all__ = [ 'FermionOp', 'FermionFockKet', 'FermionFockBra' ] class FermionOp(Operator): """A fermionic operator that satisfies {c, Dagger(c)} == 1. Parameters ========== name : str A string that labels the fermionic mode. annihilation : bool A bool that indicates if the fermionic operator is an annihilation (True, default value) or creation operator (False) Examples ======== >>> from sympy.physics.quantum import Dagger, AntiCommutator >>> from sympy.physics.quantum.fermion import FermionOp >>> c = FermionOp("c") >>> AntiCommutator(c, Dagger(c)).doit() 1 """ @property def name(self): return self.args[0] @property def is_annihilation(self): return bool(self.args[1]) @classmethod def default_args(self): return ("c", True) def __new__(cls, *args, **hints): if not len(args) in [1, 2]: raise ValueError('1 or 2 parameters expected, got %s' % args) if len(args) == 1: args = (args[0], S.One) if len(args) == 2: args = (args[0], Integer(args[1])) return Operator.__new__(cls, *args) def _eval_commutator_FermionOp(self, other, **hints): if 'independent' in hints and hints['independent']: # [c, d] = 0 return S.Zero return None def _eval_anticommutator_FermionOp(self, other, **hints): if self.name == other.name: # {a^\dagger, a} = 1 if not self.is_annihilation and other.is_annihilation: return S.One elif 'independent' in hints and hints['independent']: # {c, d} = 2 * c * d, because [c, d] = 0 for independent operators return 2 * self * other return None def _eval_anticommutator_BosonOp(self, other, **hints): # because fermions and bosons commute return 2 * self * other def _eval_commutator_BosonOp(self, other, **hints): return S.Zero def _eval_adjoint(self): return FermionOp(str(self.name), not self.is_annihilation) def _print_contents_latex(self, printer, *args): if self.is_annihilation: return r'{%s}' % str(self.name) else: return r'{{%s}^\dagger}' % str(self.name) def _print_contents(self, printer, *args): if self.is_annihilation: return r'%s' % str(self.name) else: return r'Dagger(%s)' % str(self.name) def _print_contents_pretty(self, printer, *args): from sympy.printing.pretty.stringpict import prettyForm pform = printer._print(self.args[0], *args) if self.is_annihilation: return pform else: return pform**prettyForm('\N{DAGGER}') class FermionFockKet(Ket): """Fock state ket for a fermionic mode. Parameters ========== n : Number The Fock state number. """ def __new__(cls, n): if n not in (0, 1): raise ValueError("n must be 0 or 1") return Ket.__new__(cls, n) @property def n(self): return self.label[0] @classmethod def dual_class(self): return FermionFockBra @classmethod def _eval_hilbert_space(cls, label): return HilbertSpace() def _eval_innerproduct_FermionFockBra(self, bra, **hints): return KroneckerDelta(self.n, bra.n) def _apply_from_right_to_FermionOp(self, op, **options): if op.is_annihilation: if self.n == 1: return FermionFockKet(0) else: return S.Zero else: if self.n == 0: return FermionFockKet(1) else: return S.Zero class FermionFockBra(Bra): """Fock state bra for a fermionic mode. Parameters ========== n : Number The Fock state number. """ def __new__(cls, n): if n not in (0, 1): raise ValueError("n must be 0 or 1") return Bra.__new__(cls, n) @property def n(self): return self.label[0] @classmethod def dual_class(self): return FermionFockKet