1"""
2Defines functions for constructing the Fermionic Fock skeleton out of second and
3fourth order terms in creation and anihilation operators.
4"""
5
6import numpy as np
7
8from ....hilbert_spaces.fermionic._hamming_weight_operations import \
9 hamming_weight as _hamming_weight
10from ._fermionic_fock_operations import is_empty as _is_empty,\
11 is_occupied as _is_occupied,\
12 select_orbitals as _select_orbitals,\
13 flips as _flips
14from ....hilbert_spaces import fermionic
15
[docs]
16def second_order_tensor(fock_space: fermionic.FermionSpace
17 ) -> np.ndarray[int]:
18 r"""
19 Generates a tensor given by the following expression
20 $$
21 t_{i,j,\alpha,\beta}\coloneqq \left(c^\dagger_ic_j\right)_{\alpha,\beta}
22 $$
23 where $c^\dagger_i$ and $c_i$ are the fermionic creation and anhilation
24 operators acting on the $i$th orbital, and $\alpha$ and $\beta$ are the rows
25 and columns of the matrix representation of the operator $c^\dagger_ic_j$ in
26 the Fock basis.
27
28 Parameters
29 ----------
30 fock_space: fermionic.FermionSpace
31 The fermionic Fock space
32
33 Returns
34 -------
35 NDArray[Shape[``fock_space.n_single_particle_states``, ``fock_space.n_single_particle_states``, ``fock_space.dim``, ``fock_space.dim``], int]
36 The second order skeleton tensor
37 """
38 t = np.zeros((fock_space.n_single_particle_states,
39 fock_space.n_single_particle_states,
40 fock_space.dim,
41 fock_space.dim),
42 dtype=int)
43 for j in range(fock_space.n_single_particle_states):
44 is_occupied = _is_occupied(fock_space.basis, j)
45 for i in range(j):
46 non_zero = np.copy(is_occupied)
47 non_zero[is_occupied] &= _is_empty(fock_space[is_occupied], i)
48 occupation_indices = fock_space.inverse[_flips(fock_space[non_zero], [i, j])]
49 slice = np.power(-1, _hamming_weight(_select_orbitals(fock_space[non_zero], i, j)))
50 t[i, j, occupation_indices, non_zero] = slice
51 t[j, i, non_zero, occupation_indices] = slice
52 t[j, j] = np.diag(is_occupied)
53 return t
54
[docs]
55def fourth_order_tensor(fock_space: fermionic.FermionSpace
56 ) -> np.ndarray[int]:
57 r"""
58 Generates a tensor given by the following expression
59 $$
60 u_{i,j,k,l,\alpha,\beta}\coloneqq \left(c^\dagger_ia^\dagger_ja_ka_l\right)_{\alpha,\beta}
61 $$
62 where $c^\dagger_i$ and $c_i$ are the fermionic creation and anhilation
63 operators acting on the $i$th orbital, and $\alpha$ and $\beta$ are the rows
64 and columns of the matrix representation of the operator $c^\dagger_ic_j$ in
65 the Fock basis.
66
67 Parameters
68 ----------
69 fock_space: FermionSpace
70 The fermionic Fock space
71
72 Returns
73 -------
74 NDArray[Shape[``fock_space.n_single_particle_states``, ``fock_space.n_single_particle_states``, ``fock_space.n_single_particle_states``, ``fock_space.n_single_particle_states``, ``fock_space.dim``, ``fock_space.dim``], int]
75 The fourth order skeleton tensor
76 """
77 u = np.zeros((fock_space.n_single_particle_states,
78 fock_space.n_single_particle_states,
79 fock_space.n_single_particle_states,
80 fock_space.n_single_particle_states,
81 fock_space.dim,
82 fock_space.dim),
83 dtype=int)
84 for l in range(fock_space.n_single_particle_states):
85 is_occupied = _is_occupied(fock_space.basis, l)
86 for k in range(l):
87 non_zero_k = np.copy(is_occupied)
88 non_zero_k[is_occupied] &= _is_occupied(fock_space[is_occupied], k)
89 for j in range(fock_space.n_single_particle_states):
90 if j in [k, l]:
91 non_zero_j = non_zero_k
92 else:
93 non_zero_j = np.copy(non_zero_k)
94 non_zero_j[non_zero_k] &= _is_empty(fock_space[non_zero_k], j)
95 for i in range(j):
96 if i in [k, l]:
97 non_zero = non_zero_j
98 else:
99 non_zero = np.copy(non_zero_j)
100 non_zero[non_zero_j] &= _is_empty(fock_space[non_zero_j], i)
101 occupation_indices = fock_space.inverse[_flips(fock_space[non_zero], [i, j, k, l])]
102 slice = np.power(-1, _hamming_weight(_select_orbitals(fock_space[non_zero], i, j))
103 +_hamming_weight(_select_orbitals(fock_space[non_zero], k, l))
104 +(i>k)
105 +(i>l)
106 +(j>k)
107 +(j>l)
108 )
109 u[i, j, k, l, occupation_indices, non_zero] = slice
110 u[i, j, l, k, occupation_indices, non_zero] = -slice
111 u[j, i, k, l, occupation_indices, non_zero] = -slice
112 u[j, i, l, k, occupation_indices, non_zero] = slice
113
114 u[k, l, i, j, non_zero, occupation_indices] = slice
115 u[l, k, i, j, non_zero, occupation_indices] = -slice
116 u[k, l, j, i, non_zero, occupation_indices] = -slice
117 u[l, k, j, i, non_zero, occupation_indices] = slice
118 return u