Source code for netket.hilbert.spin
# Copyright 2021 The NetKet Authors - All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from fractions import Fraction
from typing import Optional, Union
import numpy as np
from netket.utils import StaticRange
from .homogeneous import HomogeneousHilbert
from .index.constraints import SumConstraint
def _check_total_sz(total_sz, S, size):
if total_sz is None:
return
local_size = 2 * S + 1
m = round(2 * total_sz)
if np.abs(m) > size * (2 * S):
raise ValueError(
"Cannot fix the total magnetization: 2|M| cannot " "exceed Nspins."
)
# If half-integer spins (1/2, 3/2)
if local_size % 2 == 0:
# Check that the total magnetization is odd if odd spins or even if even
# number of spins
if (size + m) % 2 != 0:
raise ValueError(
"Cannot fix the total magnetization: Nspins + 2*totalSz must be even."
)
# else if full-integer (S=1,2)
else:
if m % 2 != 0:
raise ValueError(
"Cannot fix the total magnetization to a half-integer number"
)
[docs]
class Spin(HomogeneousHilbert):
r"""Hilbert space obtained as tensor product of local spin states."""
[docs]
def __init__(
self,
s: float,
N: int = 1,
total_sz: Optional[float] = None,
):
r"""Hilbert space obtained as tensor product of local spin states.
Args:
s: Spin at each site. Must be integer or half-integer.
N: Number of sites (default=1)
total_sz: If given, constrains the total spin of system to a particular
value.
Examples:
Simple spin hilbert space.
>>> import netket as nk
>>> hi = nk.hilbert.Spin(s=1/2, N=4)
>>> print(hi.size)
4
"""
local_size = round(2 * s + 1)
local_states = np.empty(local_size)
assert int(2 * s + 1) == local_size
local_states = StaticRange(
1 - local_size, 2, local_size, dtype=np.int8 if local_size < 2**7 else int
)
_check_total_sz(total_sz, s, N)
if total_sz is not None:
constraints = SumConstraint(round(2 * total_sz))
else:
constraints = None
self._total_sz = total_sz
self._s = s
super().__init__(local_states, N, constraints)
def __pow__(self, n):
if not self.constrained:
return Spin(self._s, self.size * n)
return NotImplemented
def _mul_sametype_(self, other):
assert type(self) == type(other)
if self._s == other._s:
if not self.constrained and not other.constrained:
return Spin(s=self._s, N=self.size + other.size)
return NotImplemented
[docs]
def ptrace(self, sites: Union[int, list]) -> Optional["Spin"]:
if isinstance(sites, int):
sites = [sites]
for site in sites:
if site < 0 or site >= self.size:
raise ValueError(
f"Site {site} not in this hilbert space of site {self.size}"
)
if self._total_sz is not None:
raise TypeError(
"Cannot take the partial trace with a total magnetization constraint."
)
Nsites = len(sites)
if self.size - Nsites == 0:
return None
else:
return Spin(s=self._s, N=self.size - Nsites)
def __repr__(self):
total_sz = f", total_sz={self._total_sz}" if self._total_sz is not None else ""
return f"Spin(s={Fraction(self._s)}{total_sz}, N={self.size})"
@property
def _attrs(self):
return (self.size, self._s, self._total_sz)