修改库
Some checks failed
Build wheels / build (ubuntu-latest, 3.11) (push) Has been cancelled
Build wheels / build (ubuntu-latest, 3.12) (push) Has been cancelled
Build wheels / build (ubuntu-latest, 3.13) (push) Has been cancelled
Tests / check (push) Has been cancelled
Tests / build (ubuntu-latest, 3.11) (push) Has been cancelled
Tests / build (ubuntu-latest, 3.12) (push) Has been cancelled
Tests / build (ubuntu-latest, 3.13) (push) Has been cancelled
Some checks failed
Build wheels / build (ubuntu-latest, 3.11) (push) Has been cancelled
Build wheels / build (ubuntu-latest, 3.12) (push) Has been cancelled
Build wheels / build (ubuntu-latest, 3.13) (push) Has been cancelled
Tests / check (push) Has been cancelled
Tests / build (ubuntu-latest, 3.11) (push) Has been cancelled
Tests / build (ubuntu-latest, 3.12) (push) Has been cancelled
Tests / build (ubuntu-latest, 3.13) (push) Has been cancelled
This commit is contained in:
@@ -1288,28 +1288,9 @@ class QteaTorchTensor(_AbstractQteaBaseTensor):
|
||||
To be able to work with all ranks, we currently avoid the numpy
|
||||
syntax in our implementation.
|
||||
"""
|
||||
lists = []
|
||||
for ii, corner_ii in enumerate(corner_low):
|
||||
corner_jj = corner_high[ii]
|
||||
lists.append(list(range(corner_ii, corner_jj)))
|
||||
|
||||
shape = self.elem.shape
|
||||
cdim = np.cumprod(np.array(shape[::-1], dtype=int))[::-1]
|
||||
cdim = np.array(list(cdim[1:]) + [1], dtype=int)
|
||||
|
||||
# Reshape does not make a copy, but points to memory (unlike flatten)
|
||||
self_1d = self.elem.reshape(-1)
|
||||
sub_1d = tensor.elem.reshape(-1)
|
||||
|
||||
kk = -1
|
||||
for elem in itertools.product(*lists):
|
||||
kk += 1
|
||||
elem = np.array(elem, dtype=int)
|
||||
idx = np.sum(elem * cdim)
|
||||
|
||||
self_1d[idx] = sub_1d[kk]
|
||||
|
||||
# self._elem never changed shape, we are done
|
||||
slices = tuple(slice(lo, hi) for lo, hi in zip(corner_low, corner_high))
|
||||
shape = tuple(hi - lo for lo, hi in zip(corner_low, corner_high))
|
||||
self._elem[slices] = tensor.elem.reshape(shape)
|
||||
|
||||
def to_dense(self, true_copy=False):
|
||||
"""Return dense tensor (if `true_copy=False`, same object may be returned)."""
|
||||
|
||||
@@ -0,0 +1,416 @@
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Generic base class for operators.
|
||||
"""
|
||||
|
||||
# pylint: disable=too-many-locals
|
||||
|
||||
# pylint: disable-next=no-name-in-module
|
||||
from collections import OrderedDict
|
||||
|
||||
import numpy as np
|
||||
|
||||
from qtealeaves.tooling.operatorstrings import _op_string_mul
|
||||
from qtealeaves.tooling.parameterized import _ParameterizedClass
|
||||
|
||||
__all__ = ["TNOperators"]
|
||||
|
||||
|
||||
class _DefaultMapping:
|
||||
"""Callable default mapping to avoid recreating closures during lookups."""
|
||||
|
||||
def __init__(self, default_key):
|
||||
self.default_key = default_key
|
||||
|
||||
# pylint: disable-next=unused-argument
|
||||
def __call__(self, site_idx):
|
||||
return self.default_key
|
||||
|
||||
|
||||
class TNOperators(_ParameterizedClass):
|
||||
"""
|
||||
Generic class to write operators. This class contains no pre-defined
|
||||
operators. It allows you to start from scratch if no other operator
|
||||
class fulfills your needs.
|
||||
|
||||
**Arguments**
|
||||
|
||||
set_names : list of str, optional
|
||||
Name of the operators sets.
|
||||
Default to `default`
|
||||
|
||||
mapping_func : callable (or `None`), optional
|
||||
Mapping the site index to an operator. Arguments
|
||||
`site_idx` must be accepted.
|
||||
Default to `None` (default mapping to only operator set)
|
||||
"""
|
||||
|
||||
def __init__(self, set_names="default", mapping_func=None):
|
||||
if isinstance(set_names, str):
|
||||
set_names = [set_names]
|
||||
|
||||
self._ops_dicts = {}
|
||||
for name in set_names:
|
||||
if not isinstance(name, str):
|
||||
raise TypeError(f"Set names must be str, but got `{type(name)}`.")
|
||||
self._ops_dicts[name] = OrderedDict()
|
||||
self._set_names = tuple(self._ops_dicts.keys())
|
||||
self._one_unique = len(self._set_names) == 1
|
||||
|
||||
if mapping_func is None:
|
||||
self._mapping_func = _DefaultMapping(self._set_names[0])
|
||||
else:
|
||||
self._mapping_func = mapping_func
|
||||
|
||||
# Mapping of operators (to avoid equal operators being defined twice)
|
||||
# Can be set, e.g., for 2nd order operators.
|
||||
self._has_2nd_order = False
|
||||
self._mapping_op = {}
|
||||
|
||||
@property
|
||||
def one_unique(self):
|
||||
"""Flag if only one operators set exists (True) or multiple (False)."""
|
||||
return self._one_unique
|
||||
|
||||
@property
|
||||
def mapping_func(self):
|
||||
"""Mapping function for site to operator set name."""
|
||||
return self._mapping_func
|
||||
|
||||
@property
|
||||
def set_names(self):
|
||||
"""Return operator set names as list of strings."""
|
||||
return list(self._set_names)
|
||||
|
||||
def __len__(self):
|
||||
"""Lenght of TNOperators defined as number of operator sets."""
|
||||
return len(self._ops_dicts)
|
||||
|
||||
def __contains__(self, key):
|
||||
"""Check if a key is inside the operators."""
|
||||
key_a, key_b = self._parse_key(key)
|
||||
if key_a not in self._ops_dicts:
|
||||
return False
|
||||
|
||||
return key_b in self._ops_dicts[key_a]
|
||||
|
||||
def __delitem__(self, key):
|
||||
"""Delete entry in operators."""
|
||||
key_a, key_b = self._parse_key(key)
|
||||
del self._ops_dicts[key_a][key_b]
|
||||
|
||||
def __getitem__(self, key):
|
||||
"""Extract entry by key."""
|
||||
key_a, key_b = self._parse_key(key)
|
||||
return self._ops_dicts[key_a][key_b]
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
"""Set entry by key."""
|
||||
key_a, key_b = self._parse_key(key, callee_set=True)
|
||||
self._ops_dicts[key_a][key_b] = value
|
||||
|
||||
def __iter__(self):
|
||||
"""Iterate through all keys (of all operators sets)."""
|
||||
for key, value in self._ops_dicts.items():
|
||||
for subkey in value:
|
||||
yield (key, subkey)
|
||||
|
||||
def items(self):
|
||||
"""Iterate throught all (key, value) pairs of all operators sets."""
|
||||
for key, value in self._ops_dicts.items():
|
||||
for subkey, subvalue in value.items():
|
||||
yield (key, subkey), subvalue
|
||||
|
||||
def _parse_key(self, key, callee_set=False):
|
||||
"""
|
||||
Parse the key and split into operator set key and operator name key.
|
||||
|
||||
**Arguments**
|
||||
|
||||
key : tuple (or str)
|
||||
Key as tuple of length two (or operator name).
|
||||
|
||||
callee_set : bool, optional
|
||||
Indicate if callee is `__setitem__`.
|
||||
Default to `False`.
|
||||
"""
|
||||
if isinstance(key, str) and self.one_unique:
|
||||
return self._set_names[0], key
|
||||
|
||||
if isinstance(key, str):
|
||||
raise ValueError("Operators are not unique, indicate index.")
|
||||
|
||||
if len(key) != 2:
|
||||
raise ValueError("Operators are not unique, indicate index.")
|
||||
|
||||
if isinstance(key[0], str):
|
||||
# str for operator set name
|
||||
key_0 = key[0]
|
||||
elif isinstance(key[0], (int, np.int64)) and callee_set:
|
||||
raise ValueError("Cannot set entry via integer entry (per site).")
|
||||
elif isinstance(key[0], (int, np.int64)):
|
||||
# int for site, use mapping
|
||||
# pylint: disable-next=not-callable
|
||||
key_0 = self.mapping_func(key[0])
|
||||
else:
|
||||
raise ValueError(f"First entry must be set name or int, but `{key[0]}`.")
|
||||
|
||||
if not isinstance(key[1], str):
|
||||
raise ValueError(
|
||||
f"Second entry must specify operator name, but `{key[1]}`."
|
||||
)
|
||||
|
||||
return key_0, key[1]
|
||||
|
||||
def get_operator(self, site_idx_1d, operator_name, params):
|
||||
"""
|
||||
Provide a method to return any operator, either defined via
|
||||
a callable or directly as a matrix.
|
||||
|
||||
**Arguments**
|
||||
|
||||
site_idx_1d : int, str
|
||||
If int, site where we need the operator. Mapping will evaluate what
|
||||
to return.
|
||||
If str, name of operator set.
|
||||
|
||||
operator_name : str
|
||||
Tag/identifier of the operator.
|
||||
|
||||
params : dict
|
||||
Simulation parameters as a dictionary; dict is passed
|
||||
to callable.
|
||||
"""
|
||||
if isinstance(site_idx_1d, (int, np.int64)):
|
||||
# pylint: disable-next=not-callable
|
||||
key_0 = self._mapping_func(site_idx_1d)
|
||||
else:
|
||||
key_0 = site_idx_1d
|
||||
op_mat = self.eval_numeric_param(self._ops_dicts[key_0][operator_name], params)
|
||||
return op_mat
|
||||
|
||||
def get_local_links(self, num_sites, params):
|
||||
"""
|
||||
Extract the local links from the operators.
|
||||
|
||||
**Arguments**
|
||||
|
||||
num_sites : integer
|
||||
Number of sites.
|
||||
|
||||
params : dict
|
||||
Dictionary with parameterization of the simulation.
|
||||
"""
|
||||
local_links = []
|
||||
for ii in range(num_sites):
|
||||
eye = self.get_operator(ii, "id", params)
|
||||
|
||||
if hasattr(eye, "links"):
|
||||
local_links.append(eye.links[1])
|
||||
else:
|
||||
# When constructing H, we call this with numpy tensors
|
||||
local_links.append(eye.shape[0])
|
||||
|
||||
return local_links
|
||||
|
||||
def transform(self, transformation, **kwargs):
|
||||
"""
|
||||
Generate a new :class:`TNOperators` by transforming the
|
||||
current instance.
|
||||
|
||||
**Arguments**
|
||||
|
||||
transformation : callable
|
||||
Accepting key and value as arguments plus potential
|
||||
keyword arguments.
|
||||
|
||||
**kwargs : key-word arguments
|
||||
Will be passed to `transformation`
|
||||
"""
|
||||
new_ops = TNOperators(set_names=self.set_names, mapping_func=self.mapping_func)
|
||||
for key, value in self.items():
|
||||
new_ops[key] = transformation(key, value, **kwargs)
|
||||
|
||||
return new_ops
|
||||
|
||||
def check_alternative_op(self, set_name, key):
|
||||
"""
|
||||
Check entry for alternative operators, i.e., the sigma_x squared
|
||||
is the identity.
|
||||
|
||||
Arguments
|
||||
---------
|
||||
|
||||
set_name : str
|
||||
Search in this set name of operators. (Set names allow
|
||||
different Hilbert spaces on different sites.)
|
||||
|
||||
key : str
|
||||
Operator represented as key. Check if there is an alternative
|
||||
key for this key.
|
||||
|
||||
Returns
|
||||
-------
|
||||
|
||||
alternative_key : None | str
|
||||
If `None`, no alternative key is given or the corresponding
|
||||
dictionary for checking is not set. If str, then this operator
|
||||
has the same representation as `key`.
|
||||
"""
|
||||
set_dict = self._mapping_op.get(set_name, None)
|
||||
if set_dict is None:
|
||||
return None
|
||||
|
||||
return self._mapping_op[set_name].get(key, None)
|
||||
|
||||
# pylint: disable-next=too-many-branches
|
||||
def generate_products_2nd_order(
|
||||
self,
|
||||
left_conj=False,
|
||||
left_transpose=False,
|
||||
right_conj=False,
|
||||
right_transpose=False,
|
||||
):
|
||||
"""
|
||||
Generate all possible multiplications (matrix-matrix multiplications) of
|
||||
the operator set, i.e., [A, B, ...] generators [A*A, A*B, B*A, B*B, ...].
|
||||
Transformation can be taken into account on top.
|
||||
|
||||
Arguments
|
||||
---------
|
||||
|
||||
left_conj : Boolean
|
||||
Tells if the left operator needs to be complex conjugated.
|
||||
Default is False.
|
||||
|
||||
right_transpose : Boolean
|
||||
Tells if the left operator needs to be transposed.
|
||||
Default is False.
|
||||
|
||||
right_conj : Boolean
|
||||
Tells if the right operator needs to be complex conjugated.
|
||||
Default is False.
|
||||
|
||||
right_transpose : Boolean
|
||||
Tells if the right operator needs to be transposed.
|
||||
Default is False.
|
||||
|
||||
Returns
|
||||
-------
|
||||
|
||||
None (in-place update of the operator dictionary)
|
||||
|
||||
"""
|
||||
if self._has_2nd_order:
|
||||
return
|
||||
|
||||
self._has_2nd_order = True
|
||||
|
||||
additional_ops = {}
|
||||
for set_name in self.set_names:
|
||||
additional_ops[set_name] = {}
|
||||
|
||||
for op_str_a, op_a in self._ops_dicts[set_name].items():
|
||||
for op_str_b, op_b in self._ops_dicts[set_name].items():
|
||||
op_str = _op_string_mul("", op_str_a, left_conj, left_transpose)
|
||||
op_str = _op_string_mul(
|
||||
op_str, op_str_b, right_conj, right_transpose
|
||||
)
|
||||
op_str = op_str_a + "*" + op_str_b
|
||||
if (op_str_a == "id") and (op_str_b == "id"):
|
||||
additional_ops[set_name][op_str] = "id"
|
||||
elif (
|
||||
(op_str_a == "id")
|
||||
and (not right_conj)
|
||||
and (not right_transpose)
|
||||
):
|
||||
additional_ops[set_name][op_str] = op_str_b
|
||||
elif (
|
||||
(op_str_b == "id") and (not left_conj) and (not left_transpose)
|
||||
):
|
||||
additional_ops[set_name][op_str] = op_str_a
|
||||
elif op_a.has_symmetry:
|
||||
tmp_a = _op_transformation(op_a, left_conj, left_transpose)
|
||||
tmp_b = _op_transformation(op_b, right_conj, right_transpose)
|
||||
|
||||
op = tmp_a.tensordot(tmp_b, [(2,), (1,)])
|
||||
_, op = op.split_qr([0, 3, 1, 4], [2, 5])
|
||||
op, _ = op.split_rq([0, 1], [2, 3, 4])
|
||||
additional_ops[set_name][op_str] = op
|
||||
else:
|
||||
tmp_a = _op_transformation(op_a, left_conj, left_transpose)
|
||||
tmp_b = _op_transformation(op_b, right_conj, right_transpose)
|
||||
|
||||
op = tmp_a.einsum("ijkl,akbd->ijbl", tmp_b)
|
||||
additional_ops[set_name][op_str] = op
|
||||
|
||||
# Check they are really new and not identical to existing ones
|
||||
# to get the smallest set
|
||||
to_be_added = {}
|
||||
to_be_reset = {}
|
||||
for key, op in additional_ops[set_name].items():
|
||||
if isinstance(op, str):
|
||||
continue
|
||||
|
||||
for key_ii, op_ii in self._ops_dicts[set_name].items():
|
||||
if op.are_equal(op_ii, tol=10 * op.dtype_eps):
|
||||
to_be_reset[key] = key_ii
|
||||
else:
|
||||
to_be_added[key] = op
|
||||
continue
|
||||
|
||||
for key, value in to_be_added.items():
|
||||
self._ops_dicts[set_name][key] = value
|
||||
|
||||
for key, value in to_be_reset.items():
|
||||
additional_ops[set_name][key] = value
|
||||
|
||||
for set_name, set_dict in additional_ops.items():
|
||||
if set_name not in self._mapping_op:
|
||||
self._mapping_op[set_name] = {}
|
||||
|
||||
for key, value in set_dict.items():
|
||||
self._mapping_op[set_name][key] = value
|
||||
|
||||
|
||||
def _op_transformation(op, is_conj, is_transpose):
|
||||
"""
|
||||
Carry out the transformation on an operator.
|
||||
|
||||
Arguments
|
||||
---------
|
||||
|
||||
op : :class:`_AbstractQteaTensor`
|
||||
Tensor to be transformed. We assume rank-4 tensors.
|
||||
|
||||
is_conj : bool
|
||||
Flag if conjugate is applied.
|
||||
|
||||
is_transpose : bool
|
||||
Flag if transpose is applied.
|
||||
|
||||
Returns
|
||||
-------
|
||||
|
||||
new_op : :class:`_AbstractQteaTensor`
|
||||
Operator after the transformations.
|
||||
"""
|
||||
if is_conj and is_transpose:
|
||||
new_op = op.conj().transpose([0, 2, 1, 3])
|
||||
elif is_conj:
|
||||
new_op = op.conj()
|
||||
elif is_transpose:
|
||||
new_op = op.transpose([0, 2, 1, 3])
|
||||
else:
|
||||
new_op = op
|
||||
|
||||
return new_op
|
||||
1918
.venv/lib/python3.12/site-packages/qtealeaves/tooling/mapping.py
Normal file
1918
.venv/lib/python3.12/site-packages/qtealeaves/tooling/mapping.py
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,55 @@
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Common permutations often used in tensor network methods.
|
||||
"""
|
||||
from functools import lru_cache
|
||||
|
||||
__all__ = []
|
||||
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def _transpose_idx1(num_legs, contracted_idx):
|
||||
"""Move second last index instead of last in `_transpose_idx`."""
|
||||
return _transpose_idx(num_legs - 1, contracted_idx) + (num_legs - 1,)
|
||||
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def _transpose_idx2(num_legs, contracted_idx):
|
||||
"""Move third last index instead of last in `_transpose_idx`."""
|
||||
return _transpose_idx(num_legs - 2, contracted_idx) + (
|
||||
num_legs - 2,
|
||||
num_legs - 1,
|
||||
)
|
||||
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def _transpose_idx(num_legs, contracted_idx):
|
||||
"""
|
||||
Transpose in the original order the indexes
|
||||
of a n-legs tensor contracted over the
|
||||
index `contracted_idx`
|
||||
|
||||
Parameters
|
||||
----------
|
||||
contracted_idx : int
|
||||
Index over which there has been a contraction
|
||||
|
||||
Returns
|
||||
-------
|
||||
tuple
|
||||
Indexes for the transposition
|
||||
"""
|
||||
if contracted_idx > num_legs - 1:
|
||||
raise ValueError(
|
||||
f"Cannot contract leg {contracted_idx} of tensor with {num_legs} legs"
|
||||
)
|
||||
return (*range(contracted_idx), num_legs - 1, *range(contracted_idx, num_legs - 1))
|
||||
5305
.venv/lib/python3.12/site-packages/quimb/tensor/circuit.py
Normal file
5305
.venv/lib/python3.12/site-packages/quimb/tensor/circuit.py
Normal file
File diff suppressed because it is too large
Load Diff
11987
.venv/lib/python3.12/site-packages/quimb/tensor/tensor_core.py
Normal file
11987
.venv/lib/python3.12/site-packages/quimb/tensor/tensor_core.py
Normal file
File diff suppressed because it is too large
Load Diff
@@ -5050,7 +5050,8 @@ class TNLinearOperator1D(spla.LinearOperator):
|
||||
|
||||
if self.is_conj:
|
||||
T = T.conj()
|
||||
|
||||
print(T)
|
||||
assert(0)
|
||||
return T.to_dense(self.left_inds, self.right_inds)
|
||||
|
||||
def toarray(self):
|
||||
|
||||
Reference in New Issue
Block a user