Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions grassmann_tensor/tensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -970,6 +970,36 @@ def identity(self, pairs: tuple[tuple[int, ...], tuple[int, ...]]) -> GrassmannT

return tensor_identity

def conjugate(self) -> GrassmannTensor:
tensor_conj = self.tensor.conj()

dim = self.tensor.dim()
parity = self.parity

total_parity = functools.reduce(
torch.logical_xor,
(
torch.logical_and(
self._unsqueeze(parity[i], i, dim),
self._unsqueeze(parity[j], j, dim),
)
for j in range(dim)
for i in range(0, j)
),
torch.zeros([], dtype=torch.bool, device=self.tensor.device),
)

tensor_conj = torch.where(total_parity, -tensor_conj, tensor_conj)

return dataclasses.replace(
self,
_arrow=tuple(not arrow for arrow in self.arrow),
_tensor=tensor_conj,
)

def conj(self) -> GrassmannTensor:
return self.conjugate()

def __post_init__(self) -> None:
assert len(self._arrow) == self._tensor.dim(), (
f"Arrow length ({len(self._arrow)}) must match tensor dimensions ({self._tensor.dim()})."
Expand Down
106 changes: 106 additions & 0 deletions tests/conjugate_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import torch
import pytest
from typing import TypeAlias

from grassmann_tensor import GrassmannTensor

Arrow_Edges: TypeAlias = tuple[tuple[bool, ...], tuple[tuple[int, int], ...]]


@pytest.fixture(
params=[
(
(True, False),
((2, 2), (2, 2)),
),
(
(True, False, False),
((2, 2), (2, 2), (2, 2)),
),
]
)
def arrow_edges(request: pytest.FixtureRequest) -> Arrow_Edges:
return request.param


@pytest.fixture(
params=[
(
(True, False),
((4, 0), (4, 0)),
),
(
(True, False, False),
((4, 0), (4, 0), (4, 0)),
),
]
)
def arrow_edges_without_symmetry(request: pytest.FixtureRequest) -> Arrow_Edges:
return request.param


def create_random_tensor(
arrow_edges: Arrow_Edges,
*,
dtype: torch.dtype,
) -> GrassmannTensor:
arrow, edges = arrow_edges
shape = [even + odd for even, odd in edges]

if dtype == torch.float64:
tensor = torch.rand(*shape, dtype=dtype)
else:
tensor = torch.randn(*shape) + 1j * torch.randn(*shape).to(dtype)
return GrassmannTensor(arrow, edges, tensor)


@pytest.fixture
def random_real_tensor(arrow_edges: Arrow_Edges) -> GrassmannTensor:
return create_random_tensor(arrow_edges, dtype=torch.float64)


@pytest.fixture
def random_complex_tensor(arrow_edges: Arrow_Edges) -> GrassmannTensor:
return create_random_tensor(arrow_edges, dtype=torch.complex128)


def test_conjugate_involution_with_complex_tensor(random_complex_tensor: GrassmannTensor) -> None:
contrast_a = random_complex_tensor
contrast_b = contrast_a.conj().conj()
assert contrast_a.arrow == contrast_b.arrow
assert torch.allclose(contrast_a.tensor, contrast_b.tensor)


def test_conjugate_involution_with_real_tensor(random_real_tensor: GrassmannTensor) -> None:
contrast_a = random_real_tensor
contrast_b = contrast_a.conj().conj()
assert contrast_a.arrow == contrast_b.arrow
assert torch.allclose(contrast_a.tensor, contrast_b.tensor)


def test_conjugate_reverse_order_of_contraction(
arrow_edges: Arrow_Edges,
) -> None:
a = create_random_tensor(arrow_edges, dtype=torch.complex128).update_mask()
b = create_random_tensor(arrow_edges, dtype=torch.complex128).update_mask()

contrast_a = GrassmannTensor.contract(a, b, a.tensor.dim() - 1, 0)
contrast_a = contrast_a.conj()

a_conj = a.conj()
b_conj = b.conj()

contrast_b = GrassmannTensor.contract(a_conj, b_conj, a_conj.tensor.dim() - 1, 0)

assert contrast_a.arrow == contrast_b.arrow
assert torch.allclose(contrast_a.tensor, contrast_b.tensor)


def test_conjugate_without_symmetry_equality(
arrow_edges_without_symmetry: Arrow_Edges,
) -> None:
a = create_random_tensor(arrow_edges_without_symmetry, dtype=torch.float64)
b = create_random_tensor(arrow_edges_without_symmetry, dtype=torch.complex128)

assert torch.allclose(a.tensor, a.conj().tensor)
assert torch.allclose(b.tensor.conj(), b.conj().tensor)