import numpy as np
import tensornetwork as tn
from tensornetwork import U1Charge, BlockSparseTensor
from ttnopt.src.TwoSiteUpdater import TwoSiteUpdaterMixin
[docs]class TwoSiteUpdaterSparse(TwoSiteUpdaterMixin):
def __init__(self, psi):
self.psi = psi
self.backend = "symmetric"
self.flag = self.initial_flag()
self.distance = self.initial_distance()
[docs] def decompose_two_tensors(
self,
psi,
max_bond_dim,
opt_structure=0,
epsilon=1e-8,
delta=0.1,
temperature=0.0,
):
edge_order = [0, 1, 2, 3]
psi_last = psi.copy()
if not opt_structure:
a = psi[0]
b = psi[1]
c = psi[2]
d = psi[3]
e = psi[4]
(u, s, v, _) = tn.split_node_full_svd(psi, [e, a, b], [c, d])
p = s.tensor.data
p = np.sort(p)[::-1]
p = p[p > 0.0]
ind = np.min([max_bond_dim, len(p)])
if ind < len(p):
while ind > 1:
if (np.abs(p[ind] - p[ind - 1]) / (p[ind - 1] + e)) < delta:
ind -= 1
else:
break
else:
candidates = [[0, 1, 2, 3], [2, 1, 0, 3], [0, 2, 1, 3]]
candidate_index = 0
ps = []
inds = []
for edges in candidates:
psi_ = psi.copy()
a = psi_[edges[0]]
b = psi_[edges[1]]
c = psi_[edges[2]]
d = psi_[edges[3]]
e = psi_[4]
(u_, s_, v_, terr) = tn.split_node_full_svd(psi_, [e, a, b], [c, d])
p_ = s_.tensor.data
p_ = np.sort(p_)[::-1]
p_ = p_[p_ > 0.0]
ps.append(p_)
# diagonal
ind = np.min([max_bond_dim, len(p_)])
if ind < len(p_):
while ind > 1:
if (np.abs(p_[ind] - p_[ind - 1]) / p_[ind]) < delta:
ind -= 1
else:
break
inds.append(ind)
ees = [self.entanglement_entropy(probability=p) for p in ps]
p_truncates = [p[:max_bond_dim] for p in ps]
errors = [1.0 - np.real(np.sum(p_t**2)) for p_t in p_truncates]
if opt_structure == 1:
if temperature == 0.0:
candidate_index = np.argmin(ees)
else:
weights = np.array(ees) / temperature
weights = np.exp(-weights)
weights = np.array(weights) / np.sum(weights)
candidate_index = np.random.choice(len(ees), p=weights)
elif opt_structure == 2:
candidate_index = np.argmin(errors)
if np.isclose(errors[candidate_index], errors[0], atol=1e-14):
candidate_index = np.argmin(ees)
if np.isclose(ees[candidate_index], ees[0], atol=epsilon):
candidate_index = 0
edge_order = candidates[candidate_index]
p = ps[candidate_index]
ind = inds[candidate_index]
a = psi_last[edge_order[0]]
b = psi_last[edge_order[1]]
c = psi_last[edge_order[2]]
d = psi_last[edge_order[3]]
e = psi_last[4]
(u, s, v, terr) = tn.split_node_full_svd(
psi_last, [e, a, b], [c, d], max_singular_values=ind
)
u = u.reorder_edges([u[1], u[2], u[3], u[0]])
v = v.reorder_edges([v[1], v[2], v[0]])
u_tensor = u.tensor
u_tensor.contiguous(inplace=True)
s_tensor = s.tensor / np.linalg.norm(s.tensor.data)
s_tensor.contiguous(inplace=True)
v_tensor = v.tensor
v_tensor.contiguous(inplace=True)
p_truncate = s.tensor.data
s = tn.Node(s_tensor, backend=self.backend)
u = tn.Node(u_tensor, backend=self.backend)
a, ss, b, terr = tn.split_node_full_svd(
u,
[
u[0],
u[1],
],
[u[2], u[3]],
)
u_tensor = a.tensor
u_tensor.contiguous(inplace=True)
u_tensor = BlockSparseTensor(
data=u_tensor.data,
charges=[
U1Charge(u_tensor.flat_charges[0].charges.flatten()),
U1Charge(u_tensor.flat_charges[1].charges.flatten()),
U1Charge(-u_tensor.flat_charges[2].charges.flatten()),
],
flows=[True, True, False],
)
s[0] ^ b[1]
s = tn.contractors.auto([s, b], output_edge_order=[b[0], s[1], b[2]])
s_tensor = s.tensor
s_tensor.contiguous(inplace=True)
s_tensor = BlockSparseTensor(
data=s_tensor.data,
charges=[
U1Charge(-s_tensor.flat_charges[0].charges.flatten()),
U1Charge(s_tensor.flat_charges[1].charges.flatten()),
U1Charge(s_tensor.flat_charges[2].charges.flatten()),
],
flows=[True, True, False],
)
err = 1.0 - np.real(np.sum(p_truncate**2))
return (
u_tensor,
s_tensor,
v_tensor,
p,
err,
edge_order,
)
[docs] def entanglement_entropy_at_physical_bond(self, psi, psi_edges):
ee_at_physical_bond = {}
for i, edge in enumerate(psi_edges):
if edge in self.psi.physical_edges:
ee_at_physical_bond[edge] = 0.0
for edge_id in ee_at_physical_bond.keys():
psi_ = psi.copy()
matching_index = list({i for i, v in enumerate(psi_edges) if v == edge_id})[
0
]
non_matching_indices = list(
{i for i in range(len(psi_edges)) if i != matching_index}
)
psi_dag = psi_.copy(conjugate=True)
for i in non_matching_indices:
psi_[i] ^ psi_dag[i]
psi_[4] ^ psi_dag[4]
rho = tn.contractors.auto(
[psi_, psi_dag],
output_edge_order=[psi_[matching_index], psi_dag[matching_index]],
)
(u, s, v, terr) = tn.split_node_full_svd(rho, [rho[0]], [rho[1]])
p_ = s.tensor.data
p_ = p_[p_ > 0.0]
p_ = np.sqrt(p_)
ee_at_physical_bond[edge_id] = self.entanglement_entropy(p_)
return ee_at_physical_bond
[docs] def set_ttn_properties_at_one_tensor(self, edge_id, selected_tensor_id):
# update_ttn_properties
self.psi.canonical_center_edge_id = edge_id
out_selected_inds = []
for i, e in enumerate(self.psi.edges[selected_tensor_id]):
if e == edge_id:
canonical_center_ind = i
else:
out_selected_inds.append(i)
self.psi.tensors[selected_tensor_id] = self.psi.tensors[
selected_tensor_id
].transpose(
out_selected_inds + [canonical_center_ind] + [3],
)
self.psi.edges[selected_tensor_id] = [
self.psi.edges[selected_tensor_id][i] for i in out_selected_inds
] + [edge_id]
for i, e in enumerate(self.psi.edges[selected_tensor_id]):
self.psi.edge_dims[e] = self.psi.tensors[selected_tensor_id].shape[i]
return