# This code is part of qtealeaves.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
"""
Block (generic n-body rectangular) interactions in a two-dimensional system.
"""
import numpy as np
from qtealeaves.tooling import QTeaLeavesError, map_selector
from .baseterm import _ModelTerm
__all__ = ["BlockTerm2D"]
[docs]
class BlockTerm2D(_ModelTerm):
"""
The block term is applied to sites in a rectangle with shape l_x*l_y
in a 2d model. If symmetries are used, none of the operators
is allowed to change a symmetry sector (block diagonal).
**Arguments**
operators : 2d np.array of strings
String identifier for the operators. Before launching the simulation,
the python API will check that the operator is defined.
strength : str, callable, numeric (optional)
Defines the coupling strength of the local terms. It can
be parameterized via a value in the dictionary for the
simulation or a function.
Default to 1.
prefactor : numeric, scalar (optional)
Scalar prefactor, e.g., in order to take into account signs etc.
Default to 1.
has_obc : bool or list of bools, optional
Defines the boundary condition along each spatial dimension.
If scalar is given, the boundary condition along each
spatial dimension is assumed to be equal.
Default to True
If [False, True], the topology is a strip on the x axis
If [True, False], the topology is a strip on the y axis
If [False,False], the topology is a thorus
mask : callable or ``None``, optional
The true-false-mask allows to apply the Hamiltonians terms
only to specific plaquettes,
i.e., with true values. The function
takes the dictionary with the simulation parameters as an
argument. The mask is applied to the site where the lower-left
corner of the plaquette operator is acting on.
Default to ``None`` (all sites have the interaction)
The order of the operators is for the shifts (0,0), (0,1), (1,0), (1,1)
"""
# pylint: disable-next=too-many-arguments
def __init__(self, operators, strength=1, prefactor=1, has_obc=True, mask=None):
super().__init__()
self.op_shape = operators.shape
self.operators = operators.flatten()
self.strength = strength
self.prefactor = prefactor
if isinstance(mask, np.ndarray):
# pylint: disable-next=unused-argument
def mask_function(params, mask=mask):
return mask
mask = mask_function
self.mask = mask
# If volume is 1 we need to use a local operator instead.
if operators.shape[0] * operators.shape[1] == 1:
raise ValueError(
"The operator volume is 1. Please use modeling.LocalTerm for local operators."
)
# Will be set when adding Hamiltonian terms
self.map_type = None
if isinstance(has_obc, bool):
self.has_obc = [has_obc] * 2
else:
self.has_obc = has_obc
[docs]
@staticmethod
def check_dim(dim):
"""
Only available in 2d systems.
"""
if dim != 2:
raise QTeaLeavesError("Dimension does not match.")
[docs]
def collect_operators(self):
"""
All the required operators are returned to ensure that they are
written by fortran.
"""
for operator in self.operators:
yield operator, None
[docs]
def get_interactions(self, ll, params, **kwargs):
"""
Description of interactions close to the TPO formulation.
It works for both Periodic and Open Boundary conditions,
depending on the argument has_obc.
NOTE the order of the operators is
(x2,y2) ---- (x4,y4)
| |
| |
(x1,y1) ---- (x3,y3)
**Arguments**
ll : list of ints
Number of sites along the dimensions, i.e., not the
total number of sites. Assuming list of sites
along all dimension.
params : dictionary
Contains the simulation parameters.
"""
map_to_1d = map_selector(2, ll, self.map_type)
if self.mask is None:
local_mask = np.ones(ll, dtype=bool)
else:
local_mask = self.mask(params)
elem = {"operators": self.operators}
x_coords, y_coords = np.meshgrid(
range(self.op_shape[0]), range(self.op_shape[1]), indexing="ij"
)
if not self.has_obc[0]:
x_range = range(ll[0])
else:
x_range = range(ll[0] - self.op_shape[0] + 1)
if not self.has_obc[1]:
y_range = range(ll[1])
else:
y_range = range(ll[1] - self.op_shape[1] + 1)
for x_i in x_range:
for y_i in y_range:
coordinate_grid = np.array(
[(x_coords + x_i) % ll[0], (y_coords + y_i) % ll[1]]
)
if not local_mask[x_i, y_i]:
continue
coords_1d = [
map_to_1d[elem]
for elem in [
tuple(x_y)
for x_y in coordinate_grid.reshape(
2, self.op_shape[0] * self.op_shape[1]
).T
]
]
yield elem, coords_1d
[docs]
def get_fortran_str(self, ll, params, operator_map, param_map):
"""
Get the string representation needed to write
a Fortran input file.
**Arguments**
ll : int
Number of sites along one dimension in the system, e.g.,
number of sites for one side of the square in a 2d system.
params : dictionary
Contains the simulation parameters.
operator_map : OrderedDict
For operator string as dictionary keys, it returns the
corresponding integer IDs.
param_map : dict
This dictionary contains the mapping from the parameters
to integer identifiers.
"""
str_repr = ""
param_repr = self.get_param_repr(param_map)
op_str = [operator_map[(elem, None)] for elem in self.operators]
l_x, l_y = self.op_shape
op_volume = l_x * l_y
counter = 0
for _, coords in self.get_interactions(ll, params):
counter += 1
if len(set(coords)) < op_volume:
raise QTeaLeavesError("Same site ...")
inds = np.argsort(np.array(coords))
for ii in range(op_volume):
str_repr += "%d %d\n" % (coords[inds[ii]] + 1, op_str[inds[ii]])
str_repr += param_repr + " %30.15E\n" % (self.prefactor)
# Now we can write the information for the first lines (order is correct)
str_repr = "%d\n" % (op_volume) + str_repr
str_repr = "%d\n" % (counter) + str_repr
return str_repr