Source code for qugradlab.systems.skeletons.fermionic.fermionic_fock_skeleton

  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