Skip to content

Commit

Permalink
Merge pull request #2166 from khnikhil/master
Browse files Browse the repository at this point in the history
creating fermionic creation and annihilation operators
  • Loading branch information
BoxiLi committed Jun 2, 2023
2 parents 27d3dcd + f0c0919 commit 199394b
Show file tree
Hide file tree
Showing 4 changed files with 177 additions and 8 deletions.
2 changes: 1 addition & 1 deletion doc/apidoc/functions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Quantum Operators
-----------------

.. automodule:: qutip.core.operators
:members: charge, commutator, create, destroy, displace, enr_destroy, enr_identity, jmat, num, qeye, identity, momentum, phase, position, qdiags, qutrit_ops, qzero, sigmam, sigmap, sigmax, sigmay, sigmaz, spin_Jx, spin_Jy, spin_Jz, spin_Jm, spin_Jp, squeeze, squeezing, tunneling
:members: charge, commutator, create, destroy, displace, enr_destroy, enr_identity, fcreate, fdestroy, jmat, num, qeye, identity, momentum, phase, position, qdiags, qutrit_ops, qzero, sigmam, sigmap, sigmax, sigmay, sigmaz, spin_Jx, spin_Jy, spin_Jz, spin_Jm, spin_Jp, squeeze, squeezing, tunneling


.. _functions-rand:
Expand Down
4 changes: 4 additions & 0 deletions doc/changes/2166.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
added fermionic annihilation and creation operators.

Closely followed the protocol proposed in the following `source
<https://github.com/qutip/qutip/issues/863>`_.
156 changes: 149 additions & 7 deletions qutip/core/operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@

__all__ = ['jmat', 'spin_Jx', 'spin_Jy', 'spin_Jz', 'spin_Jm', 'spin_Jp',
'spin_J_set', 'sigmap', 'sigmam', 'sigmax', 'sigmay', 'sigmaz',
'destroy', 'create', 'qeye', 'qeye_like', 'identity', 'position',
'momentum', 'num', 'squeeze', 'squeezing', 'swap', 'displace',
'commutator', 'qutrit_ops', 'qdiags', 'phase', 'qzero',
'qzero_like', 'enr_destroy', 'enr_identity', 'charge', 'tunneling',
'qft']
'destroy', 'create', 'fdestroy', 'fcreate', 'qeye', 'qeye_like',
'identity', 'position', 'momentum', 'num', 'squeeze', 'squeezing',
'swap', 'displace', 'commutator', 'qutrit_ops', 'qdiags', 'phase',
'qzero', 'qzero_like', 'enr_destroy', 'enr_identity', 'charge',
'tunneling', 'qft']

import numbers

Expand Down Expand Up @@ -135,11 +135,11 @@ def jmat(j, which=None, *, dtype=None):
return Qobj(_jplus(j, dtype=dtype).adjoint(), dims=dims, type='oper',
isherm=False, isunitary=False, copy=False)
if which == 'x':
A = _jplus(j, dtype=dtype)
A = _jplus(j, dtype=dtype)
return Qobj(_data.add(A, A.adjoint()), dims=dims, type='oper',
isherm=True, isunitary=False, copy=False) * 0.5
if which == 'y':
A = _data.mul(_jplus(j, dtype=dtype), -0.5j)
A = _data.mul(_jplus(j, dtype=dtype), -0.5j)
return Qobj(_data.add(A, A.adjoint()), dims=dims, type='oper',
isherm=True, isunitary=False, copy=False)
if which == 'z':
Expand Down Expand Up @@ -471,6 +471,148 @@ def create(N, offset=0, *, dtype=None):
return qdiags(data, -1, dtype=dtype)


def fdestroy(n_sites, site, dtype=None):
"""
Fermionic destruction operator.
We use the Jordan-Wigner transformation,
making use of the Jordan-Wigner ZZ..Z strings,
to construct this as follows:
.. math::
a_j = \\sigma_z^{\\otimes j} \\otimes
(\\frac{\\sigma_x + i \\sigma_y}{2})
\\otimes I^{\\otimes N-j-1}
Parameters
----------
n_sites : int
Number of sites in Fock space.
site : int (default 0)
The site in Fock space to add a fermion to.
Corresponds to j in the above JW transform.
Returns
-------
oper : qobj
Qobj for destruction operator.
Examples
--------
>>> fdestroy(2) # doctest: +SKIP
Quantum object: dims=[[2 2], [2 2]], shape=(4, 4), \
type='oper', isherm=False
Qobj data =
[[0. 0. 1. 0.]
[0. 0. 0. 1.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]]
"""
return _f_op(n_sites, site, 'destruction', dtype=dtype)


def fcreate(n_sites, site, dtype=None):
"""
Fermionic creation operator.
We use the Jordan-Wigner transformation,
making use of the Jordan-Wigner ZZ..Z strings,
to construct this as follows:
.. math::
a_j = \\sigma_z^{\\otimes j}
\\otimes (frac{sigma_x - i sigma_y}{2})
\\otimes I^{\\otimes N-j-1}
Parameters
----------
n_sites : int
Number of sites in Fock space.
site : int
The site in Fock space to add a fermion to.
Corresponds to j in the above JW transform.
Returns
-------
oper : qobj
Qobj for raising operator.
Examples
--------
>>> fcreate(2) # doctest: +SKIP
Quantum object: dims = [[2, 2], [2, 2]], shape = (4, 4), \
type = oper, isherm = False
Qobj data =
[[0. 0. 0. 0.]
[0. 0. 0. 0.]
[1. 0. 0. 0.]
[0. 1. 0. 0.]]
"""
return _f_op(n_sites, site, 'creation', dtype=dtype)


def _f_op(n_sites, site, action, dtype=None):
""" Makes fermionic creation and destruction operators.
We use the Jordan-Wigner transformation,
making use of the Jordan-Wigner ZZ..Z strings,
to construct this as follows:
.. math::
a_j = \\sigma_z^{\\otimes j}
\\otimes (frac{sigma_x \\pm i sigma_y}{2})
\\otimes I^{\\otimes N-j-1}
Parameters
----------
action : str
The type of operator to build.
Can only be 'creation' or 'destruction'
n_sites : int
Number of sites in Fock space.
site : int
The site in Fock space to create/destroy a fermion on.
Corresponds to j in the above JW transform.
Returns
-------
oper : qobj
Qobj for destruction operator.
"""
# get `tensor` and sigma z objects
from .tensor import tensor
s_z = 2 * jmat(0.5, 'z', dtype=dtype)

# sanity check
if site < 0:
raise ValueError(f'The specified site {site} cannot be \
less than 0.')
elif 0 >= n_sites:
raise ValueError(f'The specified number of sites {n_sites} \
cannot be equal to or less than 0.')
elif site >= n_sites:
raise ValueError(f'The specified site {site} is not in \
the range of {n_sites} sites.')

# figure out which operator to build
if action.lower() == 'creation':
operator = create(2, dtype=dtype)
elif action.lower() == 'destruction':
operator = destroy(2, dtype=dtype)
else:
raise TypeError("Unknown operator '%s'. `action` must be \
either 'creation' or 'destruction.'" % action)

eye = identity(2, dtype=dtype)
opers = [s_z] * site + [operator] + [eye] * (n_sites - site - 1)
return tensor(opers)


def _implicit_tensor_dimensions(dimensions):
"""
Total flattened size and operator dimensions for operator creation routines
Expand Down
23 changes: 23 additions & 0 deletions qutip/tests/core/test_operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,8 @@ def _id_func(val):
(qutip.spin_Jp, (1,)),
(qutip.destroy, (5,)),
(qutip.create, (5,)),
(qutip.fdestroy, (5, 0)),
(qutip.fcreate, (5, 0)),
(qutip.qzero, (5,)),
(qutip.qeye, (5,)),
(qutip.position, (5,)),
Expand Down Expand Up @@ -342,3 +344,24 @@ def test_qzero_like(dims, superrep, dtype):
opevo = qutip.QobjEvo(op)
new = qutip.qzero_like(op)
assert new == expected


@pytest.mark.parametrize('n_sites', [2, 3, 4, 5])
def test_fcreate_fdestroy(n_sites):
identity = qutip.identity([2] * n_sites)
zero_tensor = qutip.qzero([2] * n_sites)
for site_0 in range(n_sites):
c_0 = qutip.fcreate(n_sites, site_0)
d_0 = qutip.fdestroy(n_sites, site_0)
for site_1 in range(n_sites):
c_1 = qutip.fcreate(n_sites, site_1)
d_1 = qutip.fdestroy(n_sites, site_1)
assert qutip.commutator(c_0, c_1, 'anti') == zero_tensor
assert qutip.commutator(d_0, d_1, 'anti') == zero_tensor
if site_0 == site_1:
assert qutip.commutator(c_0, d_1, 'anti') == identity
assert qutip.commutator(c_1, d_0, 'anti') == identity
else:
assert qutip.commutator(c_0, d_1, 'anti') == zero_tensor
assert qutip.commutator(c_1, d_0, 'anti') == zero_tensor
assert qutip.commutator(identity, c_0) == zero_tensor

0 comments on commit 199394b

Please sign in to comment.