Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
contractions. Available backends are currently 'numpy', 'tensorflow',
'pytorch', 'jax'
"""
if center_position < 0 or center_position >= len(tensors):
raise ValueError(
'center_position = {} not between 0 <= center_position < {}'.format(
center_position, len(tensors)))
# we're no longer connecting MPS nodes because it's barely needed
# the dtype is deduced from the tensor object.
self.nodes = [
Node(tensors[n], backend=backend, name='node{}'.format(n))
for n in range(len(tensors))
]
self.connector_matrix = Node(
connector_matrix,
backend=backend) if connector_matrix is not None else connector_matrix
self.center_position = center_position
right_axis_names.append(node.axis_names[edge.axis1] if edge.node1 is node
else node.axis_names[edge.axis2])
left_axis_names.append(edge_name)
else:
left_axis_names = None
right_axis_names = None
backend = node.backend
node.reorder_edges(left_edges + right_edges)
q, r = backend.qr_decomposition(node.tensor, len(left_edges))
left_node = Node(
q, name=left_name, axis_names=left_axis_names, backend=backend)
for i, edge in enumerate(left_edges):
left_node.add_edge(edge, i)
edge.update_axis(i, node, i, left_node)
right_node = Node(
r, name=right_name, axis_names=right_axis_names, backend=backend)
for i, edge in enumerate(right_edges):
# i + 1 to account for the new edge.
right_node.add_edge(edge, i + 1)
edge.update_axis(i + len(left_edges), node, i + 1, right_node)
connect(left_node.edges[-1], right_node.edges[0], name=edge_name)
return left_node, right_node
# -- A*--
# and evolve it to the right by contracting tensors at site2 > site1
# if site2 is in `sites2`, calculate the observable
#
# ---A--........-- A--------
# | | | |
# | op1(site1) op2(site2)|
# | | | |
# ---A--........-- A*-------
for n in range(site1 + 1, n2 + 1):
if n in right_sites:
R = rs[n % N]
A = Node(self.nodes[n % N], backend=self.backend)
conj_A = conj(A)
O2 = Node(op2, backend=self.backend)
A[0] ^ L[0]
conj_A[0] ^ L[1]
O2[0] ^ conj_A[1]
O2[1] ^ A[1]
R[0] ^ A[2]
R[1] ^ conj_A[2]
res = L @ A @ O2 @ conj_A @ R
c.append(res.tensor)
if n < n2:
L = self.apply_transfer_operator(n % N, 'left', L)
return c
raise ValueError('site2 = {} has to be larger than site2 = {}'.format(
site2, site1))
if site2 != site1 + 1:
raise ValueError(
'Found site2 ={}, site1={}. Only nearest neighbor gates are currently '
'supported'.format(site2, site1))
if (max_singular_values or
max_truncation_err) and self.center_position not in (site1, site2):
raise ValueError(
'center_position = {}, but gate is applied at sites {}, {}. '
'Truncation should only be done if the gate '
'is applied at the center position of the MPS'.format(
self.center_position, site1, site2))
gate_node = Node(gate, backend=self.backend)
self.nodes[site1][2] ^ self.nodes[site2][0]
gate_node[2] ^ self.nodes[site1][1]
gate_node[3] ^ self.nodes[site2][1]
left_edges = [self.nodes[site1][0], gate_node[0]]
right_edges = [gate_node[1], self.nodes[site2][2]]
result = self.nodes[site1] @ self.nodes[site2] @ gate_node
U, S, V, tw = split_node_full_svd(
result,
left_edges=left_edges,
right_edges=right_edges,
max_singular_values=max_singular_values,
max_truncation_err=max_truncation_err,
left_name=self.nodes[site1].name,
right_name=self.nodes[site2].name)
V.reorder_edges([S[1]] + right_edges)
node_dict: A dictionary mapping the nodes to their copies.
edge_dict: A dictionary mapping the edges to their copies.
"""
node_dict = {}
for node in nodes:
if isinstance(node, CopyNode):
node_dict[node] = CopyNode(
node.rank,
node.dimension,
name=node.name,
axis_names=node.axis_names,
backend=node.backend,
dtype=node.dtype)
else:
if conjugate:
node_dict[node] = Node(
node.backend.conj(node.tensor),
name=node.name,
axis_names=node.axis_names,
backend=node.backend)
else:
node_dict[node] = Node(
node.tensor,
name=node.name,
axis_names=node.axis_names,
backend=node.backend)
edge_dict = {}
for edge in get_all_edges(nodes):
node1 = edge.node1
axis1 = edge.node1.get_axis_number(edge.axis1)
# edge dangling or node2 does not need to be copied
if edge.is_dangling() or edge.node2 not in node_dict:
if not hasattr(node, 'backend'):
raise TypeError('Node {} of type {} has no `backend`'.format(
node, type(node)))
if node1.backend.name != node2.backend.name:
raise ValueError("node {} and node {} have different backends. "
"Cannot perform outer product".format(node1, node2))
backend = node1.backend
if node1.get_rank() == 0 or node2.get_rank() == 0:
new_tensor = backend.multiply(node1.tensor, node2.tensor)
else:
new_tensor = backend.outer_product(node1.tensor, node2.tensor)
node1_axis_names = node1.axis_names
node2_axis_names = node2.axis_names
new_node = Node(
tensor=new_tensor, name=name, axis_names=axis_names, backend=backend)
additional_axes = len(node1.tensor.shape)
for i, edge in enumerate(node1.edges):
edge.update_axis(i, node1, i, new_node)
for i, edge in enumerate(node2.edges):
edge.update_axis(i, node2, i + additional_axes, new_node)
for i, edge in enumerate(node1.edges + node2.edges):
new_node.add_edge(edge, i, True)
node1.fresh_edges(node1_axis_names)
node2.fresh_edges(node2_axis_names)
return new_node
r.tensor /= self.backend.trace(r.tensor)
r.tensor = (r.tensor +
self.backend.transpose(self.backend.conj(r.tensor),
(1, 0))) / 2.0
# eigvals_left and u_left are both `Tensor` objects
eigvals_right, u_right = self.backend.eigh(r.tensor)
eigvals_right /= self.backend.norm(eigvals_right)
if pseudo_inverse_cutoff:
mask = eigvals_right <= pseudo_inverse_cutoff
inveigvals_right = 1.0 / eigvals_right
if pseudo_inverse_cutoff:
inveigvals_right = self.backend.index_update(inveigvals_right, mask, 0.0)
sqrtr = Node(
ncon([u_right,
self.backend.diag(self.backend.sqrt(eigvals_right))],
[[-1, 1], [1, -2]],
backend=self.backend.name),
backend=self.backend)
inv_sqrtr = Node(
ncon([
self.backend.diag(self.backend.sqrt(inveigvals_right)),
self.backend.conj(u_right)
], [[-1, 1], [-2, 1]],
backend=self.backend.name),
backend=self.backend)
tmp = Node(
ncon([sqrtl, sqrtr], [[-1, 1], [1, -2]], backend=self.backend.name),
Raises:
AttributeError: If `node` has no `backend` attribute, or if
`node` has no tensor.
ValueError: If either `permutation` is not the same as expected or
if you try to permute with a trace edge.
"""
if not hasattr(node, 'backend'):
raise AttributeError('Node {} of type {} has no `backend`'.format(
node, type(node)))
perm = [node.get_axis_number(p) for p in permutation]
if not axis_names:
axis_names = node.axis_names
new_node = Node(
node.tensor,
name=name,
axis_names=node.axis_names,
backend=node.backend)
return new_node.reorder_axes(perm)
for edge in right_edges:
right_axis_names.append(node.axis_names[edge.axis1] if edge.node1 is node
else node.axis_names[edge.axis2])
left_axis_names.append(edge_name)
else:
left_axis_names = None
right_axis_names = None
backend = node.backend
node.reorder_edges(left_edges + right_edges)
r, q = backend.rq_decomposition(node.tensor, len(left_edges))
left_node = Node(
r, name=left_name, axis_names=left_axis_names, backend=backend)
for i, edge in enumerate(left_edges):
left_node.add_edge(edge, i)
edge.update_axis(i, node, i, left_node)
right_node = Node(
q, name=right_name, axis_names=right_axis_names, backend=backend)
for i, edge in enumerate(right_edges):
# i + 1 to account for the new edge.
right_node.add_edge(edge, i + 1)
edge.update_axis(i + len(left_edges), node, i + 1, right_node)
connect(left_node.edges[-1], right_node.edges[0], name=edge_name)
return left_node, right_node