Skip to content

ProductGroup

Combine multiple symmetry groups into a single product group, enforcing all constituent conservation laws simultaneously. Charges are tuples of component charges.

ProductGroup dataclass

ProductGroup(components: Sequence[SymmetryGroup])

Bases: SymmetryGroup

Product of multiple independent symmetry groups.

Represents the direct product of multiple symmetry groups, enabling tensors with multiple independent conserved quantities. Charges are tuples where each component corresponds to one component group.

Supports Abelian groups and at most one UnitaryGroup (non-Abelian) component. If a UnitaryGroup is present, it must be the last component.

Attributes:

Name Type Description
components Tuple[SymmetryGroup, ...]

Tuple of component SymmetryGroup instances. Can contain AbelianGroup instances and at most one UnitaryGroup instance (placed at the end).

Examples:

>>> # U(1) particle number × U(1) spin (all Abelian)
>>> group = ProductGroup([U1Group(), U1Group()])
>>> group.neutral
(0, 0)
>>> group.fuse_unique((2, 1), (1, -1))
(3, 0)
>>> # U(1) × Z(2) (all Abelian)
>>> group = ProductGroup([U1Group(), Z2Group()])
>>> group.neutral
(0, 0)
>>> group.fuse_unique((3, 1), (-1, 0))
(2, 1)
>>> # U(1) × SU(2) (mixed: has UnitaryGroup)
>>> group = ProductGroup([U1Group(), SU2Group()])
>>> group.fuse_channels((1, 1), (0, 1))
((1, 0), (1, 2))  # Two fusion channels from SU(2) (spin-1/2 ⊗ spin-1/2)

Initialize ProductGroup with component symmetry groups.

Parameters:

Name Type Description Default
components Sequence[SymmetryGroup]

Sequence of SymmetryGroup instances. Must contain at least one group. Can include AbelianGroup instances and at most one UnitaryGroup instance. Nested ProductGroups are not allowed.

required

Raises:

Type Description
ValueError

If components is empty, contains multiple UnitaryGroups, or has UnitaryGroup not at the last position.

TypeError

If components contains non-SymmetryGroup instances or nested ProductGroups.

Attributes

name property

name: str

Return the product group name, e.g., 'U1×Z2'.

neutral property

neutral: Tuple[Any, ...]

Return the neutral element as a tuple of component neutrals.

is_abelian property

is_abelian: bool

Return True if all components are Abelian, False if any is non-Abelian.

A ProductGroup is Abelian only when all its components are Abelian groups. If it contains a UnitaryGroup component, it is non-Abelian.

Functions

irrep_dim

irrep_dim(q: Tuple[Any, ...]) -> int

Return dimension of the irreducible representation.

For ProductGroup, the irrep dimension is the product of constituent irrep dimensions.

Parameters:

Name Type Description Default
q Tuple[Any, ...]

Charge tuple.

required

Returns:

Type Description
int

Product of component irrep dimensions.

dual

dual(q: Tuple[Any, ...]) -> Tuple[Any, ...]

Return the dual (contragredient) of a charge tuple.

Parameters:

Name Type Description Default
q Tuple[Any, ...]

Charge tuple.

required

Returns:

Type Description
Tuple

Tuple of dual charges.

fuse_unique

fuse_unique(*qs: Tuple[Any, ...]) -> Tuple[Any, ...]

Fuse multiple charge tuples component-wise (Abelian-only).

This method is only available when all components are Abelian groups. For ProductGroups containing a UnitaryGroup, use fuse_channels instead.

Parameters:

Name Type Description Default
*qs Tuple[Any, ...]

Variable number of charge tuples to fuse.

()

Returns:

Type Description
Tuple

The unique fused charge tuple.

Raises:

Type Description
TypeError

If this ProductGroup contains a UnitaryGroup component.

Examples:

>>> group = ProductGroup([U1Group(), Z2Group()])
>>> group.fuse_unique((2, 1), (1, 0), (-1, 1))
(2, 0)

fuse_channels

fuse_channels(*qs: Tuple[Any, ...]) -> Tuple[Tuple[Any, ...], ...]

Fuse multiple charge tuples, returning all achievable fusion channels.

When all components are Abelian, returns a single-element tuple. When a UnitaryGroup component is present (must be at the end), returns multiple channels corresponding to the UnitaryGroup's fusion outcomes.

Parameters:

Name Type Description Default
*qs Tuple[Any, ...]

One or more charge tuples to fuse.

()

Returns:

Type Description
Tuple[Tuple[Any, ...], ...]

Tuple of all achievable fusion channel tuples, independent of the fusion tree structure.

Examples:

>>> # All Abelian: single channel
>>> group = ProductGroup([U1Group(), Z2Group()])
>>> group.fuse_channels((2, 1), (1, 0))
((3, 1),)
>>> # With UnitaryGroup: multiple channels (pairwise)
>>> group = ProductGroup([U1Group(), SU2Group()])
>>> group.fuse_channels((1, 1), (0, 1))
((1, 0), (1, 2))
>>> # With UnitaryGroup: multiple charges
>>> group.fuse_channels((1, 1), (0, 1), (2, 1))
((3, 1), (3, 3))

equal

equal(a: Tuple[Any, ...], b: Tuple[Any, ...]) -> bool

Check if two charge tuples are equal component-wise.

Parameters:

Name Type Description Default
a Tuple[Any, ...]

Charge tuples to compare.

required
b Tuple[Any, ...]

Charge tuples to compare.

required

Returns:

Type Description
bool

True if all components are equal.

validate_charge

validate_charge(q: Any) -> None

Validate that a charge is a tuple of correct length with valid components.

Parameters:

Name Type Description Default
q Any

Charge to validate.

required

Raises:

Type Description
TypeError

If charge is not a tuple.

ValueError

If charge has incorrect length or invalid component charges.

Description

Combines multiple independent symmetry groups. Charges are tuples with one component per group.

Abelian and Non-Abelian Components

ProductGroup supports both Abelian and non-Abelian component groups. When an SU2Group is included, it must be the last component:

from nicole import ProductGroup, U1Group, Z2Group, SU2Group

# Abelian only
group = ProductGroup([U1Group(), Z2Group()])
print(group.is_abelian)   # True

# With SU(2) as last component — non-Abelian
group = ProductGroup([U1Group(), SU2Group()])
print(group.is_abelian)  # False
print(group.name)        # "U1×SU2"
print(group.neutral)     # (0, 0)

Charge Operations

For Abelian-only products, all operations are component-wise:

  • Fusion: (q1_a, q1_b) ⊕ (q2_a, q2_b) = (q1_a ⊕ q2_a, q1_b ⊕ q2_b)
  • Dual: Component-wise dual
  • Identity: Tuple of component identities

For products containing SU2Group, use fuse_channels instead of fuse_unique — SU(2) fusion is multi-channel:

# Abelian: fuse_unique gives a single result
group = ProductGroup([U1Group(), Z2Group()])
group.fuse_unique((2, 1), (1, 1))   # (3, 0)  — U1: 2+1=3, Z2: 1⊕1=0

# Non-Abelian: fuse_channels gives multiple result channels
group = ProductGroup([U1Group(), SU2Group()])
# (1, 1) ⊗ (1, 1): charge 1+1=2 for U1, 1⊗1=(0,2) for SU2
group.fuse_channels((1, 1), (1, 1))   # ((2, 0), (2, 2))

irrep_dim

irrep_dim is the product of the component irrep_dim values — always 1 for Abelian factors, 2j+1 for SU(2):

# Abelian: always 1
group = ProductGroup([U1Group(), Z2Group()])
group.irrep_dim((3, 1))   # 1 × 1 = 1

# Non-Abelian: SU(2) factor contributes 2j+1
group = ProductGroup([U1Group(), SU2Group()])
group.irrep_dim((2, 1))   # 1 × 2 = 2  (U1 contributes 1, SU2 spin-1/2 contributes 2)
group.irrep_dim((0, 2))   # 1 × 3 = 3  (triplet)

Physical Applications

  • U(1) × U(1): Particle number and spin magnetization (two separate U(1) charges)
  • U(1) × Z(2): Particle number and fermion parity
  • U(1)ᴺ: Multiple particle species
  • U(1) × SU(2): Particle number + full spin-rotation symmetry (Hubbard/band models)
  • Z(2) × SU(2): Parity + full spin-rotation symmetry

See Also

Notes

  • SU2Group, if present, must be the last component of ProductGroup.
  • When SU2Group is a component, is_abelian returns False and tensors carry intertwiner (Bridge) data.
  • Charges are tuples matching the number of component groups.