Source code for qugradlab.hilbert_spaces.fermionic._fermionic_hilbert_spaces

  1from typing import Optional, Iterable, Union
  2
  3import numpy as np
  4
  5from qugrad import HilbertSpace
  6
  7from ._hamming_weight_operations import only_low_lieing_states_occupied,\
  8                                        sub_hamming_weight_is, \
  9                                        any_sub_hamming_weight_is, \
 10                                        all_constant_hamming_weight
 11from .. import QuditSpace
 12from .._get_digit import get_digit
 13    
 14
[docs] 15class FermionSpace(HilbertSpace): 16 """ 17 Represents a Fermionic Fock space. 18 """ 19 20 _n_single_particle_states: int 21 "The number of single particle states a fermion can take on" 22
[docs] 23 def __init__(self, n_single_particle_states: int): 24 """ 25 Initialises a :class:`FermionSpace` 26 27 Parameters 28 ---------- 29 n_single_particle_states: int 30 The number of single particle states a fermion can take on 31 """ 32 self._n_single_particle_states = n_single_particle_states 33 super().__init__(np.arange(2**n_single_particle_states))
34 @property 35 def n_single_particle_states(self) -> int: 36 "The number of single particle states a fermion can take on" 37 return self._n_single_particle_states 38 @staticmethod 39 def _labels(digits: Iterable[str]) -> str: 40 """ 41 Generates a strings that represent the state specified by the `digits`. 42 43 Parameters 44 ---------- 45 digits : Iterable[str] 46 The digits representing the state 47 48 Returns 49 ------- 50 str 51 The label for the specified state. 52 """ 53 return f"|{''.join(digits)}⟩"
[docs] 54 def labels(self, 55 states: Optional[Union[int, list[int]]] = None 56 ) -> Union[str, list[str]]: 57 """ 58 Generates a string (list of strings) that represent the state(s). 59 60 Parameters 61 ---------- 62 states : int | list[int], optional 63 The state(s) to label. If ``None`` then the labels for all states in 64 :attr:`basis` are returned. By default ``None``. 65 66 Returns 67 ------- 68 str | list[str] 69 The label(s) for the specified states. 70 """ 71 if states is None: states = self.basis 72 digits = get_digit(states, 73 2, 74 np.arange(self._n_single_particle_states) 75 ).astype(str) 76 if isinstance(states, int): 77 return self._labels(digits) 78 return [self._labels(d) for d in digits]
79
[docs] 80class FixedParticleFermionSpace(FermionSpace): 81 """ 82 Represents a Fermionic Fock space constrained to have a fixed particle 83 number. 84 """ 85 86 _n_particles: int 87 "The number of particles" 88
[docs] 89 def __init__(self, n_single_particle_states: int, n_particles: int): 90 """ 91 Initialises a :class:`FixedParticleFermionSpace` 92 93 Parameters 94 ---------- 95 n_single_particle_states: int 96 The number of single particle states a fermion can take on 97 n_particles : int 98 The number of particles 99 """ 100 self._n_single_particle_states = n_single_particle_states 101 self._n_particles = n_particles 102 basis = all_constant_hamming_weight(n_single_particle_states, 103 n_particles) 104 HilbertSpace.__init__(self, list(basis))
105 @property 106 def n_particles(self) -> int: 107 "The number of particles" 108 return self._n_particles
109
[docs] 110class FermionQuditSpace(FixedParticleFermionSpace, QuditSpace): 111 """ 112 A :class:`FixedParticleFermionSpace` with a computational structure. The 113 Hilbert space is split into the tensor product of sites (qudits), with each 114 site hosting a specified number of levels. The computational subspace 115 consists of the single occupation states that only have particles occupying 116 the lowest two levels. 117 """ 118 119 _sites: int 120 "The number of sites (qudits)" 121 122 _levels_per_site: int 123 "The number of states per site (qudit)" 124
[docs] 125 def __init__(self, 126 sites: int, 127 levels_per_site: int, 128 n_particles: int): 129 """ 130 Initialises a :class:`FermionQuditSpace`. 131 132 Parameters 133 ---------- 134 sites : int 135 The number of sites (qudits) 136 levels_per_site : int 137 The number of states per site (qudit) 138 n_particles : int 139 The number of particles 140 """ 141 self._sites = sites 142 self._levels_per_site = levels_per_site 143 FixedParticleFermionSpace.__init__(self, 144 sites * levels_per_site, 145 n_particles)
146 @property 147 def sites(self) -> int: 148 "The number of sites (qudits)" 149 return self._sites 150 @property 151 def levels_per_site(self) -> int: 152 "The number of states per site (qudit)" 153 return self._levels_per_site
[docs] 154 def computational_projector(self) -> np.ndarray[bool]: 155 """ 156 Generates a boolean filter for the computation basis states in 157 :attr:`basis`. The computational subspace consists of the single 158 occupation states that only have particles occupying the lowest two 159 levels. 160 161 Returns 162 ------- 163 NDArray[Shape[:attr:`dim`], bool] 164 A boolean filter for the computation basis states in :attr:`basis`. 165 """ 166 return only_low_lieing_states_occupied(2, 167 self.levels_per_site, 168 self._sites, 169 self.basis) \ 170 & self.single_occupation_states()
[docs] 171 def single_occupation_states(self) -> np.ndarray[bool]: 172 """ 173 Returns a boolean array indicating whether each of the :attr:`basis` 174 states is a single occupation state (each site has exactly one 175 particle). 176 177 Returns 178 ------- 179 NDArray[Shape[:attr:`dim`], bool] 180 A boolean array indicating whether each of the :attr:`basis` 181 states is a single occupation state. 182 """ 183 return sub_hamming_weight_is(1, 184 self.levels_per_site, 185 self._sites, 186 self.basis)
[docs] 187 def n_occupation_states(self, occupation: int) -> np.ndarray[bool]: 188 """ 189 Returns a boolean array indicating whether each of the :attr:`basis` 190 states has at most the specified occupation. 191 192 Parameters 193 ---------- 194 occupation : int 195 The occupation to check the :attr:`basis` states for. 196 197 Returns 198 ------- 199 NDArray[Shape[:attr:`dim`], bool] 200 A boolean array indicating whether each of the :attr:`basis` 201 states has at most the specified occupation. 202 203 Note 204 ---- 205 :meth:`single_occupation_states` is only equivalent to 206 ``n_occupation_states(1)`` when :attr:`n_particles` is greater than or 207 equal to :attr:`sites`. 208 """ 209 projector = any_sub_hamming_weight_is(occupation, 210 self.levels_per_site, 211 self._sites, 212 self.basis) 213 for n in range(occupation+1,self.n_particles+1): 214 projector &= np.logical_not( 215 any_sub_hamming_weight_is(n, 216 self.levels_per_site, 217 self._sites, 218 self.basis)) 219 return projector