Module khive_crystal.decompositions

Functions

def psi(H: khive_crystal.khive.KHive) ‑> list[khive_crystal.khive.KHive]

Decompose KHive into FundamentalKHive by applying the function split repeatedly.

Args

H : KHive
KHive

Returns

List[KHive]
A pair of KHive and FundamentalKHive.

Examples

>>> H: KHive = KHive(
...    n=3,
...    alpha=[3, 3, 0],
...    beta=[2, 3, 1],
...    gamma=[0, 0, 0],
...    Uij=[[1, 0], [1]]
... )
>>> psi(H=H)
[KHive(n=3, alpha=[1, 1, 0], beta=[0, 1, 1], gamma=[0, 0, 0], Uij=[[1, 0], [1]]), KHive(n=3, alpha=[1, 1, 0], beta=[1, 1, 0], gamma=[0, 0, 0], Uij=[[0, 0], [0]]), KHive(n=3, alpha=[1, 1, 0], beta=[1, 1, 0], gamma=[0, 0, 0], Uij=[[0, 0], [0]])]
>>> H: KHive = KHive(
...    n=3,
...    alpha=[3, 1, 0],
...    beta=[2, 2, 0],
...    gamma=[0, 0, 0],
...    Uij=[[1, 0], [0]]
... )
>>> psi(H=H)
[KHive(n=3, alpha=[1, 0, 0], beta=[0, 1, 0], gamma=[0, 0, 0], Uij=[[1, 0], [0]]), KHive(n=3, alpha=[1, 0, 0], beta=[1, 0, 0], gamma=[0, 0, 0], Uij=[[0, 0], [0]]), KHive(n=3, alpha=[1, 1, 0], beta=[1, 1, 0], gamma=[0, 0, 0], Uij=[[0, 0], [0]])]
>>> H: KHive = KHive(
...    n=3,
...    alpha=[1, 1, 0],
...    beta=[1, 0, 1],
...    gamma=[0, 0, 0],
...    Uij=[[0, 0], [1]]
... )
>>> psi(H=H)
[KHive(n=3, alpha=[1, 1, 0], beta=[1, 0, 1], gamma=[0, 0, 0], Uij=[[0, 0], [1]])]
def psi_inv(H: list[khive_crystal.khive.KHive]) ‑> khive_crystal.khive.KHive

Compose tensor product of K-hives to a K-hive by Psi^{-1}.

Args

H : List[KHive]
list of K-hives expected to belong to an image of Psi.

Returns

KHive
Psi^{-1}(H)

Examples

>>> H1: KHive = KHive(n=3, alpha=[1, 1, 0], beta=[1, 1, 0], gamma=[0, 0, 0], Uij=[[0, 0], [0]])
>>> H2: KHive = KHive(n=3, alpha=[1, 1, 0], beta=[0, 1, 1], gamma=[0, 0, 0], Uij=[[1, 0], [1]])
>>> H: List[KHive] = [H1, H2]
>>> psi_inv(H=H)
KHive(n=3, alpha=[2, 2, 0], beta=[1, 2, 1], gamma=[0, 0, 0], Uij=[[1, 0], [1]])
def psi_lambda(H: khive_crystal.khive.KHive) ‑> khive_crystal.khive.KHive | list[khive_crystal.khive.KHive]

Split KHive to a pair of KHive and FundamentalKHive.

Args

H : KHive
KHive

Returns

List[KHive]
A pair of KHive and FundamentalKHive.

Examples

>>> H: KHive = KHive(
...    n=3,
...    alpha=[3, 3, 0],
...    beta=[2, 3, 1],
...    gamma=[0, 0, 0],
...    Uij=[[1, 0], [1]]
... )
>>> psi_lambda(H=H)
[KHive(n=3, alpha=[2, 2, 0], beta=[1, 2, 1], gamma=[0, 0, 0], Uij=[[1, 0], [1]]), KHive(n=3, alpha=[1, 1, 0], beta=[1, 1, 0], gamma=[0, 0, 0], Uij=[[0, 0], [0]])]

Classes

class Compose (H: list[khive_crystal.khive.KHive])

This class has methods to compose tensor products of KHive to a KHive. The entry point of this methods is "run". The function psi_inv is a wrapper of run, then use psi_inv instead of using this class direct.

Expand source code
class Compose:
    """This class has methods to compose tensor products of KHive to a KHive.
    The entry point of this methods is "run". The function psi_inv is a wrapper of run,
    then use psi_inv instead of using this class direct.
    """

    def __init__(self, H: list[KHive]) -> None:
        self.H: list[KHive] = H
        self.validate_is_khive()
        self.validate_khive_size()

    def validate_is_khive(self) -> None:
        """Validate that H is a list of KHive.

        Raises:
            ValueError: If H has an object which is not a Khive, raise error.

        Examples:
            >>> H1: KHive = KHive(n=3, alpha=[1, 1, 0], beta=[1, 1, 0], gamma=[0, 0, 0], Uij=[[0, 0], [0]])
            >>> H: List[int] = [H1, 1]
            >>> Compose(H=H)
            Traceback (most recent call last):
            ...
            ValueError: H must be a list of KHive!
        """  # noqa: B950
        is_valid: bool = all(isinstance(Hi, KHive) for Hi in self.H)
        if not is_valid:
            raise ValueError("H must be a list of KHive!")

    def validate_khive_size(self) -> None:
        """Validate that H.n

        Raises:
            ValueError: If H has an KHive with n != H[0].n. raise error.

        Examples:
            >>> H1: KHive = KHive(n=3, alpha=[1, 1, 0], beta=[1, 1, 0], gamma=[0, 0, 0], Uij=[[0, 0], [0]])
            >>> H2: KHive = KHive(n=2, alpha=[1, 0], beta=[1, 0], gamma=[0, 0], Uij=[[0]])
            >>> H: List[int] = [H1, H2]
            >>> Compose(H=H)
            Traceback (most recent call last):
            ...
            ValueError: All elements of H must have the same n
        """  # noqa: B950
        lead_n: int = self.H[0].n
        is_valid: bool = all(Hi.n == lead_n for Hi in self.H)
        if not is_valid:
            raise ValueError("All elements of H must have the same n")

    def get_n(self) -> int:
        """Get n of H

        Returns:
            int: H[0].n
        """
        return self.H[0].n

    def compose_alpha(self) -> list[int]:
        """Compose alpha_i

        Returns:
            List[int]: alpha of psi^{-1}(H)

        Examples:
            >>> H1: KHive = KHive(n=3, alpha=[1, 1, 0], beta=[1, 1, 0], gamma=[0, 0, 0], Uij=[[0, 0], [0]])
            >>> H: List[int] = [H1, H1]
            >>> Compose(H=H).compose_alpha()
            [2, 2, 0]
        """  # noqa: B950
        return [sum(alpha_i) for alpha_i in zip(*[Hi.alpha for Hi in self.H], strict=False)]

    def compose_beta(self) -> list[int]:
        """Compose beta_i

        Returns:
            List[int]: beta of psi^{-1}(H)

        Examples:
            >>> H1: KHive = KHive(n=3, alpha=[1, 1, 0], beta=[1, 1, 0], gamma=[0, 0, 0], Uij=[[0, 0], [0]])
            >>> H2: KHive = KHive(n=3, alpha=[1, 1, 0], beta=[0, 1, 1], gamma=[0, 0, 0], Uij=[[1, 0], [1]])
            >>> H: List[int] = [H1, H2]
            >>> Compose(H=H).compose_beta()
            [1, 2, 1]
        """  # noqa: B950
        return [sum(beta_i) for beta_i in zip(*[Hi.beta for Hi in self.H], strict=False)]

    def compose_Uij(self) -> list[list[int]]:
        """Compose U_{ij}^{k}

        Returns:
            List[int]: U_{ij} of psi^{-1}(H)

        Examples:
            >>> H1: KHive = KHive(n=3, alpha=[1, 1, 0], beta=[1, 1, 0], gamma=[0, 0, 0], Uij=[[0, 0], [0]])
            >>> H: List[int] = [H1, H1]
            >>> Compose(H=H).compose_Uij()
            [[0, 0], [0]]

            >>> H1: KHive = KHive(n=3, alpha=[1, 1, 0], beta=[1, 1, 0], gamma=[0, 0, 0], Uij=[[0, 0], [0]])
            >>> H2: KHive = KHive(n=3, alpha=[1, 1, 0], beta=[0, 1, 1], gamma=[0, 0, 0], Uij=[[1, 0], [1]])
            >>> H: List[int] = [H1, H2]
            >>> Compose(H=H).compose_Uij()
            [[1, 0], [1]]

            >>> H2: KHive = KHive(n=3, alpha=[1, 1, 0], beta=[0, 1, 1], gamma=[0, 0, 0], Uij=[[1, 0], [1]])
            >>> H: List[int] = [H2, H2]
            >>> Compose(H=H).compose_Uij()
            [[2, 0], [2]]
        """  # noqa: B950
        return [
            [sum(uij) for uij in zip(*Ui, strict=False)] for Ui in zip(*[Hi.Uij for Hi in self.H], strict=False)
        ]

    def run(self) -> KHive:
        """Generate psi^{-1}

        Returns:
            KHive: psi^{-1}(H)

        Examples:
            >>> H1: KHive = KHive(n=3, alpha=[1, 1, 0], beta=[1, 1, 0], gamma=[0, 0, 0], Uij=[[0, 0], [0]])
            >>> H2: KHive = KHive(n=3, alpha=[1, 1, 0], beta=[0, 1, 1], gamma=[0, 0, 0], Uij=[[1, 0], [1]])
            >>> H: List[KHive] = [H1, H2]
            >>> Compose(H=H).run()
            KHive(n=3, alpha=[2, 2, 0], beta=[1, 2, 1], gamma=[0, 0, 0], Uij=[[1, 0], [1]])
        """  # noqa: B950
        n: int = self.get_n()
        alpha: list[int] = self.compose_alpha()
        beta: list[int] = self.compose_beta()
        gamma: list[int] = [0 for _ in range(n)]
        Uij: list[list[int]] = self.compose_Uij()
        return KHive(n=n, alpha=alpha, beta=beta, gamma=gamma, Uij=Uij)

Methods

def compose_Uij(self) ‑> list[list[int]]

Compose U_{ij}^{k}

Returns

List[int]
U_{ij} of psi^{-1}(H)

Examples

>>> H1: KHive = KHive(n=3, alpha=[1, 1, 0], beta=[1, 1, 0], gamma=[0, 0, 0], Uij=[[0, 0], [0]])
>>> H: List[int] = [H1, H1]
>>> Compose(H=H).compose_Uij()
[[0, 0], [0]]
>>> H1: KHive = KHive(n=3, alpha=[1, 1, 0], beta=[1, 1, 0], gamma=[0, 0, 0], Uij=[[0, 0], [0]])
>>> H2: KHive = KHive(n=3, alpha=[1, 1, 0], beta=[0, 1, 1], gamma=[0, 0, 0], Uij=[[1, 0], [1]])
>>> H: List[int] = [H1, H2]
>>> Compose(H=H).compose_Uij()
[[1, 0], [1]]
>>> H2: KHive = KHive(n=3, alpha=[1, 1, 0], beta=[0, 1, 1], gamma=[0, 0, 0], Uij=[[1, 0], [1]])
>>> H: List[int] = [H2, H2]
>>> Compose(H=H).compose_Uij()
[[2, 0], [2]]
def compose_alpha(self) ‑> list[int]

Compose alpha_i

Returns

List[int]
alpha of psi^{-1}(H)

Examples

>>> H1: KHive = KHive(n=3, alpha=[1, 1, 0], beta=[1, 1, 0], gamma=[0, 0, 0], Uij=[[0, 0], [0]])
>>> H: List[int] = [H1, H1]
>>> Compose(H=H).compose_alpha()
[2, 2, 0]
def compose_beta(self) ‑> list[int]

Compose beta_i

Returns

List[int]
beta of psi^{-1}(H)

Examples

>>> H1: KHive = KHive(n=3, alpha=[1, 1, 0], beta=[1, 1, 0], gamma=[0, 0, 0], Uij=[[0, 0], [0]])
>>> H2: KHive = KHive(n=3, alpha=[1, 1, 0], beta=[0, 1, 1], gamma=[0, 0, 0], Uij=[[1, 0], [1]])
>>> H: List[int] = [H1, H2]
>>> Compose(H=H).compose_beta()
[1, 2, 1]
def get_n(self) ‑> int

Get n of H

Returns

int
H[0].n
def run(self) ‑> khive_crystal.khive.KHive

Generate psi^{-1}

Returns

KHive
psi^{-1}(H)

Examples

>>> H1: KHive = KHive(n=3, alpha=[1, 1, 0], beta=[1, 1, 0], gamma=[0, 0, 0], Uij=[[0, 0], [0]])
>>> H2: KHive = KHive(n=3, alpha=[1, 1, 0], beta=[0, 1, 1], gamma=[0, 0, 0], Uij=[[1, 0], [1]])
>>> H: List[KHive] = [H1, H2]
>>> Compose(H=H).run()
KHive(n=3, alpha=[2, 2, 0], beta=[1, 2, 1], gamma=[0, 0, 0], Uij=[[1, 0], [1]])
def validate_is_khive(self) ‑> None

Validate that H is a list of KHive.

Raises

ValueError
If H has an object which is not a Khive, raise error.

Examples

>>> H1: KHive = KHive(n=3, alpha=[1, 1, 0], beta=[1, 1, 0], gamma=[0, 0, 0], Uij=[[0, 0], [0]])
>>> H: List[int] = [H1, 1]
>>> Compose(H=H)
Traceback (most recent call last):
...
ValueError: H must be a list of KHive!
def validate_khive_size(self) ‑> None

Validate that H.n

Raises

ValueError
If H has an KHive with n != H[0].n. raise error.

Examples

>>> H1: KHive = KHive(n=3, alpha=[1, 1, 0], beta=[1, 1, 0], gamma=[0, 0, 0], Uij=[[0, 0], [0]])
>>> H2: KHive = KHive(n=2, alpha=[1, 0], beta=[1, 0], gamma=[0, 0], Uij=[[0]])
>>> H: List[int] = [H1, H2]
>>> Compose(H=H)
Traceback (most recent call last):
...
ValueError: All elements of H must have the same n
class Split (H: khive_crystal.khive.KHive)

This class has methods to split KHive to a pair of KHive and FundamentalKHive. The entry point of this methods is "run". The above function split is a wrapper of run, then use split instead of using this class direct.

Expand source code
class Split:
    """This class has methods to split KHive to a pair of KHive and FundamentalKHive.
    The entry point of this methods is "run". The above function split is a wrapper of run,
    then use split instead of using this class direct.
    """

    def __init__(self, H: KHive) -> None:
        self.H: KHive = H

    def split_alpha(self) -> list[list[int]]:
        """Split H.alpha

        Returns:
            List[List[int]]: [alpha^{(1)}, alpha^{(2)}]

        Examples:
            >>> H: KHive = KHive(
            ...    n=3,
            ...    alpha=[3, 3, 0],
            ...    beta=[2, 3, 1],
            ...    gamma=[0, 0, 0],
            ...    Uij=[[1, 0], [1]]
            ... )
            >>> Split(H=H).split_alpha()
            [[2, 2, 0], [1, 1, 0]]
        """
        alpha: list[int] = self.H.alpha.copy()
        alpha2: list[int] = [1 if alpha_i > 0 else 0 for alpha_i in alpha]
        alpha1: list[int] = [
            alpha_i - alpha2_i for alpha_i, alpha2_i in zip(alpha, alpha2, strict=False)
        ]

        return [alpha1, alpha2]

    def split_full_Uij(self) -> list[list[list[int]]]:
        """Split H.Uij

        Returns:
            List[List[List[int]]]: [U_{ij}^{(1)}, U_{ij}^{(2)}] (i<=j)

        Examples:
            >>> H: KHive = KHive(
            ...    n=3,
            ...    alpha=[3, 3, 0],
            ...    beta=[2, 3, 1],
            ...    gamma=[0, 0, 0],
            ...    Uij=[[1, 0], [1]]
            ... )
            >>> Split(H=H).split_full_Uij()
            [[[1, 1, 0], [1, 1], [0]], [[1, 0, 0], [1, 0], [0]]]

            >>> H: KHive = KHive(
            ...    n=4,
            ...    alpha=[3, 3, 0, 0],
            ...    beta=[2, 3, 1, 0],
            ...    gamma=[0, 0, 0, 0],
            ...    Uij=[[1, 0, 0], [1, 0], [0]]
            ... )
            >>> Split(H=H).split_full_Uij()
            [[[1, 1, 0, 0], [1, 1, 0], [0, 0], [0]], [[1, 0, 0, 0], [1, 0, 0], [0, 0], [0]]]
        """
        full_Uij: list[list[int]] = deepcopy(self.H.full_Uij)

        # a set of \min\{j in [n] \mid U_{ij} > 0\}s for i in I.
        full_Uij_pos_indexes: list[int | None] = [
            [uij > 0 for uij in Ui].index(True) if sum(Ui) > 0 else None
            for Ui in full_Uij[:-1]
        ]

        full_Uij2: list[list[int]] = [
            [1 if j == Ui_pos_index else 0 for j, uij in enumerate(Ui)]
            for Ui, Ui_pos_index in zip(full_Uij, full_Uij_pos_indexes, strict=False)
        ] + [
            [0]
        ]  # U_{nn} is always 0
        full_Uij1: list[list[int]] = [
            [uij - uij2 for uij, uij2 in zip(Ui, Ui2, strict=False)]
            for Ui, Ui2 in zip(full_Uij, full_Uij2, strict=False)
        ]

        return [full_Uij1, full_Uij2]

    def get_splitted_Uij(self) -> list[list[list[int]]]:
        """Get splitted result of H.Uij from splitted H.full_Uij

        Returns:
            List[List[List[int]]]: [U_{ij}^{(1)}, U_{ij}^{(2)}] (i<j)

        Examples:
            >>> H: KHive = KHive(
            ...    n=3,
            ...    alpha=[3, 3, 0],
            ...    beta=[2, 3, 1],
            ...    gamma=[0, 0, 0],
            ...    Uij=[[1, 0], [1]]
            ... )
            >>> Split(H=H).get_splitted_Uij()
            [[[1, 0], [1]], [[0, 0], [0]]]
        """
        splitted_full_Uij: list[list[list[int]]] = self.split_full_Uij()
        return [
            [full_Ui[1:] for full_Ui in full_Uij[:-1]] for full_Uij in splitted_full_Uij
        ]

    def split_beta(self) -> list[list[int]]:
        """Split H.beta

        Returns:
            List[List[int]]: [alpha^{(1)}, alpha^{(2)}]

        Examples:
            >>> H: KHive = KHive(
            ...    n=3,
            ...    alpha=[3, 3, 0],
            ...    beta=[2, 3, 1],
            ...    gamma=[0, 0, 0],
            ...    Uij=[[1, 0], [1]]
            ... )
            >>> Split(H=H).split_beta()
            [[1, 2, 1], [1, 1, 0]]
        """

        splitted_full_Uij: list[list[list[int]]] = self.split_full_Uij()

        align: Callable[[list[list[int]]], list[list[int]]] = lambda full_Uij: [
            [0] * ((self.H.n) - len(ui)) + ui for ui in full_Uij
        ]
        aligned_full_Uij1: list[list[int]] = align(splitted_full_Uij[0])
        aligned_full_Uij2: list[list[int]] = align(splitted_full_Uij[1])

        get_full_Uji: Callable[
            [list[list[int]]], list[list[int]]
        ] = lambda aligned_full_Uij: [
            list(uj)[: j + 1] for j, uj in enumerate(zip(*aligned_full_Uij, strict=False))
        ]
        full_Uji1: list[list[int]] = get_full_Uji(aligned_full_Uij1)
        full_Uji2: list[list[int]] = get_full_Uji(aligned_full_Uij2)

        beta1: list[int] = [sum(Uj) for Uj in full_Uji1]
        beta2: list[int] = [sum(Uj) for Uj in full_Uji2]

        return [beta1, beta2]

    def run(self) -> list[KHive] | KHive:
        """Split KHive to a pair of KHive and FundamentalKHive.

        Returns:
            Union[List[KHive], KHive]: A pair of KHive and FundamentalKHive.
        """
        if self.H.is_fundamental_khive():
            return self.H
        splitted_alpha: list[list[int]] = self.split_alpha()
        splitted_Uij: list[list[list[int]]] = self.get_splitted_Uij()
        splitted_beta: list[list[int]] = self.split_beta()
        gamma: list[int] = [0] * self.H.n
        return [
            KHive(
                n=self.H.n,
                alpha=splitted_alpha[i],
                beta=splitted_beta[i],
                gamma=gamma,
                Uij=splitted_Uij[i],
            )
            for i in range(2)
        ]

Methods

def get_splitted_Uij(self) ‑> list[list[list[int]]]

Get splitted result of H.Uij from splitted H.full_Uij

Returns

List[List[List[int]]]
[U_{ij}^{(1)}, U_{ij}^{(2)}] (i<j)

Examples

>>> H: KHive = KHive(
...    n=3,
...    alpha=[3, 3, 0],
...    beta=[2, 3, 1],
...    gamma=[0, 0, 0],
...    Uij=[[1, 0], [1]]
... )
>>> Split(H=H).get_splitted_Uij()
[[[1, 0], [1]], [[0, 0], [0]]]
def run(self) ‑> khive_crystal.khive.KHive | list[khive_crystal.khive.KHive]

Split KHive to a pair of KHive and FundamentalKHive.

Returns

Union[List[KHive], KHive]
A pair of KHive and FundamentalKHive.
def split_alpha(self) ‑> list[list[int]]

Split H.alpha

Returns

List[List[int]]
[alpha^{(1)}, alpha^{(2)}]

Examples

>>> H: KHive = KHive(
...    n=3,
...    alpha=[3, 3, 0],
...    beta=[2, 3, 1],
...    gamma=[0, 0, 0],
...    Uij=[[1, 0], [1]]
... )
>>> Split(H=H).split_alpha()
[[2, 2, 0], [1, 1, 0]]
def split_beta(self) ‑> list[list[int]]

Split H.beta

Returns

List[List[int]]
[alpha^{(1)}, alpha^{(2)}]

Examples

>>> H: KHive = KHive(
...    n=3,
...    alpha=[3, 3, 0],
...    beta=[2, 3, 1],
...    gamma=[0, 0, 0],
...    Uij=[[1, 0], [1]]
... )
>>> Split(H=H).split_beta()
[[1, 2, 1], [1, 1, 0]]
def split_full_Uij(self) ‑> list[list[list[int]]]

Split H.Uij

Returns

List[List[List[int]]]
[U_{ij}^{(1)}, U_{ij}^{(2)}] (i<=j)

Examples

>>> H: KHive = KHive(
...    n=3,
...    alpha=[3, 3, 0],
...    beta=[2, 3, 1],
...    gamma=[0, 0, 0],
...    Uij=[[1, 0], [1]]
... )
>>> Split(H=H).split_full_Uij()
[[[1, 1, 0], [1, 1], [0]], [[1, 0, 0], [1, 0], [0]]]
>>> H: KHive = KHive(
...    n=4,
...    alpha=[3, 3, 0, 0],
...    beta=[2, 3, 1, 0],
...    gamma=[0, 0, 0, 0],
...    Uij=[[1, 0, 0], [1, 0], [0]]
... )
>>> Split(H=H).split_full_Uij()
[[[1, 1, 0, 0], [1, 1, 0], [0, 0], [0]], [[1, 0, 0, 0], [1, 0, 0], [0, 0], [0]]]