Source code for spgrep.symmetry.subgroup

"""Isotropy subgroup of space group."""

from __future__ import annotations

from queue import Queue

from spgrep.rep.group import get_identity_index, get_inverse_index
from spgrep.utils import (
    NDArrayInt,
)


[docs] def enumerate_point_subgroup( table: NDArrayInt, preserve_sublattice: list[bool], return_conjugacy_class: bool = True ) -> list[list[int]]: """Enumerate conjugacy subgroups of point group. Parameters ---------- table: array[int], (order, order) Multiplication table of group preserve_sublattice: list[bool] Specify ``preserve_sublattice[i] = True`` if the ``i``-th operation preserves translational subgroup of isotropy subgroup return_conjugacy_class: bool, default=True If true, return representatives of conjugacy classes. Returns ------- subgroups: list[list[int]] """ order = len(table) identity = get_identity_index(table) # Represent choice of elements by bit array st = {1 << identity} for i in range(order): if (not preserve_sublattice[i]) or (i == identity): continue if (1 << i) in st: # Already visited continue next_st = set() for bits in st: elements = _decode_bits(bits, order) assert _is_subgroup(elements, table) generated = _traverse(elements + [i], identity, table) next_st.add(sum(1 << idx for idx in set(generated))) st = st.union(next_st) if not return_conjugacy_class: subgroups = [] for bits in sorted(st): subgroups.append(_decode_bits(bits, order)) return subgroups # Group by conjugacy classes found = set() ret = [] for bits in sorted(st): if bits in found: continue elements = _decode_bits(bits, order) ret.append(elements) for i in range(order): if not preserve_sublattice[i]: continue inv = get_inverse_index(table, i) conj = [int(table[inv, table[idx, i]]) for idx in elements] found.add(sum(1 << idx for idx in set(conj))) assert found == st return ret
def enumerate_point_subgroup_naive(table, preserve_sublattice: list[bool]): """Enumerate conjugacy subgroups of point group in brute force.""" order = len(table) ret = [] for bits in range(1, 1 << order): elements = _decode_bits(bits, order) if not all([preserve_sublattice[idx] for idx in elements]): continue if _is_subgroup(elements, table): ret.append(bits) return ret def _decode_bits(bits: int, order: int) -> list[int]: elements = [idx for idx in range(order) if (bits >> idx) & 1 == 1] return elements def _is_subgroup(elements: list[int], table: NDArrayInt) -> bool: subtable = table[elements][:, elements] for i in range(len(subtable)): if (set(subtable[i]) != set(elements)) or (set(subtable[:, i]) != set(elements)): return False return True def _traverse( generators: list[int], identity: int, table: NDArrayInt, ) -> list[int]: """Traverse group elements from generators.""" visited = [False for _ in range(len(table))] que = Queue() # type: ignore que.put(identity) while not que.empty(): g = que.get() if visited[g]: continue visited[g] = True for h in generators: gh = int(table[g, h]) # cast np.int64 to int if not visited[gh]: que.put(gh) return sorted([i for i, v in enumerate(visited) if v])