From aafbc20890ed5409cb04446a97e65c668bfaac26 Mon Sep 17 00:00:00 2001 From: Hao Zhang Date: Fri, 8 Sep 2023 15:06:44 +0800 Subject: [PATCH] [TAT.py] Add tests for TAT.py. These tests is nearly the same to tests for TAT.hpp. --- PyTAT/tests/old_test/check_aux.py | 73 ------- PyTAT/tests/old_test/random_test_contract.py | 53 ----- PyTAT/tests/old_test/random_test_fuse.py | 78 ------- PyTAT/tests/old_test/random_test_qr.py | 46 ---- PyTAT/tests/old_test/random_test_svd.py | 45 ---- PyTAT/tests/old_test/random_test_trace.py | 58 ----- PyTAT/tests/test_arrow_and_parity.py | 8 + PyTAT/tests/test_callable.py | 5 + PyTAT/tests/test_clear_symmetry.py | 47 ++++ PyTAT/tests/test_conjugate.py | 173 +++++++++++++++ PyTAT/tests/test_contract.py | 213 +++++++++++++++++++ PyTAT/tests/test_contract_with_numpy.py | 37 ++++ PyTAT/tests/test_create_fermi_tensor.py | 109 ++++++++++ PyTAT/tests/test_create_normal_tensor.py | 61 ++++++ PyTAT/tests/test_create_symmetry_tensor.py | 92 ++++++++ PyTAT/tests/test_edge.py | 82 +++++++ PyTAT/tests/test_edge_operator.py | 77 +++++++ PyTAT/tests/test_edge_rename.py | 14 ++ PyTAT/tests/test_exponential.py | 52 +++++ PyTAT/tests/test_fuse_with_numpy.py | 63 ++++++ PyTAT/tests/test_identity.py | 112 ++++++++++ PyTAT/tests/test_module_alias.py | 12 ++ PyTAT/tests/test_norm.py | 14 ++ PyTAT/tests/test_qr.py | 103 +++++++++ PyTAT/tests/test_qr_with_numpy.py | 32 +++ PyTAT/tests/test_random.py | 19 ++ PyTAT/tests/test_scalar.py | 116 ++++++++++ PyTAT/tests/test_split_and_merge.py | 100 +++++++++ PyTAT/tests/test_split_to_no_edge.py | 11 + PyTAT/tests/test_svd.py | 128 +++++++++++ PyTAT/tests/test_svd_with_numpy.py | 31 +++ PyTAT/tests/test_symmetry.py | 65 ++++++ PyTAT/tests/test_trace.py | 106 +++++++++ PyTAT/tests/test_trace_with_numpy.py | 44 ++++ PyTAT/tests/test_transform_map_and_set.py | 39 ++++ PyTAT/tests/test_transpose.py | 139 ++++++++++++ PyTAT/tests/test_type_conversion.py | 38 ++++ tests/test_contract.cpp | 1 + 38 files changed, 2143 insertions(+), 353 deletions(-) delete mode 100644 PyTAT/tests/old_test/check_aux.py delete mode 100644 PyTAT/tests/old_test/random_test_contract.py delete mode 100644 PyTAT/tests/old_test/random_test_fuse.py delete mode 100644 PyTAT/tests/old_test/random_test_qr.py delete mode 100644 PyTAT/tests/old_test/random_test_svd.py delete mode 100644 PyTAT/tests/old_test/random_test_trace.py create mode 100644 PyTAT/tests/test_arrow_and_parity.py create mode 100644 PyTAT/tests/test_callable.py create mode 100644 PyTAT/tests/test_clear_symmetry.py create mode 100644 PyTAT/tests/test_conjugate.py create mode 100644 PyTAT/tests/test_contract.py create mode 100644 PyTAT/tests/test_contract_with_numpy.py create mode 100644 PyTAT/tests/test_create_fermi_tensor.py create mode 100644 PyTAT/tests/test_create_normal_tensor.py create mode 100644 PyTAT/tests/test_create_symmetry_tensor.py create mode 100644 PyTAT/tests/test_edge.py create mode 100644 PyTAT/tests/test_edge_operator.py create mode 100644 PyTAT/tests/test_edge_rename.py create mode 100644 PyTAT/tests/test_exponential.py create mode 100644 PyTAT/tests/test_fuse_with_numpy.py create mode 100644 PyTAT/tests/test_identity.py create mode 100644 PyTAT/tests/test_module_alias.py create mode 100644 PyTAT/tests/test_norm.py create mode 100644 PyTAT/tests/test_qr.py create mode 100644 PyTAT/tests/test_qr_with_numpy.py create mode 100644 PyTAT/tests/test_random.py create mode 100644 PyTAT/tests/test_scalar.py create mode 100644 PyTAT/tests/test_split_and_merge.py create mode 100644 PyTAT/tests/test_split_to_no_edge.py create mode 100644 PyTAT/tests/test_svd.py create mode 100644 PyTAT/tests/test_svd_with_numpy.py create mode 100644 PyTAT/tests/test_symmetry.py create mode 100644 PyTAT/tests/test_trace.py create mode 100644 PyTAT/tests/test_trace_with_numpy.py create mode 100644 PyTAT/tests/test_transform_map_and_set.py create mode 100644 PyTAT/tests/test_transpose.py create mode 100644 PyTAT/tests/test_type_conversion.py diff --git a/PyTAT/tests/old_test/check_aux.py b/PyTAT/tests/old_test/check_aux.py deleted file mode 100644 index 9a8a44ccb..000000000 --- a/PyTAT/tests/old_test/check_aux.py +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# Copyright (C) 2020-2023 Hao Zhang -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# - -import TAT -import tetragono as tet - -Tensor = TAT(float) - -M = 4 -N = 4 - -dimension_virtual = 10 - -TAT.random.seed(0) - - -def _initialize_tensor_in_network(i: int, j: int): - name_list = [] - dimension_list = [] - if i != 0: - name_list.append("U") - dimension_list.append(dimension_virtual) - if j != 0: - name_list.append("L") - dimension_list.append(dimension_virtual) - if i != M - 1: - name_list.append("D") - dimension_list.append(dimension_virtual) - if j != N - 1: - name_list.append("R") - dimension_list.append(dimension_virtual) - return Tensor(name_list, dimension_list).randn() - - -lattice = [[_initialize_tensor_in_network(i, j) for j in range(N)] for i in range(M)] -vector = Tensor(1) -for i in range(M): - for j in range(N): - if i == M - 1 and j == N - 1: - break - vector = vector.contract(lattice[i][j].edge_rename({"D": f"D-{j}"}), {("R", "L"), (f"D-{j}", "U")}) - vector /= vector.norm_max() - -r2 = vector.edge_rename({f"D-{N - 1}": "U0", "R": "L0"}) -del vector -r2 /= r2.norm_max() - -print(r2) - -for Dc in range(2, 100): - aux = tet.auxiliaries.SingleLayerAuxiliaries(M, N, cut_dimension=Dc, normalize=False, Tensor=Tensor) - for i in range(M): - for j in range(N): - aux[i, j] = lattice[i][j] - - r1 = aux.hole(((M - 1, N - 1),)).edge_rename({"R0": "L0", "D0": "U0"}) - r1 /= r1.norm_max() - print(Dc, (r1 - r2).norm_max() / r2.norm_max()) diff --git a/PyTAT/tests/old_test/random_test_contract.py b/PyTAT/tests/old_test/random_test_contract.py deleted file mode 100644 index 339924b45..000000000 --- a/PyTAT/tests/old_test/random_test_contract.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# Copyright (C) 2020-2023 Hao Zhang -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# - -import numpy as np -from TAT.No.D import Tensor -import TAT - -max_random = 8 - -for _ in range(100): - rank_A = np.random.randint(2, max_random) - rank_B = np.random.randint(2, max_random) - rank_contract = np.random.randint(1, np.min([rank_A, rank_B])) - # print(rank_A, rank_B, rank_contract) - - contract_name_A = np.random.choice(range(rank_A), rank_contract, False) - contract_name_B = np.random.choice(range(rank_B), rank_contract, False) - - dim_A = np.random.randint(1, max_random, size=rank_A) - dim_B = np.random.randint(1, max_random, size=rank_B) - dim_contract = np.random.randint(1, max_random, size=rank_contract) - - dim_A = [ - int(j if i not in contract_name_A else dim_contract[contract_name_A.tolist().index(i)]) - for i, j in enumerate(dim_A) - ] - dim_B = [ - int(j if i not in contract_name_B else dim_contract[contract_name_B.tolist().index(i)]) - for i, j in enumerate(dim_B) - ] - - A = Tensor([f"A.{i}" for i in range(rank_A)], dim_A).randn() - B = Tensor([f"B.{i}" for i in range(rank_B)], dim_B).randn() - v_t = A.contract(B, {(f"A.{i}", f"B.{j}") for i, j in zip(contract_name_A, contract_name_B)}) - v_t = v_t.blocks[v_t.names] - v_n = np.tensordot(A.blocks[A.names], B.blocks[B.names], [contract_name_A, contract_name_B]) - v_d = v_t - v_n - print(np.max(np.abs(v_d))) diff --git a/PyTAT/tests/old_test/random_test_fuse.py b/PyTAT/tests/old_test/random_test_fuse.py deleted file mode 100644 index 4493d6c69..000000000 --- a/PyTAT/tests/old_test/random_test_fuse.py +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# Copyright (C) 2020-2023 Hao Zhang -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# - -import numpy as np -from TAT.No.D import Tensor -import TAT - -max_random = 8 - -for _ in range(1000): - rank_A = np.random.randint(3, max_random) - rank_B = np.random.randint(3, max_random) - rank_total = np.random.randint(2, np.min([rank_A, rank_B])) - rank_contract = np.random.randint(1, rank_total) - rank_fuse = rank_total - rank_contract - - total_leg_A = np.random.choice(range(rank_A), rank_total, False) - total_leg_B = np.random.choice(range(rank_B), rank_total, False) - contract_leg_A = total_leg_A[:rank_contract] - contract_leg_B = total_leg_B[:rank_contract] - fuse_leg_A = total_leg_A[rank_contract:] - fuse_leg_B = total_leg_B[rank_contract:] - - name_list_A = [f"A.{i}" for i in range(rank_A)] - name_list_B = [f"B.{i}" for i in range(rank_B)] - fuse_names = set() - for i in range(rank_fuse): - name = f"C.{fuse_leg_B[i]}" - name_list_A[fuse_leg_A[i]] = name - name_list_B[fuse_leg_B[i]] = name - fuse_names.add(name) - - dim_A = np.random.randint(1, max_random, rank_A).tolist() - dim_B = np.random.randint(1, max_random, rank_B).tolist() - for i in range(rank_total): - dim_A[total_leg_A[i]] = dim_B[total_leg_B[i]] = np.random.randint(2, max_random) - - A = Tensor(name_list_A, dim_A).range() - B = Tensor(name_list_B, dim_B).range() - C = A.contract(B, {(f"A.{contract_leg_A[i]}", f"B.{contract_leg_B[i]}") for i in range(rank_contract)}, fuse_names) - # print(repr(A)) - # print(repr(B)) - # print(repr(C)) - a = A.blocks[A.names] - b = B.blocks[B.names] - - index_A = [chr(ord('a') + i) for i in range(rank_A)] - index_B = [chr(ord('A') + i) for i in range(rank_B)] - index_C = [] - for i in range(rank_total): - index_A[total_leg_A[i]] = index_B[total_leg_B[i]] - for c_name in C.names: - if c_name.startswith("A"): - index_C.append(chr(ord('a') + int(c_name[2:]))) - elif c_name.startswith("B"): - index_C.append(chr(ord('A') + int(c_name[2:]))) - else: - index_C.append(chr(ord('A') + int(c_name[2:]))) - ein_conf = "".join(index_A) + "," + "".join(index_B) + "->" + "".join(index_C) - # print(ein_conf) - c = np.einsum(ein_conf, a, b) - # print(c.shape) - print(np.max(np.abs(C.blocks[C.names] - c))) diff --git a/PyTAT/tests/old_test/random_test_qr.py b/PyTAT/tests/old_test/random_test_qr.py deleted file mode 100644 index 8ff0e4ee4..000000000 --- a/PyTAT/tests/old_test/random_test_qr.py +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# Copyright (C) 2020-2023 Hao Zhang -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# - -import numpy as np -from TAT.No.D import Tensor - -max_random = 8 - -for _ in range(100): - rank_A = np.random.randint(2, max_random) - rank_contract = np.random.randint(1, rank_A) - U_leg = np.random.choice(range(rank_A), rank_contract, False) - - dim_A = np.random.randint(1, max_random, size=rank_A) - - A = Tensor([f"A.{i}" for i in range(rank_A)], dim_A.tolist()).randn() - - Q, R = A.qr("Q", {f"A.{i}" for i in U_leg}, "QR.Q", "QR.R") - re_A = Q.contract(R, {("QR.Q", "QR.R")}) - diff = re_A - A - - QTQ = Q.contract(Q.edge_rename({"QR.Q": "new"}), {(name, name) for name in Q.names if name != "QR.Q"}) - QTQ = QTQ.blocks[QTQ.names] - - diff_Q = QTQ - np.identity(len(QTQ)) - - print(np.max([diff.norm_max(), np.max(np.abs(diff_Q))])) - R_block = R.blocks[R.names] - # print(R_block.shape) - # print(R_block.reshape([-1, R_block.shape[-1]])) - # print(R_block.reshape([R_block.shape[0], -1])) diff --git a/PyTAT/tests/old_test/random_test_svd.py b/PyTAT/tests/old_test/random_test_svd.py deleted file mode 100644 index 7b7416749..000000000 --- a/PyTAT/tests/old_test/random_test_svd.py +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# Copyright (C) 2020-2023 Hao Zhang -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# - -import numpy as np -from TAT.No.D import Tensor - -max_random = 8 - -for _ in range(1000): - rank_A = np.random.randint(2, max_random) - rank_contract = np.random.randint(1, rank_A) - U_leg = np.random.choice(range(rank_A), rank_contract, False) - - dim_A = np.random.randint(1, max_random, size=rank_A) - - A = Tensor([f"A.{i}" for i in range(rank_A)], dim_A.tolist()).randn() - - U, S, V = A.svd({f"A.{i}" for i in U_leg}, "SVD.U", "SVD.V", "S.U", "S.V") - re_A = U.contract(S, {("SVD.U", "S.U")}).edge_rename({"S.V": "SVD.U"}).contract(V, {("SVD.U", "SVD.V")}) - diff = re_A - A - - UTU = U.contract(U.edge_rename({"SVD.U": "new"}), {(name, name) for name in U.names if name != "SVD.U"}) - UTU = UTU.blocks[UTU.names] - VTV = V.contract(V.edge_rename({"SVD.V": "new"}), {(name, name) for name in V.names if name != "SVD.V"}) - VTV = VTV.blocks[VTV.names] - - diff_U = UTU - np.identity(len(UTU)) - diff_V = VTV - np.identity(len(VTV)) - - print(np.max([diff.norm_max(), np.max(np.abs(diff_U)), np.max(np.abs(diff_V))])) diff --git a/PyTAT/tests/old_test/random_test_trace.py b/PyTAT/tests/old_test/random_test_trace.py deleted file mode 100644 index c0ae0fc0c..000000000 --- a/PyTAT/tests/old_test/random_test_trace.py +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# Copyright (C) 2020-2023 Hao Zhang -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# - -import numpy as np -from TAT.No.D import Tensor - -max_random = 8 - -for _ in range(1000): - rank_A = np.random.randint(3, max_random) - rank_trace = np.random.randint(1, rank_A // 2 + 1) - pair_leg = np.random.choice(range(rank_A), [rank_trace, 2], False) - - name_list = [f"A.{i}" for i in range(rank_A)] - dim_list = np.random.randint(2, max_random, rank_A) - dim_trace = np.random.randint(2, max_random, rank_trace) - for i, (j, k) in enumerate(pair_leg): - dim_list[j] = dim_trace[i] - dim_list[k] = dim_trace[i] - - trace_conf = {(f"A.{i}", f"A.{j}") for i, j in pair_leg} - - A = Tensor(name_list, dim_list.tolist()).range() - B = A.trace(trace_conf) - - res = A.blocks[A.names] - for i in range(rank_trace): - res = res.trace(0, pair_leg[i, 0], pair_leg[i, 1]) - for j in range(i + 1, rank_trace): - if pair_leg[j, 0] > pair_leg[i, 0]: - pair_leg[j, 0] -= 1 - if pair_leg[j, 1] > pair_leg[i, 0]: - pair_leg[j, 1] -= 1 - if pair_leg[i, 1] > pair_leg[i, 0]: - pair_leg[i, 1] -= 1 - if pair_leg[j, 0] > pair_leg[i, 1]: - pair_leg[j, 0] -= 1 - if pair_leg[j, 1] > pair_leg[i, 1]: - pair_leg[j, 1] -= 1 - - diff = res - B.blocks[B.names] - - print(np.max(np.abs(diff))) diff --git a/PyTAT/tests/test_arrow_and_parity.py b/PyTAT/tests/test_arrow_and_parity.py new file mode 100644 index 000000000..d9cae6b4f --- /dev/null +++ b/PyTAT/tests/test_arrow_and_parity.py @@ -0,0 +1,8 @@ +import TAT + + +def test_arrow_and_parity(): + assert TAT.arrow(+1) == False + assert TAT.arrow(-1) == True + assert TAT.parity(+1) == False + assert TAT.parity(-1) == True diff --git a/PyTAT/tests/test_callable.py b/PyTAT/tests/test_callable.py new file mode 100644 index 000000000..30b149f81 --- /dev/null +++ b/PyTAT/tests/test_callable.py @@ -0,0 +1,5 @@ +import TAT + + +def test_callable(): + assert TAT() == TAT.information diff --git a/PyTAT/tests/test_clear_symmetry.py b/PyTAT/tests/test_clear_symmetry.py new file mode 100644 index 000000000..8070e4570 --- /dev/null +++ b/PyTAT/tests/test_clear_symmetry.py @@ -0,0 +1,47 @@ +import TAT + + +def test_bose_mode(): + a = TAT.U1.S.Tensor(["i", "j"], [ + [(0, 2), (1, 3), (2, 5)], + [(0, 5), (-1, 4), (-2, 2)], + ]).range() + b = a.clear_symmetry() + for sym in range(3): + dim_i = a.edges(0).dimension_by_symmetry(+sym) + dim_j = a.edges(1).dimension_by_symmetry(-sym) + for i in range(dim_i): + for j in range(dim_j): + index_i = a.edges(0).index_by_point((+sym, i)) + index_j = a.edges(1).index_by_point((-sym, j)) + assert a[{"i": index_i, "j": index_j}] == b[{"i": index_i, "j": index_j}] + + +def test_fermi_mode(): + a = TAT.Fermi.S.Tensor(["i", "j"], [ + [(0, 2), (1, 3), (2, 5)], + [(0, 5), (-1, 4), (-2, 2)], + ]).range() + b = a.clear_symmetry() + for sym in range(3): + dim_i = a.edges(0).dimension_by_symmetry(+sym) + dim_j = a.edges(1).dimension_by_symmetry(-sym) + for i in range(dim_i): + for j in range(dim_j): + p_sym = sym % 2 != 0 + p_i = i + for s, d in a.edges(0).segments: + # print(s, +sym, s == +sym, s.parity, p_sym, s.parity == p_sym) + if s == +sym: + break + if s.parity == p_sym: + p_i += d + p_j = j + for s, d in a.edges(1).segments: + if s == -sym: + break + if s.parity == p_sym: + p_j += d + index_i = a.edges(0).index_by_point((+sym, i)) + index_j = a.edges(1).index_by_point((-sym, j)) + assert a[{"i": (+sym, i), "j": (-sym, j)}] == b[{"i": (p_sym, p_i), "j": (p_sym, p_j)}] diff --git a/PyTAT/tests/test_conjugate.py b/PyTAT/tests/test_conjugate.py new file mode 100644 index 000000000..b7f0fc887 --- /dev/null +++ b/PyTAT/tests/test_conjugate.py @@ -0,0 +1,173 @@ +import TAT + + +def test_no_symmetry_float(): + A = TAT.No.D.Tensor(["i", "j"], [2, 3]).range() + A_c = A.conjugate() + assert all(A.storage.conj() == A_c.storage) + + +def test_no_symmetry_complex(): + A = TAT.No.Z.Tensor(["i", "j"], [2, 3]).range(1 + 5j, 1 + 7j) + A_c = A.conjugate() + assert all(A.storage.conj() == A_c.storage) + + +def test_u1_symmetry_float(): + A = TAT.U1.D.Tensor( + ["i", "j"], + [ + [(-1, 2), (0, 2), (+1, 2)], + [(-1, 2), (0, 2), (+1, 2)], + ], + ).range(-8, +1) + A_c = A.conjugate() + B = A.contract(A_c, {("i", "i"), ("j", "j")}) + assert float(B) > 0 + assert (A_c.conjugate() - A).norm_max() == 0 + + +def test_u1_symmetry_complex(): + A = TAT.U1.Z.Tensor( + ["i", "j"], + [ + [(-1, 2), (0, 2), (+1, 2)], + [(-1, 2), (0, 2), (+1, 2)], + ], + ).range(-8 - 20j, +1 + 7j) + A_c = A.conjugate() + B = A.contract(A_c, {("i", "i"), ("j", "j")}) + assert complex(B).real > 0 + assert complex(B).imag == 0 + assert (A_c.conjugate() - A).norm_max() == 0 + + +def test_Fermi_symmetry_float(): + A = TAT.Fermi.D.Tensor( + ["i", "j"], + [ + [(-1, 2), (0, 2), (+1, 2)], + [(-1, 2), (0, 2), (+1, 2)], + ], + ).range(-8, +1) + A_c = A.conjugate() + B = A.contract(A_c, {("i", "i"), ("j", "j")}) + assert float(B) > 0 + assert (A_c.conjugate() - A).norm_max() == 0 + + +def test_Fermi_symmetry_float_bidirection_arrow(): + A = TAT.Fermi.D.Tensor( + ["i", "j"], + [ + ([(-1, 2), (0, 2), (+1, 2)], False), + ([(-1, 2), (0, 2), (+1, 2)], True), + ], + ).range(-8, +1) + A_c = A.conjugate() + B = A.contract(A_c, {("i", "i"), ("j", "j")}) + assert float(B) < 0 # The A * Ac may not be positive + assert (A_c.conjugate() - A).norm_max() == 0 + + +def test_Fermi_symmetry_float_bidirection_arrow_fixed(): + A = TAT.Fermi.D.Tensor( + ["i", "j"], + [ + ([(-1, 2), (0, 2), (+1, 2)], False), + ([(-1, 2), (0, 2), (+1, 2)], True), + ], + ).range(-8, +1) + A_c = A.conjugate(True) + B = A.contract(A_c, {("i", "i"), ("j", "j")}) + assert float(B) > 0 + assert (A_c.conjugate(True) - A).norm_max() == 0 + + +def test_fermi_symmetry_complex(): + A = TAT.Fermi.Z.Tensor( + ["i", "j"], + [ + [(-1, 2), (0, 2), (+1, 2)], + [(-1, 2), (0, 2), (+1, 2)], + ], + ).range(-8 - 20j, +1 + 7j) + A_c = A.conjugate() + B = A.contract(A_c, {("i", "i"), ("j", "j")}) + assert complex(B).real > 0 + assert complex(B).imag == 0 + assert (A_c.conjugate() - A).norm_max() == 0 + + +def test_fermi_symmetry_complex_bidirection_arrow(): + A = TAT.Fermi.Z.Tensor( + ["i", "j"], + [ + ([(-1, 2), (0, 2), (+1, 2)], False), + ([(-1, 2), (0, 2), (+1, 2)], True), + ], + ).range(-8 - 20j, +1 + 7j) + A_c = A.conjugate() + B = A.contract(A_c, {("i", "i"), ("j", "j")}) + assert complex(B).real < 0 + assert complex(B).imag == 0 + assert (A_c.conjugate() - A).norm_max() == 0 + + +def test_fermi_symmetry_complex_bidirection_arrow_fixed(): + A = TAT.Fermi.Z.Tensor( + ["i", "j"], + [ + ([(-1, 2), (0, 2), (+1, 2)], False), + ([(-1, 2), (0, 2), (+1, 2)], True), + ], + ).range(-8 - 20j, +1 + 7j) + A_c = A.conjugate(True) + B = A.contract(A_c, {("i", "i"), ("j", "j")}) + assert complex(B).real > 0 + assert complex(B).imag == 0 + assert (A_c.conjugate(True) - A).norm_max() == 0 + + +def test_fermi_symmetry_contract_with_conjugate(): + A = TAT.Fermi.Z.Tensor( + ["i", "j"], + [ + ([(-1, 2), (0, 2), (+1, 2)], False), + ([(+1, 2), (0, 2), (-1, 2)], True), + ], + ).range(-8 - 20j, +1 + 7j) + B = TAT.Fermi.Z.Tensor( + ["i", "j"], + [ + ([(-1, 2), (0, 2), (+1, 2)], False), + ([(+1, 2), (0, 2), (-1, 2)], True), + ], + ).range(-7 - 29j, +5 + 3j) + C = A.contract(B, {("i", "j")}) + A_c = A.conjugate() + B_c = B.conjugate() + C_c = A_c.contract(B_c, {("i", "j")}) + assert (C.conjugate() - C_c).norm_max() == 0 + + +def test_fermi_symmetry_contract_with_conjugate_arrow_fix_wrong(): + A = TAT.Fermi.Z.Tensor( + ["i", "j"], + [ + ([(-1, 2), (0, 2), (+1, 2)], False), + ([(+1, 2), (0, 2), (-1, 2)], True), + ], + ).range(-8 - 20j, +1 + 7j) + B = TAT.Fermi.Z.Tensor( + ["i", "j"], + [ + ([(-1, 2), (0, 2), (+1, 2)], False), + ([(+1, 2), (0, 2), (-1, 2)], True), + ], + ).range(-7 - 29j, +5 + 3j) + C = A.contract(B, {("i", "j")}) + A_c = A.conjugate(True) + B_c = B.conjugate(True) + C_c = A_c.contract(B_c, {("i", "j")}) + assert (C.conjugate(True) - C_c).norm_max() > 1e-3 diff --git a/PyTAT/tests/test_contract.py b/PyTAT/tests/test_contract.py new file mode 100644 index 000000000..244f60c2c --- /dev/null +++ b/PyTAT/tests/test_contract.py @@ -0,0 +1,213 @@ +import TAT + + +def test_no_symmetry_example_0(): + a = TAT.No.D.Tensor(["A", "B"], [2, 2]).range() + b = TAT.No.D.Tensor(["C", "D"], [2, 2]).range() + + c = a.contract(b, {("A", "C")}) + c_expect = TAT.No.D.Tensor(["B", "D"], [2, 2]) + c_expect.storage = [4, 6, 6, 10] + assert (c - c_expect).norm_max() == 0 + + d = a.contract(b, {("A", "D")}) + d_expect = TAT.No.D.Tensor(["B", "C"], [2, 2]) + d_expect.storage = [2, 6, 3, 11] + assert (d - d_expect).norm_max() == 0 + + e = a.contract(b, {("B", "C")}) + e_expect = TAT.No.D.Tensor(["A", "D"], [2, 2]) + e_expect.storage = [2, 3, 6, 11] + assert (e - e_expect).norm_max() == 0 + + f = a.contract(b, {("B", "D")}) + f_expect = TAT.No.D.Tensor(["A", "C"], [2, 2]) + f_expect.storage = [1, 3, 3, 13] + assert (f - f_expect).norm_max() == 0 + + +def test_no_symmetry_example_1(): + x = TAT.No.D.Tensor(["A", "B", "C", "D"], [1, 2, 3, 4]).range() + y = TAT.No.D.Tensor(["E", "F", "G", "H"], [3, 1, 2, 4]).range() + a = x.contract(y, {("B", "G"), ("D", "H")}) + b = TAT.No.D.Tensor(["A", "C", "E", "F"], [1, 3, 3, 1]) + b.storage = [316, 796, 1276, 428, 1164, 1900, 540, 1532, 2524] + assert (a - b).norm_max() == 0 + + +def test_u1_symmetry_example_0(): + Tensor = TAT.U1.D.Tensor + edge1 = [(-1, 2), (0, 2), (+1, 2)] + edge2 = [(+1, 2), (0, 2), (-1, 2)] + a = Tensor(["a", "b", "c", "d"], [edge1, edge2, edge1, edge2]).range() + b = Tensor(["e", "f", "g", "h"], [edge1, edge2, edge1, edge2]).range() + for plan in [ + {("a", "f"), ("b", "e")}, + {("a", "f"), ("b", "g")}, + {("a", "h"), ("b", "e")}, + {("a", "h"), ("b", "g")}, + {("a", "f"), ("c", "h")}, + {("a", "h"), ("c", "f")}, + {("a", "f"), ("d", "e")}, + {("a", "f"), ("d", "g")}, + {("a", "h"), ("d", "e")}, + {("a", "h"), ("d", "g")}, + {("c", "f"), ("b", "e")}, + {("c", "f"), ("b", "g")}, + {("c", "h"), ("b", "e")}, + {("c", "h"), ("b", "g")}, + {("b", "e"), ("d", "g")}, + {("b", "g"), ("d", "e")}, + {("c", "f"), ("d", "e")}, + {("c", "f"), ("d", "g")}, + {("c", "h"), ("d", "e")}, + {("c", "h"), ("d", "g")}, + ]: + c = a.contract(b, plan).clear_symmetry() + d = a.clear_symmetry().contract(b.clear_symmetry(), plan) + assert (c - d).norm_max() == 0 + + +def test_fermi_symmetry_example_0(): + FermiTensor = TAT.Fermi.D.Tensor + fermi_edge1 = [(-1, 2), (0, 2), (+1, 2)] + fermi_edge2 = [(+1, 2), (0, 2), (-1, 2)] + fermi_a = FermiTensor(["a", "b", "c", "d"], [ + (fermi_edge1, True), + fermi_edge2, + (fermi_edge1, True), + (fermi_edge2, True), + ]).range() + fermi_b = FermiTensor(["e", "f", "g", "h"], [ + fermi_edge1, + fermi_edge2, + (fermi_edge1, True), + fermi_edge2, + ]).range() + fermi_c = fermi_a.contract(fermi_b, {("d", "e"), ("c", "f")}) + fermi_d = fermi_b.contract(fermi_a, {("e", "d"), ("f", "c")}) + assert (fermi_c - fermi_d).norm_max() == 0 + + U1Tensor = TAT.U1.D.Tensor + u1_edge1 = [(-1, 2), (0, 2), (+1, 2)] + u1_edge2 = [(+1, 2), (0, 2), (-1, 2)] + u1_a = U1Tensor(["a", "b", "c", "d"], [ + u1_edge1, + u1_edge2, + u1_edge1, + u1_edge2, + ]).range() + u1_b = U1Tensor(["e", "f", "g", "h"], [ + u1_edge1, + u1_edge2, + u1_edge1, + u1_edge2, + ]).range() + u1_c = u1_a.contract(u1_b, {("d", "e"), ("c", "f")}) + u1_d = u1_b.contract(u1_a, {("e", "d"), ("f", "c")}) + assert (u1_c - u1_d).norm_max() == 0 + + assert all(fermi_a.storage == u1_a.storage) + assert all(fermi_b.storage == u1_b.storage) + assert all(fermi_c.storage == u1_c.storage) + + +def test_contract_with_split_and_merge(): + Tensor = TAT.Fermi.D.Tensor + edge1 = ([(-1, 2), (0, 2), (+1, 2)], False) + edge2 = ([(+1, 2), (0, 2), (-1, 2)], True) + a = Tensor(["a", "b", "c", "d"], [edge1, edge2, edge1, edge2]).range() + b = Tensor(["e", "f", "g", "h"], [edge1, edge2, edge1, edge2]).range() + c = a.contract(b, {("a", "f"), ("b", "g"), ("c", "h")}) + + a_merged = a.merge_edge({"m": ["b", "a"]}, False) + b_merged = b.merge_edge({"m": ["g", "f"]}, True) + c_merged = a_merged.contract(b_merged, {("m", "m"), ("c", "h")}) + + assert (c - c_merged).norm_max() == 0 + + +def test_contract_with_reverse_0(): + a = TAT.Parity.D.Tensor( + ["i", "j"], + [([(False, 2), (True, 2)], False), ([(False, 2), (True, 2)], True)], + ).range() + b = TAT.Parity.D.Tensor( + ["i", "j"], + [([(False, 2), (True, 2)], False), ([(False, 2), (True, 2)], True)], + ).range().transpose(["j", "i"]) + c = a.contract(b, {("j", "i")}) + + a_reversed = a.reverse_edge({"j"}, False) + b_reversed = b.reverse_edge({"i"}, True) + c_reversed = a_reversed.contract(b_reversed, {("j", "i")}) + + assert (c - c_reversed).norm_max() == 0 + + +def test_contract_with_reverse_1(): + Tensor = TAT.Fermi.D.Tensor + edge1 = ([(-1, 2), (0, 2), (+1, 2)], False) + edge2 = ([(+1, 2), (0, 2), (-1, 2)], True) + a = Tensor(["a", "b", "c", "d"], [edge1, edge2, edge1, edge2]).range() + b = Tensor(["e", "f", "g", "h"], [edge1, edge2, edge1, edge2]).range() + c = a.contract(b, {("a", "f"), ("b", "g"), ("c", "h")}) + + a_reversed = a.reverse_edge({"b", "a"}, False) + b_reversed = b.reverse_edge({"g", "f"}, True) + c_reversed = a_reversed.contract(b_reversed, {("a", "f"), ("b", "g"), ("c", "h")}) + + assert (c - c_reversed).norm_max() == 0 + + +def test_fuse(): + fuse_d = 3 + common_d = 4 + a = TAT.No.D.Tensor(["A", "B", "C"], [fuse_d, common_d, 5]).range() + b = TAT.No.D.Tensor(["A", "B", "D"], [fuse_d, common_d, 7]).range() + c = a.contract(b, {("B", "B")}, {"A"}) + for i in range(fuse_d): + hat = TAT.No.D.Tensor(["A"], [fuse_d]).zero() + hat.storage[i] = 1 + a0 = a.contract(hat, {("A", "A")}) + b0 = b.contract(hat, {("A", "A")}) + c0 = c.contract(hat, {("A", "A")}) + assert (a0.contract(b0, {("B", "B")}) - c0).norm_max() == 0 + + +def test_corner_no_symmetry_0k(): + a = TAT.No.D.Tensor(["A", "B"], [2, 0]).range() + b = TAT.No.D.Tensor(["C", "D"], [0, 2]).range() + c = a.contract(b, {("B", "C")}) + assert c.storage.size == 4 + assert c.norm_max() == 0 + + +def test_corner_z2_symmetry_0k(): + a = TAT.Z2.D.Tensor(["A", "B"], [[(False, 2)], [(False, 0)]]).range() + b = TAT.Z2.D.Tensor(["C", "D"], [[(False, 0)], [(False, 2)]]).range() + c = a.contract(b, {("B", "C")}) + assert c.storage.size == 4 + assert c.norm_max() == 0 + + +def test_corner_z2_symmetry_not_match_missing_left(): + a = TAT.Z2.D.Tensor(["A", "B"], [[(True, 2)], [(False, 0)]]).range() + b = TAT.Z2.D.Tensor(["C", "D"], [[(False, 0)], [(False, 2)]]).range() + c = a.contract(b, {("B", "C")}) + assert c.storage.size == 0 + + +def test_corner_z2_symmetry_not_match_missing_right(): + a = TAT.Z2.D.Tensor(["A", "B"], [[(False, 2)], [(False, 0)]]).range() + b = TAT.Z2.D.Tensor(["C", "D"], [[(False, 0)], [(True, 2)]]).range() + c = a.contract(b, {("B", "C")}) + assert c.storage.size == 0 + + +def test_corner_z2_symmetry_not_match_missing_middle(): + a = TAT.Z2.D.Tensor(["A", "B"], [[(False, 2)], [(True, 0)]]).range() + b = TAT.Z2.D.Tensor(["C", "D"], [[(True, 0)], [(False, 2)]]).range() + c = a.contract(b, {("B", "C")}) + assert c.storage.size == 4 + assert c.norm_max() == 0 diff --git a/PyTAT/tests/test_contract_with_numpy.py b/PyTAT/tests/test_contract_with_numpy.py new file mode 100644 index 000000000..00ed87411 --- /dev/null +++ b/PyTAT/tests/test_contract_with_numpy.py @@ -0,0 +1,37 @@ +import numpy as np +import TAT + + +def test_main(): + max_random = 8 + Tensor = TAT.No.D.Tensor + + for _ in range(100): + rank_A = np.random.randint(2, max_random) + rank_B = np.random.randint(2, max_random) + rank_contract = np.random.randint(1, np.min([rank_A, rank_B])) + # print(rank_A, rank_B, rank_contract) + + contract_name_A = np.random.choice(range(rank_A), rank_contract, False) + contract_name_B = np.random.choice(range(rank_B), rank_contract, False) + + dim_A = np.random.randint(1, max_random, size=rank_A) + dim_B = np.random.randint(1, max_random, size=rank_B) + dim_contract = np.random.randint(1, max_random, size=rank_contract) + + dim_A = [ + int(j if i not in contract_name_A else dim_contract[contract_name_A.tolist().index(i)]) + for i, j in enumerate(dim_A) + ] + dim_B = [ + int(j if i not in contract_name_B else dim_contract[contract_name_B.tolist().index(i)]) + for i, j in enumerate(dim_B) + ] + + A = Tensor([f"A.{i}" for i in range(rank_A)], dim_A).randn() + B = Tensor([f"B.{i}" for i in range(rank_B)], dim_B).randn() + v_t = A.contract(B, {(f"A.{i}", f"B.{j}") for i, j in zip(contract_name_A, contract_name_B)}) + v_t = v_t.blocks[v_t.names] + v_n = np.tensordot(A.blocks[A.names], B.blocks[B.names], [contract_name_A, contract_name_B]) + v_d = v_t - v_n + assert np.max(np.abs(v_d)) < 1e-6 diff --git a/PyTAT/tests/test_create_fermi_tensor.py b/PyTAT/tests/test_create_fermi_tensor.py new file mode 100644 index 000000000..997c63e37 --- /dev/null +++ b/PyTAT/tests/test_create_fermi_tensor.py @@ -0,0 +1,109 @@ +import TAT + + +class FakeEdge: + __slots__ = ["direction"] + + def __init__(self, direction): + self.direction = direction + + def __getitem__(self, x): + return (list(x), self.direction) + + +Fedge = FakeEdge(False) +Tedge = FakeEdge(True) + + +def test_basic_usage(): + # 1 1 0 : 3*1*3 + # 1 0 1 : 3*2*2 + # 0 1 1 : 1*1*2 + # 0 0 0 : 1*2*3 + a = TAT.Parity.D.Tensor(["Left", "Right", "Up"], [ + Tedge[(True, 3), (False, 1)], + Fedge[(True, 1), (False, 2)], + Tedge[(True, 2), (False, 3)], + ]).range() + assert a.names == ["Left", "Right", "Up"] + assert a.edges("Left") == a.edges(0) + assert a.edges("Right") == a.edges(1) + assert a.edges("Up") == a.edges(2) + assert a.edges(0).arrow == True + assert a.edges(1).arrow == False + assert a.edges(2).arrow == True + + assert a.blocks[("Left", True), ("Right", False), ("Up", True)].shape == (3, 2, 2) + assert a.blocks[("Left", False), ("Up", True), ("Right", True)].shape == (1, 2, 1) + + assert a[{"Left": (True, 2), "Right": (False, 0), "Up": (True, 1)}] == 3 * 1 * 3 + 9 + assert a[{"Left": (False, 0), "Right": (False, 1), "Up": (False, 2)}] == 3 * 1 * 3 + 3 * 2 * 2 + 1 * 1 * 2 + 5 + + +def test_when_0rank(): + a = TAT.Parity.D.Tensor([], []).range(2333) + assert a.names == [] + assert all(a.storage == [2333]) + + assert a.blocks[()].shape == () + + assert a[{}] == 2333 + assert float(a) == 2333 + + +def test_when_0size(): + a = TAT.Fermi.D.Tensor(["Left", "Right", "Up"], [ + Fedge[((0, 0),)], + Tedge[(-1, 1), (0, 2), (1, 3)], + Tedge[(-1, 2), (0, 3), (1, 1)], + ]).zero() + assert a.names == ["Left", "Right", "Up"] + assert a.storage.size == 0 + assert a.edges("Left") == a.edges(0) + assert a.edges("Right") == a.edges(1) + assert a.edges("Up") == a.edges(2) + assert a.edges(0).arrow == False + assert a.edges(1).arrow == True + assert a.edges(2).arrow == True + + assert a.blocks[("Left", 0), ("Right", +1), ("Up", -1)].shape == (0, 3, 2) + assert a.blocks[("Left", 0), ("Up", -1), ("Right", +1)].shape == (0, 2, 3) + + +def test_when_0block(): + a = TAT.Fermi.D.Tensor(["Left", "Right", "Up"], [ + Fedge[()], + Fedge[(-1, 1), (0, 2), (1, 3)], + Tedge[(-1, 2), (0, 3), (1, 1)], + ]).zero() + assert a.names == ["Left", "Right", "Up"] + assert a.storage.size == 0 + assert a.edges("Left") == a.edges(0) + assert a.edges("Right") == a.edges(1) + assert a.edges("Up") == a.edges(2) + assert a.edges(0).arrow == False + assert a.edges(1).arrow == False + assert a.edges(2).arrow == True + + +def test_conversion_scalar(): + a = TAT.Fermi.D.Tensor(2333, ["i", "j"], [-2, +2], [True, False]) + assert a.names == ["i", "j"] + assert (a.storage == [2333]).all() + assert a.edges("i") == a.edges(0) + assert a.edges("j") == a.edges(1) + assert a.edges(0).arrow == True + assert a.edges(1).arrow == False + + assert a.blocks[("i", -2), ("j", +2)].shape == (1, 1) + assert a.blocks[("j", +2), ("i", -2)].shape == (1, 1) + + assert float(a) == 2333 + assert a[{"i": 0, "j": 0}] == 2333 + + +def test_conversion_scalar_empty(): + a = TAT.Fermi.Z.Tensor(["i"], [[ + (-2, 333), + ]]).range(2333) + assert complex(a) == 0 diff --git a/PyTAT/tests/test_create_normal_tensor.py b/PyTAT/tests/test_create_normal_tensor.py new file mode 100644 index 000000000..e7540826e --- /dev/null +++ b/PyTAT/tests/test_create_normal_tensor.py @@ -0,0 +1,61 @@ +import TAT + + +def test_basic_usage(): + a = TAT.No.Z.Tensor(["Left", "Right"], [3, 4]).range() + assert a.names == ["Left", "Right"] + assert a.rank == 2 + assert (a.storage == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]).all() + assert a.edges(0) == a.edges("Left") + assert a.edges(1) == a.edges("Right") + + assert a.blocks["Left", "Right"].shape == (3, 4) + assert a.blocks["Right", "Left"].shape == (4, 3) + assert a.blocks[("Left", ()), ("Right", ())].shape == (3, 4) + assert a.blocks[("Right", ()), ("Left", ())].shape == (4, 3) + assert a.blocks[("Left", TAT.No.Symmetry()), ("Right", TAT.No.Symmetry())].shape == (3, 4) + assert a.blocks[("Right", TAT.No.Symmetry()), ("Left", TAT.No.Symmetry())].shape == (4, 3) + + assert a[{"Left": 1, "Right": 2}] == a[{"Right": 2, "Left": 1}] == 6 + assert a[{"Left": ((), 1), "Right": ((), 2)}] == a[{"Right": ((), 2), "Left": ((), 1)}] == 6 + + +def test_when_0rank(): + a = TAT.No.Z.Tensor([], []).range() + assert a.names == [] + assert a.rank == 0 + assert (a.storage == [0]).all() + + assert a.blocks[()].shape == () + + assert a[{}] == 0 + + +def test_when_0size(): + a = TAT.No.Z.Tensor(["Left", "Right"], [0, 4]).range() + assert a.names == ["Left", "Right"] + assert a.rank == 2 + assert a.storage.size == 0 + assert a.edges(0) == a.edges("Left") + assert a.edges(1) == a.edges("Right") + + assert a.blocks["Left", "Right"].shape == (0, 4) + assert a.blocks[("Left", ()), ("Right", ())].shape == (0, 4) + assert a.blocks[("Right", TAT.No.Symmetry()), ("Left", TAT.No.Symmetry())].shape == (4, 0) + + +def test_conversion_scalar(): + a = TAT.No.Z.Tensor(2333, ["i", "j"]) + assert a.names == ["i", "j"] + assert a.rank == 2 + assert (a.storage == [2333]).all() + assert a.edges(0) == a.edges("i") + assert a.edges(1) == a.edges("j") + + assert a.blocks["i", "j"].shape == (1, 1) + assert a.blocks[("i", ()), ("j", ())].shape == (1, 1) + assert a.blocks[("j", TAT.No.Symmetry()), ("i", TAT.No.Symmetry())].shape == (1, 1) + + assert a[{"i": 0, "j": 0}] == 2333 + assert a[{"i": ((), 0), "j": ((), 0)}] == 2333 + assert complex(a) == 2333 diff --git a/PyTAT/tests/test_create_symmetry_tensor.py b/PyTAT/tests/test_create_symmetry_tensor.py new file mode 100644 index 000000000..0f618c16f --- /dev/null +++ b/PyTAT/tests/test_create_symmetry_tensor.py @@ -0,0 +1,92 @@ +import TAT + + +def test_basic_usage(): + # 1 1 0 : 3*1*3 + # 1 0 1 : 3*2*2 + # 0 1 1 : 1*1*2 + # 0 0 0 : 1*2*3 + a = TAT.Z2.D.Tensor( + ["Left", "Right", "Up"], + [[(True, 3), (False, 1)], [(True, 1), (False, 2)], [(True, 2), (False, 3)]], + ).range() + assert a.names == ["Left", "Right", "Up"] + assert a.rank == 3 + assert a.storage.size == 1 * 2 * 3 + 1 * 1 * 2 + 3 * 2 * 2 + 3 * 1 * 3 + assert a.edges(0) == a.edges("Left") + assert a.edges(1) == a.edges("Right") + assert a.edges(2) == a.edges("Up") + + assert a.blocks[[("Left", True), ("Right", False), ("Up", True)]].shape == (3, 2, 2) + assert a.blocks[[("Left", False), ("Right", True), ("Up", True)]].shape == (1, 1, 2) + + assert a[{"Left": (True, 2), "Right": (False, 0), "Up": (True, 1)}] == 3 * 1 * 3 + 9 + assert a[{"Left": 2, "Right": 1, "Up": 1}] == 3 * 1 * 3 + 9 + assert a[{"Left": (False, 0), "Right": (False, 1), "Up": (False, 2)}] == 3 * 1 * 3 + 3 * 2 * 2 + 1 * 1 * 2 + 5 + assert a[{"Left": 3, "Right": 2, "Up": 4}] == 3 * 1 * 3 + 3 * 2 * 2 + 1 * 1 * 2 + 5 + + +def test_when_0rank(): + a = TAT.U1.D.Tensor([], []).range(2333) + assert a.names == [] + assert a.rank == 0 + assert (a.storage == [2333]).all() + + assert a.blocks[()].shape == () + + assert a[{}] == 2333 + + +def test_when_0size(): + a = TAT.U1.D.Tensor( + ["Left", "Right", "Up"], + [[ + (0, 0), + ], [(-1, 1), (0, 2), (1, 3)], [(-1, 2), (0, 3), (1, 1)]], + ).zero() + assert a.names == ["Left", "Right", "Up"] + assert a.rank == 3 + assert a.storage.size == 0 + assert a.edges(0) == a.edges("Left") + assert a.edges(1) == a.edges("Right") + assert a.edges(2) == a.edges("Up") + + assert a.blocks[("Left", 0), ("Right", +1), ("Up", -1)].shape == (0, 3, 2) + + +def test_when_0block(): + a = TAT.U1.D.Tensor( + ["Left", "Right", "Up"], + [[], [(-1, 1), (0, 2), (1, 3)], [(-1, 2), (0, 3), (1, 1)]], + ).zero() + assert a.names == ["Left", "Right", "Up"] + assert a.rank == 3 + assert a.storage.size == 0 + assert a.edges(0) == a.edges("Left") + assert a.edges(1) == a.edges("Right") + assert a.edges(2) == a.edges("Up") + + +def test_conversion_scalar(): + a = TAT.U1.D.Tensor(2333, ["i", "j"], [-2, +2]) + assert a.names == ["i", "j"] + assert a.rank == 2 + assert (a.storage == [2333]).all() + assert a.edges(0) == a.edges("i") + assert a.edges(1) == a.edges("j") + + assert a.blocks[("i", -2), ("j", +2)].shape == (1, 1) + assert a.blocks[("i", (-2,)), ("j", (+2,))].shape == (1, 1) + assert a.blocks[("j", TAT.U1.Symmetry(+2)), ("i", TAT.U1.Symmetry(-2))].shape == (1, 1) + + assert a[{"i": (-2, 0), "j": (+2, 0)}] == 2333 + assert a[{"i": ((-2,), 0), "j": ((+2,), 0)}] == 2333 + assert float(a) == 2333 + + +def test_conversion_scalar_empty(): + a = TAT.U1.D.Tensor(["i"], [[ + (+2, 333), + ]]).range(2333) + assert a.rank == 1 + assert float(a) == 0 diff --git a/PyTAT/tests/test_edge.py b/PyTAT/tests/test_edge.py new file mode 100644 index 000000000..bc66705d3 --- /dev/null +++ b/PyTAT/tests/test_edge.py @@ -0,0 +1,82 @@ +import TAT + + +def test_create_trivial(): + assert TAT.No.Edge(2) == TAT.No.Edge([(TAT.No.Symmetry(), 2)]) + assert TAT.Fermi.Edge(2) == TAT.Fermi.Edge([(TAT.Fermi.Symmetry(0), 2)]) + assert TAT.Fermi.Edge(2).arrow == False + + +def test_create_symmetry_list(): + assert TAT.U1.Edge([1, 2, 3]) == TAT.U1.Edge([(1, 1), (2, 1), (3, 1)]) + assert TAT.Fermi.Edge([1, 2, 3]) == TAT.Fermi.Edge([(1, 1), (2, 1), (3, 1)], False) + assert TAT.Fermi.Edge([1, 2, 3], True) == TAT.Fermi.Edge([(1, 1), (2, 1), (3, 1)], True) + assert TAT.Fermi.Edge([1, 2, 3], True) != TAT.Fermi.Edge([(1, 1), (2, 1), (3, 1)]) + assert TAT.Fermi.Edge(([1, 2, 3], True)) == TAT.Fermi.Edge([(1, 1), (2, 1), (3, 1)], True) + + +def test_segments(): + assert TAT.No.Edge(2).segments == [(TAT.No.Symmetry(), 2)] + assert TAT.Z2.Edge(2).segments == [(TAT.Z2.Symmetry(0), 2)] + assert TAT.U1.Edge([1, 2, 3]).segments == [(sym, 1) for sym in [1, 2, 3]] + assert TAT.U1.Edge([1, 2, 3]).segments == [((sym,), 1) for sym in [1, 2, 3]] + e = TAT.U1.Edge([1, 2, 3]) + assert TAT.U1.Edge(e.segments).segments == [(sym, 1) for sym in [1, 2, 3]] + assert TAT.U1.Edge([(1, 2), (2, 2), (3, 2)]).segments == [(sym, 2) for sym in [1, 2, 3]] + assert TAT.Fermi.Edge([(1, 2), (2, 2), (3, 2)]).segments == [(sym, 2) for sym in [1, 2, 3]] + assert TAT.Fermi.Edge([(1, 2), (2, 2), (3, 2)], True).segments == [(sym, 2) for sym in [1, 2, 3]] + + +def test_arrow_when_construct(): + assert TAT.Fermi.Edge([(1, 2), (2, 2), (3, 2)], False), arrow() == False + assert TAT.Fermi.Edge([(1, 2), (2, 2), (3, 2)], True), arrow() == True + assert TAT.U1.Edge([(1, 2), (2, 2), (3, 2)], False), arrow() == False + assert TAT.U1.Edge([(1, 2), (2, 2), (3, 2)], True), arrow() == False + + +def test_compare_arrow(): + assert TAT.U1.Edge([1, 2, 3], False) == TAT.U1.Edge([1, 2, 3], True) + assert TAT.Fermi.Edge([1, 2, 3], False) != TAT.Fermi.Edge([1, 2, 3], True) + + +def test_compare_segments(): + assert TAT.U1.Edge([(1, 2), (2, 2), (3, 2)]) == TAT.U1.Edge([(1, 2), (2, 2), (3, 2)]) + assert TAT.U1.Edge([(1, 2), (2, 2), (3, 2)]) != TAT.U1.Edge([(1, 2), (2, 2), (3, 3)]) + assert TAT.U1.Edge([(1, 2), (2, 2), (3, 2)]) != TAT.U1.Edge([(1, 2), (2, 2), (4, 2)]) + + +def test_conjugate(): + e1 = TAT.Fermi.Edge([(+1, 2), (+2, 2), (+3, 2)], True) + e2 = TAT.Fermi.Edge([(-1, 2), (-2, 2), (-3, 2)], False) + assert e1.conjugated() == e2 + + +def test_segments_size(): + e1 = TAT.U1.Edge([(1, 2), (2, 2), (3, 2)]) + assert len(e1.segments) == e1.segments_size + e2 = TAT.Parity.Edge([(False, 2), (True, 2)], True) + assert len(e2.segments) == e2.segments_size + + +def test_total_dimension(): + e1 = TAT.U1.Edge([(1, 2), (2, 2), (3, 2)]) + assert e1.dimension == 6 + e2 = TAT.Parity.Edge([(False, 2), (True, 2)], True) + assert e2.dimension == 4 + + +def test_segment_query(): + e1 = TAT.Fermi.Edge([(1, 2), (2, 2), (3, 4)], True) + assert e1.coord_by_point((2, 1)) == (1, 1) + assert e1.point_by_coord((1, 1)) == (2, 1) + assert e1.coord_by_index(3) == (1, 1) + assert e1.index_by_coord((1, 1)) == 3 + assert e1.point_by_index(3) == (2, 1) + assert e1.index_by_point((2, 1)) == 3 + + assert e1.dimension_by_symmetry(1) == 2 + assert e1.dimension_by_symmetry(2) == 2 + assert e1.dimension_by_symmetry(3) == 4 + assert e1.position_by_symmetry(1) == 0 + assert e1.position_by_symmetry(2) == 1 + assert e1.position_by_symmetry(3) == 2 diff --git a/PyTAT/tests/test_edge_operator.py b/PyTAT/tests/test_edge_operator.py new file mode 100644 index 000000000..8ffadadf4 --- /dev/null +++ b/PyTAT/tests/test_edge_operator.py @@ -0,0 +1,77 @@ +import TAT + + +def test_no_symmetry_example_0(): + a = TAT.No.D.Tensor(["A", "B"], [8, 8]).range() + b = a.edge_rename({ + "A": "C" + }).edge_operator({ + "C": [("D", 4), ("E", 2)], + "B": [("F", 2), ("G", 4)], + }, {"D", "F"}, { + "I": ["D", "F"], + "J": ["G", "E"], + }, ["J", "I"]) + b_s = a.edge_rename({ + "A": "C" + }).split_edge({ + "C": [("D", 4), ("E", 2)], + "B": [("F", 2), ("G", 4)], + }).merge_edge({ + "I": ["D", "F"], + "J": ["G", "E"], + }).transpose(["J", "I"]) + assert (b - b_s).norm_max() == 0 + + +def test_u1_symmetry_example_0(): + a = TAT.U1.D.Tensor(["Left", "Right", "Up", "Down"], [ + [(-1, 3), (0, 1), (1, 2)], + [(-1, 1), (0, 4), (1, 2)], + [(-1, 2), (0, 3), (1, 1)], + [(-1, 1), (0, 3), (1, 2)], + ]).range() + b = a.edge_rename({ + "Right": "Right1" + }).split_edge({"Down": [ + ("Down1", [(0, 1), (1, 2)]), + ("Down2", [(-1, 1), (0, 1)]), + ]}) + c = b.transpose(["Down1", "Right1", "Up", "Left", "Down2"]) + d = c.merge_edge({"Left": ["Left", "Down2"]}) + total = a.edge_rename({ + "Right": "Right1" + }).edge_operator( + {"Down": [("Down1", [(0, 1), (1, 2)]), ("Down2", [(-1, 1), (0, 1)])]}, + set(), + {"Left": ["Left", "Down2"]}, + ["Down1", "Right1", "Up", "Left"], + ) + assert (total - d).norm_max() == 0 + + +def test_fermi_symmetry_example_0(): + a = TAT.Fermi.D.Tensor(["Left", "Right", "Up", "Down"], [ + [(-1, 3), (0, 1), (1, 2)], + [(-1, 1), (0, 4), (1, 2)], + [(-1, 2), (0, 3), (1, 1)], + [(-1, 1), (0, 3), (1, 2)], + ]).range() + b = a.edge_rename({ + "Right": "Right1" + }).split_edge({"Down": [ + ("Down1", [(0, 1), (1, 2)]), + ("Down2", [(-1, 1), (0, 1)]), + ]}) + r = b.reverse_edge({"Left"}) + c = r.transpose(["Down1", "Right1", "Up", "Left", "Down2"]) + d = c.merge_edge({"Left": ["Left", "Down2"]}) + total = a.edge_rename({ + "Right": "Right1" + }).edge_operator( + {"Down": [("Down1", [(0, 1), (1, 2)]), ("Down2", [(-1, 1), (0, 1)])]}, + {"Left"}, + {"Left": ["Left", "Down2"]}, + ["Down1", "Right1", "Up", "Left"], + ) + assert (total - d).norm_max() == 0 diff --git a/PyTAT/tests/test_edge_rename.py b/PyTAT/tests/test_edge_rename.py new file mode 100644 index 000000000..964af2f6d --- /dev/null +++ b/PyTAT/tests/test_edge_rename.py @@ -0,0 +1,14 @@ +import TAT + + +def test_basic_rename(): + import numpy as np + t1 = TAT.Z2.D.Tensor(["Left", "Right", "Phy"], [ + [(False, 1), (True, 2)], + [(False, 3), (True, 4)], + [(False, 5), (True, 6)], + ]).range() + t2 = t1.edge_rename({"Left": "Up"}) + assert t1.names == ["Left", "Right", "Phy"] + assert t2.names == ["Up", "Right", "Phy"] + assert np.shares_memory(t1.storage, t2.storage) diff --git a/PyTAT/tests/test_exponential.py b/PyTAT/tests/test_exponential.py new file mode 100644 index 000000000..db1b8ecbd --- /dev/null +++ b/PyTAT/tests/test_exponential.py @@ -0,0 +1,52 @@ +import TAT + + +def reference_exponential(tensor, pairs, step): + result = tensor.same_shape().identity(pairs) + power = result + for i in range(1, step): + power = power.contract(tensor, pairs) / i + result += power + return result + + +def test_no_symmetry(): + A = TAT.No.D.Tensor(["i", "j"], [3, 3]).range() + pairs = {("i", "j")} + expA = A.exponential(pairs, 10) + expA_r = reference_exponential(A, pairs, 100) + assert (expA - expA_r).norm_max() < 1e-8 + + +def test_u1_symmetry(): + A = TAT.U1.D.Tensor( + ["i", "j", "k", "l"], + [ + [(-1, 2), (0, 2), (+1, 2)], + [(+1, 2), (0, 2), (-1, 2)], + [(+1, 2), (0, 2), (-1, 2)], + [(-1, 2), (0, 2), (+1, 2)], + ], + ).range() + A /= A.norm_max() + pairs = {("i", "k"), ("l", "j")} + expA = A.exponential(pairs, 10) + expA_r = reference_exponential(A, pairs, 100) + assert (expA - expA_r).norm_max() < 1e-8 + + +def test_fermi_symmetry(): + A = TAT.Fermi.D.Tensor( + ["i", "j", "k", "l"], + [ + ([(-1, 2), (0, 2), (+1, 2)], True), + ([(+1, 2), (0, 2), (-1, 2)], True), + ([(+1, 2), (0, 2), (-1, 2)], False), + ([(-1, 2), (0, 2), (+1, 2)], False), + ], + ).range() + A /= A.norm_max() + pairs = {("i", "k"), ("l", "j")} + expA = A.exponential(pairs, 10) + expA_r = reference_exponential(A, pairs, 100) + assert (expA - expA_r).norm_max() < 1e-8 diff --git a/PyTAT/tests/test_fuse_with_numpy.py b/PyTAT/tests/test_fuse_with_numpy.py new file mode 100644 index 000000000..06da376fe --- /dev/null +++ b/PyTAT/tests/test_fuse_with_numpy.py @@ -0,0 +1,63 @@ +import numpy as np +import TAT + + +def test_main(): + max_random = 8 + Tensor = TAT.No.D.Tensor + + for _ in range(100): + rank_A = np.random.randint(3, max_random) + rank_B = np.random.randint(3, max_random) + rank_total = np.random.randint(2, np.min([rank_A, rank_B])) + rank_contract = np.random.randint(1, rank_total) + rank_fuse = rank_total - rank_contract + + total_leg_A = np.random.choice(range(rank_A), rank_total, False) + total_leg_B = np.random.choice(range(rank_B), rank_total, False) + contract_leg_A = total_leg_A[:rank_contract] + contract_leg_B = total_leg_B[:rank_contract] + fuse_leg_A = total_leg_A[rank_contract:] + fuse_leg_B = total_leg_B[rank_contract:] + + name_list_A = [f"A.{i}" for i in range(rank_A)] + name_list_B = [f"B.{i}" for i in range(rank_B)] + fuse_names = set() + for i in range(rank_fuse): + name = f"C.{fuse_leg_B[i]}" + name_list_A[fuse_leg_A[i]] = name + name_list_B[fuse_leg_B[i]] = name + fuse_names.add(name) + + dim_A = np.random.randint(1, max_random, rank_A).tolist() + dim_B = np.random.randint(1, max_random, rank_B).tolist() + for i in range(rank_total): + dim_A[total_leg_A[i]] = dim_B[total_leg_B[i]] = np.random.randint(2, max_random) + + A = Tensor(name_list_A, dim_A).range() + B = Tensor(name_list_B, dim_B).range() + C = A.contract(B, {(f"A.{contract_leg_A[i]}", f"B.{contract_leg_B[i]}") for i in range(rank_contract)}, + fuse_names) + # print(repr(A)) + # print(repr(B)) + # print(repr(C)) + a = A.blocks[A.names] + b = B.blocks[B.names] + + index_A = [chr(ord('a') + i) for i in range(rank_A)] + index_B = [chr(ord('A') + i) for i in range(rank_B)] + index_C = [] + for i in range(rank_total): + index_A[total_leg_A[i]] = index_B[total_leg_B[i]] + for c_name in C.names: + if c_name.startswith("A"): + index_C.append(chr(ord('a') + int(c_name[2:]))) + elif c_name.startswith("B"): + index_C.append(chr(ord('A') + int(c_name[2:]))) + else: + index_C.append(chr(ord('A') + int(c_name[2:]))) + ein_conf = "".join(index_A) + "," + "".join(index_B) + "->" + "".join(index_C) + # print(ein_conf) + c = np.einsum(ein_conf, a, b) + # print(c.shape) + assert np.max(np.abs(C.blocks[C.names] - c)) < 1e-6 diff --git a/PyTAT/tests/test_identity.py b/PyTAT/tests/test_identity.py new file mode 100644 index 000000000..d6e9721f4 --- /dev/null +++ b/PyTAT/tests/test_identity.py @@ -0,0 +1,112 @@ +import TAT + +arrange_pairs_indices = [ + [0, 1, 2, 3, 4, 5], + [0, 1, 2, 4, 5, 3], + [0, 1, 2, 5, 3, 4], + [0, 2, 1, 3, 4, 5], + [0, 2, 1, 4, 5, 3], + [0, 2, 1, 5, 3, 4], + [0, 3, 1, 2, 4, 5], + [0, 3, 1, 4, 5, 2], + [0, 3, 1, 5, 2, 4], + [0, 4, 1, 2, 3, 5], + [0, 4, 1, 3, 5, 2], + [0, 4, 1, 5, 2, 3], + [0, 5, 1, 2, 3, 4], + [0, 5, 1, 3, 4, 2], + [0, 5, 1, 4, 2, 3], +] + +order_lists = [ + [0, 0, 0], + [0, 0, 1], + [0, 1, 0], + [0, 1, 1], + [1, 0, 0], + [1, 0, 1], + [1, 1, 0], + [1, 1, 1], +] + + +def test_no_symmetry_0(): + a = TAT.No.float.Tensor(["i", "j"], [4, 4]).identity({("i", "j")}) + assert (a - a.contract(a, {("i", "j")})).norm_max() == 0 + assert (a - a.contract(a, {("j", "i")})).norm_max() == 0 + + +def test_no_symmetry_1(): + a = TAT.No.float.Tensor(["i", "j"], [4, 4]).identity({("j", "i")}) + assert (a - a.contract(a, {("i", "j")})).norm_max() == 0 + assert (a - a.contract(a, {("j", "i")})).norm_max() == 0 + + +def test_no_symmetry_2(): + half_rank = 3 + for pairs_index in arrange_pairs_indices: + a = TAT.No.S.Tensor(["1", "2", "3", "4", "5", "6"], [4, 4, 4, 4, 4, 4]).range() + pairs = set() + for i in range(half_rank): + p0 = pairs_index[i * 2 + 0] + p1 = pairs_index[i * 2 + 1] + pairs.add((a.names[p0], a.names[p1])) + a.identity(pairs) + assert (a - a.contract(a, pairs)).norm_max() == 0 + + +def test_z2_symmetry_0(): + half_rank = 3 + edge = [(False, 2), (True, 2)] + for pairs_index in arrange_pairs_indices: + a = TAT.Z2.S.Tensor(["1", "2", "3", "4", "5", "6"], [edge, edge, edge, edge, edge, edge]).range() + pairs = set() + for i in range(half_rank): + p0 = pairs_index[i * 2 + 0] + p1 = pairs_index[i * 2 + 1] + pairs.add((a.names[p0], a.names[p1])) + a.identity(pairs) + assert (a - a.contract(a, pairs)).norm_max() == 0 + + +def test_u1_symmetry_0(): + half_rank = 3 + edge0 = [(-1, 1), (0, 1), (+1, 1)] + edge1 = [(+1, 1), (0, 1), (-1, 1)] + for pairs_index in arrange_pairs_indices: + names = ["1", "2", "3", "4", "5", "6"] + edges = [None, None, None, None, None, None] + pairs = set() + for i in range(half_rank): + p0 = pairs_index[i * 2 + 0] + p1 = pairs_index[i * 2 + 1] + pairs.add((names[p0], names[p1])) + edges[p0] = edge0 + edges[p1] = edge1 + a = TAT.U1.S.Tensor(names, edges).range() + a.identity(pairs) + assert (a - a.contract(a, pairs)).norm_max() == 0 + + +def test_fermi_symmetry_0(): + half_rank = 3 + edge0 = ([(-1, 1), (0, 1), (+1, 1)], False) + edge1 = ([(+1, 1), (0, 1), (-1, 1)], True) + for order in order_lists: + for pairs_index in arrange_pairs_indices: + names = ["1", "2", "3", "4", "5", "6"] + edges = [None, None, None, None, None, None] + pairs = set() + for i in range(half_rank): + p0 = pairs_index[i * 2 + 0] + p1 = pairs_index[i * 2 + 1] + pairs.add((names[p0], names[p1])) + if order[i] != 0: + edges[p0] = edge0 + edges[p1] = edge1 + else: + edges[p0] = edge1 + edges[p1] = edge0 + a = TAT.Fermi.S.Tensor(names, edges).range() + a.identity(pairs) + assert (a - a.contract(a, pairs)).norm_max() == 0 diff --git a/PyTAT/tests/test_module_alias.py b/PyTAT/tests/test_module_alias.py new file mode 100644 index 000000000..117ad9271 --- /dev/null +++ b/PyTAT/tests/test_module_alias.py @@ -0,0 +1,12 @@ +import TAT + + +def test_normal_tensor(): + assert TAT.No == TAT.Normal + + +def test_scalar(): + assert TAT.Fermi.S == TAT.Fermi.float32 + assert TAT.Z2.D == TAT.Z2.float == TAT.Z2.float64 + assert TAT.Parity.C == TAT.Parity.complex64 + assert TAT.FermiU1.Z == TAT.FermiU1.complex == TAT.FermiU1.complex128 diff --git a/PyTAT/tests/test_norm.py b/PyTAT/tests/test_norm.py new file mode 100644 index 000000000..cc481f24b --- /dev/null +++ b/PyTAT/tests/test_norm.py @@ -0,0 +1,14 @@ +import TAT + + +def test_basic_usage(): + t = TAT.U1.D.Tensor(["Left", "Right", "Up"], [ + [(-1, 3), (0, 1), (1, 2)], + [(-1, 1), (0, 2), (1, 3)], + [(-1, 2), (0, 3), (1, 1)], + ]).range(7).to("complex128") + assert t.storage.size == 60 + assert t.norm_max() == 66 + assert t.norm_num() == 60 + assert t.norm_sum() == (7 + 66) * 60 / 2 + assert t.norm_2() == ((66 * (66 + 1) * (2 * 66 + 1) - 6 * (6 + 1) * (2 * 6 + 1)) / 6.)**(1 / 2.) diff --git a/PyTAT/tests/test_qr.py b/PyTAT/tests/test_qr.py new file mode 100644 index 000000000..aa140cec4 --- /dev/null +++ b/PyTAT/tests/test_qr.py @@ -0,0 +1,103 @@ +import TAT + + +def check_unitary(tensor, name, name_prime, fermi): + pairs = set() + for n in tensor.names: + if n != name: + pairs.add((n, n)) + conjugated = tensor.conjugate(True).edge_rename({name: name_prime}) + product = tensor.contract(conjugated, pairs) + identity = product.same_shape().identity({(name, name_prime)}) + if fermi: + product.transform(abs) + identity.transform(abs) + assert (product - identity).norm_max() < 1e-6 + + +def test_no_symmetry_0(): + a = TAT.No.D.Tensor(["A", "B"], [5, 10]).range() + q, r = a.qr('r', {"A"}, "newQ", "newR") + q_2, r_2 = a.qr('q', {"B"}, "newQ", "newR") + assert (q - q_2).norm_max() == 0 + assert (r - r_2).norm_max() == 0 + assert q.names == ["newQ", "B"] + assert r.names == ["A", "newR"] + assert q.edges("newQ").dimension == 5 + assert r.edges("newR").dimension == 5 + check_unitary(q, "newQ", "newQ'", False) + assert (q.contract(r, {("newQ", "newR")}) - a).norm_max() < 1e-6 + + +def test_no_symmetry_1(): + a = TAT.No.Z.Tensor(["A", "B"], [10, 5]).range(-21 - 29j, 2 + 3j) + q, r = a.qr('r', {"A"}, "newQ", "newR") + q_2, r_2 = a.qr('q', {"B"}, "newQ", "newR") + assert (q - q_2).norm_max() == 0 + assert (r - r_2).norm_max() == 0 + assert q.names == ["newQ", "B"] + assert r.names == ["A", "newR"] + assert q.edges("newQ").dimension == 5 + assert r.edges("newR").dimension == 5 + check_unitary(q, "newQ", "newQ'", False) + assert (q.contract(r, {("newQ", "newR")}) - a).norm_max() < 1e-6 + + +def test_fermi_symmetry_0(): + a = TAT.Fermi.D.Tensor( + ["A", "B"], + [ + ([(-1, 2), (0, 1), (+1, 2)], False), + ([(-1, 4), (0, 3), (+1, 3)], True), + ], + ).range() + q, r = a.qr('r', {"A"}, "newQ", "newR") + q_2, r_2 = a.qr('q', {"B"}, "newQ", "newR") + assert (q - q_2).norm_max() == 0 + assert (r - r_2).norm_max() == 0 + assert q.names == ["newQ", "B"] + assert r.names == ["A", "newR"] + assert q.edges("newQ").dimension == 5 + assert r.edges("newR").dimension == 5 + check_unitary(q, "newQ", "newQ'", True) + assert (q.contract(r, {("newQ", "newR")}) - a).norm_max() < 1e-6 + + +def test_fermi_symmetry_1(): + a = TAT.Fermi.Z.Tensor( + ["A", "B"], + [ + ([(-1, 2), (0, 1), (+1, 2)], True), + ([(-1, 4), (0, 3), (+1, 3)], False), + ], + ).range() + q, r = a.qr('r', {"A"}, "newQ", "newR") + q_2, r_2 = a.qr('q', {"B"}, "newQ", "newR") + assert (q - q_2).norm_max() == 0 + assert (r - r_2).norm_max() == 0 + assert q.names == ["newQ", "B"] + assert r.names == ["A", "newR"] + assert q.edges("newQ").dimension == 5 + assert r.edges("newR").dimension == 5 + check_unitary(q, "newQ", "newQ'", True) + assert (q.contract(r, {("newQ", "newR")}) - a).norm_max() < 1e-6 + + +def test_fermi_symmetry_edge_mismatch(): + a = TAT.Fermi.Z.Tensor( + ["A", "B"], + [ + ([(-1, 2), (0, 2), (+2, 2)], True), + ([(-1, 4), (0, 3), (+2, 3)], False), + ], + ).range() + q, r = a.qr('r', {"B"}, "newQ", "newR") + q_2, r_2 = a.qr('q', {"A"}, "newQ", "newR") + assert (q - q_2).norm_max() == 0 + assert (r - r_2).norm_max() == 0 + assert q.names == ["A", "newQ"] + assert r.names == ["newR", "B"] + assert q.edges("newQ").dimension == 2 + assert r.edges("newR").dimension == 2 + check_unitary(q, "newQ", "newQ'", True) + assert (q.contract(r, {("newQ", "newR")}) - a).norm_max() < 1e-6 diff --git a/PyTAT/tests/test_qr_with_numpy.py b/PyTAT/tests/test_qr_with_numpy.py new file mode 100644 index 000000000..f271c4d57 --- /dev/null +++ b/PyTAT/tests/test_qr_with_numpy.py @@ -0,0 +1,32 @@ +import numpy as np +import TAT + + +def test_main(): + Tensor = TAT.No.D.Tensor + + max_random = 8 + + for _ in range(100): + rank_A = np.random.randint(2, max_random) + rank_contract = np.random.randint(1, rank_A) + U_leg = np.random.choice(range(rank_A), rank_contract, False) + + dim_A = np.random.randint(1, max_random, size=rank_A) + + A = Tensor([f"A.{i}" for i in range(rank_A)], dim_A.tolist()).randn() + + Q, R = A.qr("Q", {f"A.{i}" for i in U_leg}, "QR.Q", "QR.R") + re_A = Q.contract(R, {("QR.Q", "QR.R")}) + diff = re_A - A + + QTQ = Q.contract(Q.edge_rename({"QR.Q": "new"}), {(name, name) for name in Q.names if name != "QR.Q"}) + QTQ = QTQ.blocks[QTQ.names] + + diff_Q = QTQ - np.identity(len(QTQ)) + + assert np.max([diff.norm_max(), np.max(np.abs(diff_Q))]) < 1e-6 + R_block = R.blocks[R.names] + # print(R_block.shape) + # print(R_block.reshape([-1, R_block.shape[-1]])) + # print(R_block.reshape([R_block.shape[0], -1])) diff --git a/PyTAT/tests/test_random.py b/PyTAT/tests/test_random.py new file mode 100644 index 000000000..16b2b7a7d --- /dev/null +++ b/PyTAT/tests/test_random.py @@ -0,0 +1,19 @@ +import TAT + + +def test_seed(): + TAT.random.seed(233) + x = TAT.random.uniform_int(0, 100)() + TAT.random.seed(233) + y = TAT.random.uniform_int(0, 100)() + assert x == y + + +def test_uniform_int(): + rg = TAT.random.uniform_int(0, 100) + assert all(0 <= rg() <= 100 for _ in range(10000)) + + +def test_uniform_real(): + rg = TAT.random.uniform_real(0, 100) + assert all(0 <= rg() <= 100 for _ in range(10000)) diff --git a/PyTAT/tests/test_scalar.py b/PyTAT/tests/test_scalar.py new file mode 100644 index 000000000..4e61de212 --- /dev/null +++ b/PyTAT/tests/test_scalar.py @@ -0,0 +1,116 @@ +import TAT + + +def test_tensor_and_number(): + import numpy as np + a = TAT.Z2.Z.Tensor(["Left", "Right", "Phy"], [ + [(False, 2), (True, 2)], + [(False, 2), (True, 2)], + [(False, 2), (True, 2)], + ]).range() + assert a.storage.size == 32 + s_a = np.arange(32, dtype=float) + assert all(a.storage == s_a) + b = a + 1.0 + s_b = s_a + 1.0 + assert all(b.storage == s_b) + c = 1.0 + a + s_c = 1.0 + s_a + assert all(c.storage == s_c) + d = a - 1.0 + s_d = s_a - 1.0 + assert all(d.storage == s_d) + e = 1.0 - a + s_e = 1.0 - s_a + assert all(e.storage == s_e) + f = a * 1.5 + s_f = s_a * 1.5 + assert all(f.storage == s_f) + g = 1.5 * a + s_g = 1.5 * s_a + assert all(g.storage == s_g) + h = a / 1.5 + s_h = s_a / 1.5 + assert all(h.storage == s_h) + i = 1.5 / (a + 1) + s_i = 1.5 / (s_a + 1) + assert all(i.storage == s_i) + + +def test_tensor_and_tensor(): + import numpy as np + a = TAT.No.D.Tensor(["Left", "Right"], [3, 4]).range() + b = TAT.No.D.Tensor(["Left", "Right"], [3, 4]).range(0, 0.1) + s_a = np.zeros(12) + s_b = np.zeros(12) + s_a[0] = s_b[0] = 0 + for i in range(1, 12): + s_a[i] = s_a[i - 1] + 1 + s_b[i] = s_b[i - 1] + 0.1 + assert a.storage.size == s_a.size + assert b.storage.size == s_b.size + assert all(a.storage == s_a) + assert all(b.storage == s_b) + c = a + b + s_c = s_a + s_b + assert all(c.storage == s_c) + d = a - b + s_d = s_a - s_b + assert all(d.storage == s_d) + e = a * b + s_e = s_a * s_b + assert all(e.storage == s_e) + f = a / (b + 1) + s_f = s_a / (s_b + 1) + assert all(f.storage == s_f) + + +def test_tensor_and_number_inplace(): + import numpy as np + a = TAT.Z2.Z.Tensor(["Left", "Right", "Phy"], [ + [(False, 2), (True, 2)], + [(False, 2), (True, 2)], + [(False, 2), (True, 2)], + ]).range() + s_a = np.arange(32, dtype=float) + assert all(a.storage == s_a) + a += 1.5 + s_a += 1.5 + assert all(a.storage == s_a) + a *= 0.9 + s_a *= 0.9 + assert all(a.storage == s_a) + a -= 0.1 + s_a -= 0.1 + assert all(a.storage == s_a) + a /= 2.0 + s_a /= 2.0 + assert all(a.storage == s_a) + + +def test_tensor_and_tensor_inplace(): + import numpy as np + a = TAT.No.D.Tensor(["Left", "Right"], [3, 4]).range() + b = TAT.No.D.Tensor(["Left", "Right"], [3, 4]).range(0, 0.1) + s_a = np.zeros(12) + s_b = np.zeros(12) + s_a[0] = s_b[0] = 0 + for i in range(1, 12): + s_a[i] = s_a[i - 1] + 1 + s_b[i] = s_b[i - 1] + 0.1 + assert a.storage.size == s_a.size + assert b.storage.size == s_b.size + assert all(a.storage == s_a) + assert all(b.storage == s_b) + a += b + s_a += s_b + assert all(a.storage == s_a) + a *= b + s_a *= s_b + assert all(a.storage == s_a) + a -= b + s_a -= s_b + assert all(a.storage == s_a) + a /= b + 1 + s_a /= s_b + 1 + assert all(a.storage == s_a) diff --git a/PyTAT/tests/test_split_and_merge.py b/PyTAT/tests/test_split_and_merge.py new file mode 100644 index 000000000..d7cbd5f7b --- /dev/null +++ b/PyTAT/tests/test_split_and_merge.py @@ -0,0 +1,100 @@ +import TAT + + +def test_no_symmetry_basic(): + a = TAT.No.D.Tensor(["Left", "Right"], [2, 3]).range() + + b = a.merge_edge({"Merged": ["Left", "Right"]}) + assert all(b.storage == a.storage) + b_a = b.split_edge({"Merged": [("Left", 2), ("Right", 3)]}) + assert (b_a - a).norm_max() == 0 + + c = a.merge_edge({"Merged": ["Right", "Left"]}) + assert all(c.storage == a.transpose(["Right", "Left"]).storage) + c_a = c.split_edge({"Merged": [("Right", 3), ("Left", 2)]}) + assert (c_a - a).norm_max() == 0 + + +def test_no_symmetry_high_dimension(): + a = TAT.No.D.Tensor(["1", "2", "3", "4", "5", "6", "7", "8"], [2, 2, 2, 2, 2, 2, 2, 2]).range() + for i in range(8): + for j in range(i, 8): + names = a.names[i:j] + plans = [(name, 2) for name in names] + b = a.merge_edge({"m": names}) + assert all(b.storage == a.storage) + c = b.split_edge({"m": plans}) + assert (c - a).norm_max() == 0 + + +def test_u1_symmetry_basic(): + a = TAT.U1.D.Tensor(["i", "j"], [[-1, 0, +1], [-1, 0, +1]]).range() + d = TAT.U1.D.Tensor(["m"], [(-2, 1), (-1, 2), (0, 3), (+1, 2), (+2, 1)]).range() + + b = a.merge_edge({"m": ["i", "j"]}) + assert (d - b).norm_max() == 0 + c = b.split_edge({"m": [("i", [-1, 0, +1]), ("j", [-1, 0, +1])]}) + assert (c - a).norm_max() == 0 + + +def test_u1_symmetry_high_dimension(): + edge = [(-1, 2), (0, 2), (+1, 2)] + a = TAT.U1.D.Tensor(["1", "2", "3", "4", "5"], [edge, edge, edge, edge, edge]).range() + for i in range(5): + for j in range(i, 5): + names = a.names[i:j] + plans = [(name, edge) for name in names] + b = a.merge_edge({"m": names}) + c = b.split_edge({"m": plans}) + assert (c - a).norm_max() == 0 + + +def test_fermi_symmetry_high_dimension(): + edge = [(-1, 2), (0, 2), (+1, 2)] + a = TAT.Fermi.D.Tensor(["1", "2", "3", "4", "5"], [edge, edge, edge, edge, edge]).range() + for i in range(5): + for j in range(i, 5): + for p in [False, True]: + names = a.names[i:j] + plans = [(name, edge) for name in names] + b = a.merge_edge({"m": names}, p) + c = b.split_edge({"m": plans}, p) + assert (c - a).norm_max() == 0 + + +def test_fermi_symmetry_high_dimension_compare_u1(): + edge = [(-1, 1), (0, 1), (+1, 1)] + a_u1 = TAT.U1.D.Tensor(["1", "2", "3", "4", "5"], [edge, edge, edge, edge, edge]).range() + a_f = TAT.Fermi.D.Tensor(["1", "2", "3", "4", "5"], [edge, edge, edge, edge, edge]).range() + for i in range(5): + for j in range(i, 5): + for p in [False, True]: + names = a_u1.names[i:j] + plans = [(name, edge) for name in names] + b_u1 = a_u1.merge_edge({"m": names}) + b_f = a_f.merge_edge({"m": names}, p) + if p: + s = [0, 0, 0, 0, 0] + for s[0] in [-1, 0, +1]: + for s[1] in [-1, 0, +1]: + for s[2] in [-1, 0, +1]: + for s[3] in [-1, 0, +1]: + for s[4] in [-1, 0, +1]: + if sum(s) != 0: + continue + item = a_u1[{ + "1": (s[0], 0), + "2": (s[1], 0), + "3": (s[2], 0), + "4": (s[3], 0), + "5": (s[4], 0), + }] + assert item in b_u1.storage + count = sum(s[x] != 0 for x in range(i, j)) + parity = count & 2 + if parity: + assert -item in b_f.storage + else: + assert item in b_f.storage + else: + assert all(b_u1.storage == b_f.storage) diff --git a/PyTAT/tests/test_split_to_no_edge.py b/PyTAT/tests/test_split_to_no_edge.py new file mode 100644 index 000000000..6dd00e733 --- /dev/null +++ b/PyTAT/tests/test_split_to_no_edge.py @@ -0,0 +1,11 @@ +import TAT + + +def test_basic_usage_0(): + a = TAT.Z2.S.Tensor(["i", "j"], [[(False, 2)], [(False, 1)]]).range() + b = a.split_edge({"i": [("k", [(False, 2)])], "j": []}) + + +def test_basic_usage_1(): + a = TAT.Z2.S.Tensor(["i", "j"], [[(False, 2), (True, 2)], [(False, 1)]]).range() + b = a.split_edge({"i": [("k", [(False, 2), (True, 2)])], "j": []}) diff --git a/PyTAT/tests/test_svd.py b/PyTAT/tests/test_svd.py new file mode 100644 index 000000000..c803aa423 --- /dev/null +++ b/PyTAT/tests/test_svd.py @@ -0,0 +1,128 @@ +import TAT + + +def check_unitary(tensor, name, name_prime, fermi): + pairs = set() + for n in tensor.names: + if n != name: + pairs.add((n, n)) + conjugated = tensor.conjugate(True).edge_rename({name: name_prime}) + product = tensor.contract(conjugated, pairs) + identity = product.same_shape().identity({(name, name_prime)}) + if fermi: + product.transform(abs) + identity.transform(abs) + assert (product - identity).norm_max() < 1e-6 + + +def test_no_symmetry(): + a = TAT.No.D.Tensor(["A", "B", "C", "D"], [2, 3, 4, 5]).range() + u, s, v = a.svd({"C", "A"}, "E", "F", "U", "V") + check_unitary(u, "E", "E'", False) + check_unitary(v, "F", "F'", False) + b = v.contract(s, {("F", "V")}).contract(u, {("U", "E")}) + assert (a - b).norm_max() < 1e-6 + + +def test_no_symmetry_cut(): + a = TAT.No.D.Tensor(["A", "B", "C", "D"], [5, 4, 3, 2]).range() + u, s, v = a.svd({"C", "A"}, "E", "F", "U", "V", 2) + check_unitary(u, "E", "E'", False) + check_unitary(v, "F", "F'", False) + b = v.contract(s, {("F", "V")}).contract(u, {("U", "E")}) + assert (a - b).norm_max() < 1e-6 + + +def test_u1_symmetry(): + a = TAT.U1.D.Tensor( + ["A", "B", "C", "D"], + [ + ([(-1, 1), (0, 1), (-2, 1)], True), + ([(0, 1), (1, 2)], False), + ([(0, 2), (1, 2)], False), + ([(-2, 2), (-1, 1), (0, 2)], True), + ], + ).range() + u, s, v = a.svd({"C", "A"}, "E", "F", "U", "V") + check_unitary(u, "E", "E'", False) + check_unitary(v, "F", "F'", False) + b = v.contract(s, {("F", "V")}).contract(u, {("U", "E")}) + assert (a - b).norm_max() < 1e-6 + + +def test_u1_symmetry_cut(): + a = TAT.U1.D.Tensor( + ["A", "B", "C", "D"], + [ + ([(-1, 1), (0, 1), (-2, 1)], True), + ([(0, 1), (1, 2)], False), + ([(0, 2), (1, 2)], False), + ([(-2, 2), (-1, 1), (0, 2)], True), + ], + ).range() + u, s, v = a.svd({"C", "A"}, "E", "F", "U", "V", 7) + check_unitary(u, "E", "E'", False) + check_unitary(v, "F", "F'", False) + b = v.contract(s, {("F", "V")}).contract(u, {("U", "E")}) + assert (a - b).norm_max() < 1e-6 + + +def test_fermi_symmetry(): + a = TAT.Fermi.D.Tensor( + ["A", "B", "C", "D"], + [ + ([(-1, 1), (0, 1), (-2, 1)], True), + ([(0, 1), (1, 2)], False), + ([(0, 2), (1, 2)], False), + ([(-2, 2), (-1, 1), (0, 2)], True), + ], + ).range() + u, s, v = a.svd({"C", "A"}, "E", "F", "U", "V") + check_unitary(u, "E", "E'", True) + check_unitary(v, "F", "F'", True) + b = v.contract(s, {("F", "V")}).contract(u, {("U", "E")}) + assert (a - b).norm_max() < 1e-6 + + +def test_fermi_symmetry_cut(): + a = TAT.Fermi.D.Tensor( + ["A", "B", "C", "D"], + [ + ([(-1, 1), (0, 1), (-2, 1)], True), + ([(0, 1), (1, 2)], False), + ([(0, 2), (1, 2)], False), + ([(-2, 2), (-1, 1), (0, 2)], True), + ], + ).range() + u, s, v = a.svd({"B", "D"}, "E", "F", "U", "V", 8) + check_unitary(u, "E", "E'", True) + check_unitary(v, "F", "F'", True) + b = v.contract(s, {("F", "V")}).contract(u, {("U", "E")}) + assert (a - b).norm_max() < 1e-6 + + +def test_no_symmetry_cut_too_small(): + a = TAT.No.D.Tensor(["A", "B"], [2, 2]).zero() + a[{"A": 0, "B": 0}] = 1 + u, s, v = a.svd({"B"}, "E", "F", "U", "V", 8) + check_unitary(u, "E", "E'", False) + check_unitary(v, "F", "F'", False) + b = v.contract(s, {("F", "V")}).contract(u, {("U", "E")}) + assert (a - b).norm_max() < 1e-6 + assert s.storage.size == 1 + + +def test_fermi_symmetry_cut_too_small(): + a = TAT.Fermi.D.Tensor( + ["A", "B"], + [ + [(0, 1), (+1, 1)], + [(-1, 1), (0, 1)], + ], + ).range(0, 1) + u, s, v = a.svd({"B"}, "E", "F", "U", "V", 8) + check_unitary(u, "E", "E'", True) + check_unitary(v, "F", "F'", True) + b = v.contract(s, {("F", "V")}).contract(u, {("U", "E")}) + assert (a - b).norm_max() < 1e-6 + assert s.storage.size == 1 diff --git a/PyTAT/tests/test_svd_with_numpy.py b/PyTAT/tests/test_svd_with_numpy.py new file mode 100644 index 000000000..567e4be92 --- /dev/null +++ b/PyTAT/tests/test_svd_with_numpy.py @@ -0,0 +1,31 @@ +import numpy as np +import TAT + + +def test_main(): + Tensor = TAT.No.D.Tensor + + max_random = 8 + + for _ in range(1000): + rank_A = np.random.randint(2, max_random) + rank_contract = np.random.randint(1, rank_A) + U_leg = np.random.choice(range(rank_A), rank_contract, False) + + dim_A = np.random.randint(1, max_random, size=rank_A) + + A = Tensor([f"A.{i}" for i in range(rank_A)], dim_A.tolist()).randn() + + U, S, V = A.svd({f"A.{i}" for i in U_leg}, "SVD.U", "SVD.V", "S.U", "S.V") + re_A = U.contract(S, {("SVD.U", "S.U")}).edge_rename({"S.V": "SVD.U"}).contract(V, {("SVD.U", "SVD.V")}) + diff = re_A - A + + UTU = U.contract(U.edge_rename({"SVD.U": "new"}), {(name, name) for name in U.names if name != "SVD.U"}) + UTU = UTU.blocks[UTU.names] + VTV = V.contract(V.edge_rename({"SVD.V": "new"}), {(name, name) for name in V.names if name != "SVD.V"}) + VTV = VTV.blocks[VTV.names] + + diff_U = UTU - np.identity(len(UTU)) + diff_V = VTV - np.identity(len(VTV)) + + assert np.max([diff.norm_max(), np.max(np.abs(diff_U)), np.max(np.abs(diff_V))]) < 1e-6 diff --git a/PyTAT/tests/test_symmetry.py b/PyTAT/tests/test_symmetry.py new file mode 100644 index 000000000..7b025c0ea --- /dev/null +++ b/PyTAT/tests/test_symmetry.py @@ -0,0 +1,65 @@ +import TAT + + +def test_create_symmetry(): + assert TAT.No.Symmetry() == TAT.No.Symmetry(()) + + assert TAT.Z2.Symmetry() == TAT.Z2.Symmetry(False) == TAT.Z2.Symmetry((False,)) + assert TAT.Z2.Symmetry(True).z2 == True + + assert TAT.U1.Symmetry() == TAT.U1.Symmetry(0) == TAT.U1.Symmetry((0,)) + assert TAT.U1.Symmetry(2).u1 == 2 + + assert TAT.Fermi.Symmetry() == TAT.Fermi.Symmetry(0) == TAT.Fermi.Symmetry((0,)) + assert TAT.Fermi.Symmetry(2).fermi == 2 + + assert TAT.FermiZ2.Symmetry() == TAT.FermiZ2.Symmetry(0, False) == TAT.FermiZ2.Symmetry((0, False)) + assert TAT.FermiZ2.Symmetry(2, True).fermi == 2 + assert TAT.FermiZ2.Symmetry(2, True).z2 == True + + assert TAT.FermiU1.Symmetry() == TAT.FermiU1.Symmetry(0, 0) == TAT.FermiU1.Symmetry((0, 0)) + assert TAT.FermiU1.Symmetry(2, 3).fermi == 2 + assert TAT.FermiU1.Symmetry(2, 3).u1 == 3 + + assert TAT.Parity.Symmetry() == TAT.Parity.Symmetry(False) == TAT.Parity.Symmetry((False,)) + assert TAT.Parity.Symmetry(True).parity == True + + assert TAT.FermiFermi.Symmetry() == TAT.FermiFermi.Symmetry(0, 0) == TAT.FermiFermi.Symmetry((0, 0)) + assert TAT.FermiFermi.Symmetry(2, 3).fermi_0 == 2 + assert TAT.FermiFermi.Symmetry(2, 3).fermi_1 == 3 + + +def test_compare(): + assert TAT.FermiZ2.Symmetry(4, False) < TAT.FermiZ2.Symmetry(5, False) + assert TAT.FermiZ2.Symmetry(5, True) >= TAT.FermiZ2.Symmetry(4, True) + assert TAT.FermiZ2.Symmetry(4, False) < TAT.FermiZ2.Symmetry(4, True) + assert TAT.FermiZ2.Symmetry(4, False) <= TAT.FermiZ2.Symmetry(5, True) + assert TAT.FermiZ2.Symmetry(5, False) > TAT.FermiZ2.Symmetry(4, True) + + +def test_arithmetic(): + assert -TAT.FermiZ2.Symmetry(4, False) == TAT.FermiZ2.Symmetry(-4, False) + assert -TAT.FermiZ2.Symmetry(4, True) == TAT.FermiZ2.Symmetry(-4, True) + assert TAT.FermiZ2.Symmetry(2, True) + TAT.FermiZ2.Symmetry(-1, True) == TAT.FermiZ2.Symmetry(1, False) + assert TAT.FermiZ2.Symmetry(2, False) - TAT.FermiZ2.Symmetry(-1, True) == TAT.FermiZ2.Symmetry(3, True) + + +def test_parity(): + assert TAT.U1.Symmetry(233).parity == False + assert TAT.Fermi.Symmetry(233).parity == True + assert TAT.FermiU1.Symmetry(1, 2).parity == True + assert TAT.FermiZ2.Symmetry(2, True).parity == False + assert TAT.FermiFermi.Symmetry(2, 3).parity == True + assert TAT.FermiFermi.Symmetry(3, 3).parity == False + + +def test_hash(): + assert hash(TAT.Fermi.Symmetry(4)) == hash(TAT.U1.Symmetry(4)) + assert hash(TAT.Fermi.Symmetry(4)) != hash(TAT.Fermi.Symmetry(5)) + + +def test_io(): + import pickle + + e = TAT.FermiZ2.Symmetry(233, True) + assert e == pickle.loads(pickle.dumps(e)) diff --git a/PyTAT/tests/test_trace.py b/PyTAT/tests/test_trace.py new file mode 100644 index 000000000..cdfb08af4 --- /dev/null +++ b/PyTAT/tests/test_trace.py @@ -0,0 +1,106 @@ +import TAT + + +def trace_two(tensor, pairs, fuses={}): + traced_tensor_0 = tensor.trace(pairs, fuses) + + double_names = set() + names = [] + edges = [] + for n0, n1 in pairs: + double_names.add((n0, n0)) + double_names.add((n1, n1)) + names.append(n0) + names.append(n1) + edges.append(tensor.edges(n0).conjugated()) + edges.append(tensor.edges(n1).conjugated()) + identity = tensor.__class__(names, edges).identity(pairs) + if fuses: + for out, [in_0, in_1] in fuses.items(): + dimension = tensor.edges(in_0).dimension + tee = tensor.__class__([out, in_0, in_1], [dimension, dimension, dimension]).zero() + for i in range(dimension): + tee[{out: i, in_0: i, in_1: i}] = 1 + identity = identity.contract(tee, set()) + double_names.add((in_0, in_0)) + double_names.add((in_1, in_1)) + traced_tensor_1 = tensor.contract(identity, double_names) + + assert (traced_tensor_0 - traced_tensor_1).norm_max() == 0 + return traced_tensor_0, traced_tensor_1 + + +def test_no_symmetry(): + trace_two(TAT.No.D.Tensor( + ["A", "B", "C", "D", "E"], + [2, 3, 2, 3, 4], + ).range(), {("A", "C"), ("B", "D")}) + trace_two(TAT.No.D.Tensor( + ["A", "B", "C"], + [2, 3, 2], + ).range(), {("A", "C")}) + a = TAT.No.D.Tensor(["A", "B", "C"], [4, 3, 5]).range() + b = TAT.No.D.Tensor(["D", "E", "F"], [5, 4, 6]).range() + trace_two(a.contract(b, set()), {("A", "E"), ("C", "D")}) + + +def test_u1_symmetry(): + a = TAT.U1.D.Tensor( + ["A", "B", "C", "D"], + [ + ([(-1, 1), (0, 1), (-2, 1)], True), + ([(0, 1), (1, 2)], False), + ([(0, 2), (1, 2)], False), + ([(0, 2), (-1, 1), (-2, 2)], True), + ], + ).range() + b = TAT.U1.D.Tensor( + ["E", "F", "G", "H"], + [ + ([(0, 2), (1, 1)], False), + ([(-2, 1), (-1, 1), (0, 2)], True), + ([(0, 1), (-1, 2)], True), + ([(0, 2), (1, 1), (2, 2)], False), + ], + ).range() + c = a.contract(b, set()) + d = trace_two(c, {("B", "G")}) + e = trace_two(d[0], {("H", "D")}) + f = trace_two(c, {("G", "B"), ("D", "H")}) + assert (e[0] - f[0]).norm_max() == 0 + + +def test_fermi_symmetry(): + a = TAT.Fermi.D.Tensor( + ["A", "B", "C", "D"], + [ + ([(-1, 1), (0, 1), (-2, 1)], True), + ([(0, 1), (1, 2)], False), + ([(0, 2), (1, 2)], False), + ([(-2, 2), (-1, 1), (0, 2)], True), + ], + ).range() + b = TAT.Fermi.D.Tensor( + ["E", "F", "G", "H"], + [ + ([(0, 2), (1, 1)], False), + ([(-2, 1), (-1, 1), (0, 2)], True), + ([(0, 1), (-1, 2)], True), + ([(2, 2), (1, 1), (0, 2)], False), + ], + ).range() + c = a.contract(b, set()) + d = trace_two(c, {("B", "G")}) + e = trace_two(d[0], {("H", "D")}) + f = trace_two(c, {("G", "B"), ("D", "H")}) + assert (e[0] - f[0]).norm_max() == 0 + + +def test_fuse(): + a = TAT.No.D.Tensor(["A", "B", "C", "D"], [4, 4, 4, 4]).range() + b = TAT.No.D.Tensor(["E", "F", "G", "H"], [4, 4, 4, 4]).range() + c = a.contract(b, set()) + d = trace_two(c, {("B", "G")}, {"X": ("C", "F")}) + e = trace_two(d[0], set(), {"Y": ("A", "H")}) + f = trace_two(c, {("B", "G")}, {"X": ("F", "C"), "Y": ("A", "H")}) + assert (e[0] - f[0]).norm_max() == 0 diff --git a/PyTAT/tests/test_trace_with_numpy.py b/PyTAT/tests/test_trace_with_numpy.py new file mode 100644 index 000000000..262babd3d --- /dev/null +++ b/PyTAT/tests/test_trace_with_numpy.py @@ -0,0 +1,44 @@ +import numpy as np +import TAT + + +def test_main(): + Tensor = TAT.No.D.Tensor + + max_random = 8 + + for _ in range(100): + rank_A = np.random.randint(3, max_random) + rank_trace = np.random.randint(1, rank_A // 2 + 1) + pair_leg = np.random.choice(range(rank_A), [rank_trace, 2], False) + + name_list = [f"A.{i}" for i in range(rank_A)] + dim_list = np.random.randint(2, max_random, rank_A) + dim_trace = np.random.randint(2, max_random, rank_trace) + for i, (j, k) in enumerate(pair_leg): + dim_list[j] = dim_trace[i] + dim_list[k] = dim_trace[i] + + trace_conf = {(f"A.{i}", f"A.{j}") for i, j in pair_leg} + + A = Tensor(name_list, dim_list.tolist()).range() + B = A.trace(trace_conf) + + res = A.blocks[A.names] + for i in range(rank_trace): + res = res.trace(0, pair_leg[i, 0], pair_leg[i, 1]) + for j in range(i + 1, rank_trace): + if pair_leg[j, 0] > pair_leg[i, 0]: + pair_leg[j, 0] -= 1 + if pair_leg[j, 1] > pair_leg[i, 0]: + pair_leg[j, 1] -= 1 + if pair_leg[i, 1] > pair_leg[i, 0]: + pair_leg[i, 1] -= 1 + if pair_leg[j, 0] > pair_leg[i, 1]: + pair_leg[j, 0] -= 1 + if pair_leg[j, 1] > pair_leg[i, 1]: + pair_leg[j, 1] -= 1 + + diff = res - B.blocks[B.names] + + assert np.max(np.abs(diff)) < 1e-6 diff --git a/PyTAT/tests/test_transform_map_and_set.py b/PyTAT/tests/test_transform_map_and_set.py new file mode 100644 index 000000000..3dc9af183 --- /dev/null +++ b/PyTAT/tests/test_transform_map_and_set.py @@ -0,0 +1,39 @@ +import TAT + + +def test_transform(): + t = TAT.No.D.Tensor(["i", "j"], [2, 3]).range() + assert all(t.storage == [0, 1, 2, 3, 4, 5]) + t.transform(lambda x: x + 1) + assert all(t.storage == [1, 2, 3, 4, 5, 6]) + + +def test_map(): + t = TAT.No.D.Tensor(["i", "j"], [2, 3]).range() + assert all(t.storage == [0, 1, 2, 3, 4, 5]) + z = t.map(lambda x: x + 1) + assert all(z.storage == [1, 2, 3, 4, 5, 6]) + assert all(t.storage == [0, 1, 2, 3, 4, 5]) + + +def test_copy(): + t = TAT.No.D.Tensor(["i", "j"], [2, 3]).range() + assert all(t.storage == [0, 1, 2, 3, 4, 5]) + s = t.copy() + assert all(s.storage == [0, 1, 2, 3, 4, 5]) + + +def test_set(): + s = iter([6, 2, 8, 3, 7, 1]) + t = TAT.No.D.Tensor(["i", "j"], [2, 3]).set(lambda: next(s)) + assert all(t.storage == [6, 2, 8, 3, 7, 1]) + + +def test_zero(): + t = TAT.No.D.Tensor(["i", "j"], [2, 3]).zero() + assert all(t.storage == [0, 0, 0, 0, 0, 0]) + + +def test_range(): + t = TAT.No.D.Tensor(["i", "j"], [2, 3]).range(3, 2) + assert all(t.storage == [3, 5, 7, 9, 11, 13]) diff --git a/PyTAT/tests/test_transpose.py b/PyTAT/tests/test_transpose.py new file mode 100644 index 000000000..eeadae73d --- /dev/null +++ b/PyTAT/tests/test_transpose.py @@ -0,0 +1,139 @@ +import TAT + + +def test_no_symmetry_basic_0(): + a = TAT.No.D.Tensor(["Left", "Right"], [2, 3]).range() + b = a.transpose(["Right", "Left"]) + assert all(a.storage == [0, 1, 2, 3, 4, 5]) + assert all(b.storage == [0, 3, 1, 4, 2, 5]) + + +def test_no_symmetry_basic_1(): + a = TAT.No.D.Tensor(["i", "j", "k"], [2, 3, 4]).range() + for result_edge in [ + ["i", "j", "k"], + ["i", "k", "j"], + ["j", "k", "i"], + ["j", "i", "k"], + ["k", "i", "j"], + ["k", "j", "i"], + ]: + b = a.transpose(result_edge) + for i in range(2): + for j in range(3): + for k in range(4): + assert a[{"i": i, "j": j, "k": k}] == b[{"i": i, "j": j, "k": k}] + + +def test_no_symmetry_high_dimension(): + a = TAT.No.D.Tensor(["i", "j", "k", "l", "m", "n"], [2, 2, 2, 2, 2, 2]).range() + b = a.transpose(["l", "j", "i", "n", "k", "m"]) + for i in range(2): + for j in range(2): + for k in range(2): + for l in range(2): + for m in range(2): + for n in range(2): + assert a[{ + "i": i, + "j": j, + "k": k, + "l": l, + "m": m, + "n": n + }] == b[{ + "i": i, + "j": j, + "k": k, + "l": l, + "m": m, + "n": n + }] + + +def test_z2_symmetry_high_dimension(): + edge = TAT.Z2.Edge([(False, 2), (True, 2)]) + a = TAT.Z2.D.Tensor(["i", "j", "k", "l", "m", "n"], [edge, edge, edge, edge, edge, edge]).range() + b = a.transpose(["l", "j", "i", "n", "k", "m"]) + for i in range(2): + for j in range(2): + for k in range(2): + for l in range(2): + for m in range(2): + for n in range(2): + p_i = i & 2 != 0 + p_j = j & 2 != 0 + p_k = k & 2 != 0 + p_l = l & 2 != 0 + p_m = m & 2 != 0 + p_n = n & 2 != 0 + if p_i ^ p_j ^ p_k ^ p_l ^ p_m ^ p_n: + continue + assert a[{ + "i": i, + "j": j, + "k": k, + "l": l, + "m": m, + "n": n + }] == b[{ + "i": i, + "j": j, + "k": k, + "l": l, + "m": m, + "n": n + }] + + +def test_parity_symmetry_high_dimension(): + edge = TAT.Parity.Edge([(False, 2), (True, 2)]) + a = TAT.Parity.D.Tensor(["i", "j", "k", "l", "m", "n"], [edge, edge, edge, edge, edge, edge]).range() + b = a.transpose(["l", "j", "i", "n", "k", "m"]) + for i in range(2): + for j in range(2): + for k in range(2): + for l in range(2): + for m in range(2): + for n in range(2): + p_i = i & 2 != 0 + p_j = j & 2 != 0 + p_k = k & 2 != 0 + p_l = l & 2 != 0 + p_m = m & 2 != 0 + p_n = n & 2 != 0 + if p_i ^ p_j ^ p_k ^ p_l ^ p_m ^ p_n: + continue + parity = (p_l and (p_i ^ p_j ^ p_k)) ^ (p_j and p_i) ^ (p_n and (p_k ^ p_m)) + if parity: + assert -a[{ + "i": i, + "j": j, + "k": k, + "l": l, + "m": m, + "n": n + }] == b[{ + "i": i, + "j": j, + "k": k, + "l": l, + "m": m, + "n": n + }] + else: + assert a[{ + "i": i, + "j": j, + "k": k, + "l": l, + "m": m, + "n": n + }] == b[{ + "i": i, + "j": j, + "k": k, + "l": l, + "m": m, + "n": n + }] diff --git a/PyTAT/tests/test_type_conversion.py b/PyTAT/tests/test_type_conversion.py new file mode 100644 index 000000000..283201485 --- /dev/null +++ b/PyTAT/tests/test_type_conversion.py @@ -0,0 +1,38 @@ +import TAT + + +def test_dummy(): + import numpy as np + a = TAT.No.D.Tensor(["Left", "Right"], [3, 4]).range(2) + b = a.to("float64") + assert np.shares_memory(a.storage, b.storage) + c = a.to("float32") + assert not np.shares_memory(a.storage, c.storage) + + +def test_inside_float(): + a = TAT.No.D.Tensor(["Left", "Right"], [3, 4]).range(2) + b = a.to("float32") + assert all(a.storage == [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]) + assert all(b.storage == [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]) + + +def test_inside_complex(): + a = TAT.No.Z.Tensor(["Left", "Right"], [3, 4]).range(2) + b = a.to("complex64") + assert all(a.storage == [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]) + assert all(b.storage == [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]) + + +def test_complex_to_float(): + a = TAT.No.Z.Tensor(["Left", "Right"], [3, 4]).range(2) + b = a.to("float32") + assert all(a.storage == [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]) + assert all(b.storage == [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]) + + +def test_float_to_complex(): + a = TAT.No.D.Tensor(["Left", "Right"], [3, 4]).range(2) + b = a.to("complex64") + assert all(a.storage == [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]) + assert all(b.storage == [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]) diff --git a/tests/test_contract.cpp b/tests/test_contract.cpp index 843c5afc1..3f6d42bf4 100644 --- a/tests/test_contract.cpp +++ b/tests/test_contract.cpp @@ -38,6 +38,7 @@ TEST(test_contract, no_symmetry_example_1) { auto b = TAT::Tensor{{"A", "C", "E", "F"}, {1, 3, 3, 1}}.set( [i = 0, v = std::vector{316, 796, 1276, 428, 1164, 1900, 540, 1532, 2524}]() mutable { return v[i++]; } ); + ASSERT_FLOAT_EQ((a - b).norm<-1>(), 0); } TEST(test_contract, u1_symmetry_example_0) {