Skip to content

Bridge

Storage container for the SU(2) Clebsch–Gordan intertwiner data attached to each block of a non-Abelian tensor.

Bridge dataclass

Bridge(cgspec: CGSpec, weights: Tensor)

Storage container for SU(2) CG tensor data using yuzuha canonical bases.

The Bridge class stores a CGSpec (Clebsch-Gordan specification) that identifies the canonical bases for the outer multiplicity (OM) space, along with a weight matrix that represents coefficients expanded according to these canonical bases.

Attributes:

Name Type Description
cgspec CGSpec

Identifies the canonical bases for the OM space. Encapsulates:

  • External edge list (spins and directions)
  • All valid internal-spin tuples (α) for left-associative fusion trees
  • The full OM space dimension

weights Tensor

Coefficient matrix with shape (num_components, om_dimension). Each row represents coefficients expanded in the canonical basis. The canonical bases are normalized with respect to the OM space. num_components can be any positive integer (typically starts at 1). om_dimension is obtained from cgspec.om_dimension().

device device

Device where the weight matrix is stored.

om_dimension int

Dimension of the outer multiplicity space (number of fusion tree configurations).

num_components int

Number of component rows in the weight matrix (weights.shape[0]).

num_external int

Number of external edges in the CGSpec.

Examples:

Create a Bridge for a simple three-edge SU(2) tensor:

>>> import yuzuha
>>> import torch
>>> from nicole.symmetry.delegate import Bridge
>>> 
>>> # Define edges: two incoming spin-1/2, one outgoing spin-1
>>> j_half = yuzuha.Spin(1)  # 2j=1 for j=1/2
>>> j_one = yuzuha.Spin(2)   # 2j=2 for j=1
>>> edges = [
...     yuzuha.Edge.incoming(j_half),
...     yuzuha.Edge.incoming(j_half),
...     yuzuha.Edge.outgoing(j_one),
... ]
>>> 
>>> # Create CGSpec
>>> cgspec = yuzuha.CGSpec.from_edges(edges)
>>> 
>>> # Initialize weight matrix (1 component)
>>> om_dim = cgspec.om_dimension()
>>> weights = torch.zeros(1, om_dim, dtype=torch.float64)
>>> 
>>> # Create Bridge
>>> bridge = Bridge(cgspec, weights)
>>> bridge.om_dimension
1
>>> bridge.num_components
1
>>> bridge.num_external
3
Notes
  • The OM space and canonical bases are fully managed by yuzuha.
  • Nicole's Direction.IN (value +1) maps to yuzuha.Edge.incoming (+1).
  • Nicole's Direction.OUT (value -1) maps to yuzuha.Edge.outgoing (-1).
  • Both Nicole and yuzuha use the 2j integer convention for SU(2) spins.

Attributes

cgspec instance-attribute

cgspec: CGSpec

weights instance-attribute

weights: Tensor

om_dimension property

om_dimension: int

Return the dimension of the outer multiplicity space.

num_components property

num_components: int

Return the number of component rows in the weight matrix.

num_external property

num_external: int

Return the number of external edges in the CGSpec.

device property

device: device

Return the device where the weight matrix is stored.

Functions

from_block staticmethod

from_block(
    group: SymmetryGroup,
    key: Tuple[Charge, ...],
    directions: Sequence[Direction],
    weights: Optional[Tensor] = None,
    dtype: dtype = torch.float64,
    device: Optional[device] = None,
) -> Bridge

Construct a Bridge from a BlockKey and corresponding directions.

This factory method creates a Bridge by extracting SU(2) charges from a BlockKey and creating the corresponding yuzuha CGSpec. Useful for converting Nicole tensor blocks into the yuzuha canonical basis representation.

Parameters:

Name Type Description Default
group SymmetryGroup

The symmetry group. Must be either SU2Group or ProductGroup with SU2Group as the last component.

required
key Tuple[Charge, ...]

BlockKey containing one charge per edge. For SU2Group, charges are integers (2j values). For ProductGroup with SU(2), charges are tuples where the last element is the 2j value.

required
directions Sequence[Direction]

One direction per edge in the BlockKey. Length must match len(key).

required
weights Tensor

Pre-initialized weight matrix. If provided, must have shape (num_components, om_dimension). If None, weights are initialized with shape (1, om_dimension) with the first element set to 1 and all other elements set to 0.

None
dtype dtype

Data type for the weight matrix. Only used if weights is None. Defaults to torch.float64.

float64
device device or str

Device for the weight matrix. Only used if weights is None. If None, defaults to torch.get_default_device().

None

Returns:

Type Description
Bridge

New Bridge instance with CGSpec constructed from the block key and weights either provided or initialized with first element as 1.

Raises:

Type Description
TypeError

If group is not SU2Group or ProductGroup with SU2Group, or if provided weights is not a torch.Tensor (via Bridge.post_init).

ValueError

If the number of directions doesn't match the number of charges, if provided weights has incorrect shape (via Bridge.post_init), or if yuzuha rejects the edge configuration.

Examples:

>>> from nicole import SU2Group, Direction
>>> from nicole.symmetry.delegate import Bridge
>>> 
>>> # Pure SU(2) group with default weights
>>> group = SU2Group()
>>> key = (1, 1, 2)  # Three edges: spin-1/2, spin-1/2, spin-1
>>> directions = [Direction.IN, Direction.IN, Direction.OUT]
>>> bridge = Bridge.from_block(group, key, directions)
>>> bridge.weights[0, 0]  # First element is 1
tensor(1.)
>>> 
>>> # ProductGroup with SU(2) and custom weights
>>> import torch
>>> from nicole import ProductGroup, U1Group
>>> group = ProductGroup([U1Group(), SU2Group()])
>>> key = ((0, 1), (-1, 1), (1, 2))  # Three edges with U1 and SU(2) charges
>>> custom_weights = torch.randn(3, 1)
>>> bridge = Bridge.from_block(group, key, directions, weights=custom_weights)
>>> bridge.num_components
3

to

to(device: Union[str, device], dtype: Optional[dtype] = None) -> Bridge

Move Bridge to a new device and optionally convert dtype.

Parameters:

Name Type Description Default
device str or device

Target device ('cpu', 'cuda', 'mps', etc.)

required
dtype dtype

Target dtype. If None, preserves current dtype.

None

Returns:

Type Description
Bridge

New Bridge instance with weights on the target device/dtype. If already on target device with target dtype, returns self.

Examples:

>>> bridge = Bridge(cgspec, weights)
>>> bridge_gpu = bridge.to('cuda')
>>> bridge_gpu.device.type
'cuda'

clone

clone() -> Bridge

Create a deep copy of this Bridge with cloned weights.

Returns:

Type Description
Bridge

New Bridge instance with cloned weight matrix.

Examples:

>>> bridge = Bridge(cgspec, weights)
>>> bridge_copy = bridge.clone()
>>> bridge_copy.weights is bridge.weights
False

conj

conj() -> Bridge

Return a new Bridge with flipped edge directions (conjugation).

Creates a new Bridge instance where all edge directions in the CGSpec are flipped (incoming <-> outgoing). The cumulated Frobenius-Schur phase returned by yuzuha.compute_conjugate is multiplied into the weight matrix, so the resulting weights may differ from the original by a global factor of ±1.

Returns:

Type Description
Bridge

New Bridge instance with flipped edge directions and weights scaled by the cumulated FS phase (±1).

Examples:

>>> bridge = Bridge(cgspec, weights)
>>> bridge_conj = bridge.conj()
>>> # All edge directions are flipped
>>> for orig, conj in zip(bridge.cgspec.edges, bridge_conj.cgspec.edges):
...     assert orig.dir == conj.dir.flip()

conj_phase

conj_phase() -> float

Return the conjugation phase for this Bridge.

Computes and returns the cumulated Frobenius-Schur phase (±1) between the conjugate of a CG basis and the canonical basis. This phase arises from the coupling tree structure and is essential for correct scalar contractions of SU(2) tensors.

Returns:

Type Description
float

The cumulated FS phase, either +1.0 or -1.0.

Examples:

>>> bridge = Bridge(cgspec, weights)
>>> phase = bridge.conj_phase()
>>> assert phase in [1.0, -1.0]

Description

Every block of an SU(2) or non-Abelian ProductGroup tensor carries a Bridge alongside its reduced-tensor data. The Bridge encodes the outer multiplicity (OM) structure of the block's Clebsch–Gordan decomposition using the yuzuha canonical basis:

  • cgspec (yuzuha.CGSpec) — identifies the canonical OM basis. It records the external edges (spins and their in/out directions) and all valid left-associative fusion trees (internal-spin tuples α). The OM dimension is the number of such trees.
  • weights (torch.Tensor, shape (num_components, om_dimension)) — coefficient matrix expanded in the canonical basis. Each row corresponds to one component of the outer multiplicity; the number of rows grows as blocks mix under operations such as contraction.

Nicole manages Bridge objects internally. Users typically inspect them when examining tensor blocks or implementing custom SU(2)-aware routines.

Role in the R-W-C Decomposition

Each SU(2) tensor block is stored as a three-factor product R · W · C:

  • R (reduced tensor, Tensor.data[key]): the dense array of reduced tensor elements, indexed over the multiplet space of each index (Sector.dim).
  • W (weight matrix, Bridge.weights, shape (dim(r), dim(α))): expands the component in the canonical OM basis. Each row is a vector in the OM space.
  • C (CG basis): the Clebsch–Gordan basis tensor with one axis per external edge and one OM axis (α), in a canonical orthonormal basis, computed on demand by the Yuzuha engine.

The connecting index between R and W is r (component); between W and C is α (outer multiplicity). See the Yuzuha Protocol for the full derivation and diagrams.

Accessing Bridge Data

from nicole import Tensor, SU2Group, Index, Sector, Direction

group = SU2Group()
idx = Index(Direction.OUT, group, sectors=(Sector(0, 1), Sector(1, 2), Sector(2, 1)))
T = Tensor.random([idx, idx.flip(), idx], itags=["i", "j", "k"], seed=42)

# Iterate over blocks
for key, block in T.data.items():
    bridge = T.intw[key]             # Bridge for this block
    print(key, bridge.om_dimension)  # OM dimension for this charge sector
    print(bridge.weights.shape)      # (num_components, om_dimension)

Constructing a Bridge

The from_block factory is the standard way to create a Bridge from a charge key and direction list:

from nicole import SU2Group, Direction
from nicole.symmetry.delegate import Bridge

group = SU2Group()

# Three-edge block: spin-1/2 ⊗ spin-1/2 → spin-1
key = (1, 1, 2)                            # charges in 2j convention
dirs = [Direction.IN, Direction.IN, Direction.OUT]
bridge = Bridge.from_block(group, key, dirs)

print(bridge.om_dimension)   # 1 — only one valid fusion tree for j=1/2 ⊗ j=1/2 → j=1
print(bridge.num_components) # 1 — single component by default
print(bridge.weights)        # tensor([[1.]])

For ProductGroup tensors the key contains tuples and the SU(2) charge is always the last element:

from nicole import ProductGroup, U1Group, SU2Group, Direction
from nicole.symmetry.delegate import Bridge
import torch

group = ProductGroup([U1Group(), SU2Group()])
key = ((0, 1), (-1, 1), (-1, 2))          # three edges: (U1, SU2) charges
dirs = [Direction.IN, Direction.IN, Direction.OUT]

bridge = Bridge.from_block(group, key, dirs)
print(bridge.num_external)   # 3

Device and dtype

A Bridge's weights tensor lives on the same device as the corresponding data block. Use .to() to move it:

bridge_gpu = bridge.to('cuda')
bridge_gpu = bridge.to('cuda', dtype=torch.float32)  # also cast dtype

OM Dimension

om_dimension is the number of linearly independent ways to couple the given spins to the target spin via a left-associative binary fusion tree. 2nd- and 3rd-order blocks always have OM = 1. OM > 1 first appears at 4th order, where multiple intermediate spin paths may be valid, and generally grows with both the tensor order and the spin magnitudes.

from nicole import SU2Group, Direction
from nicole.symmetry.delegate import Bridge

group = SU2Group()

# Four edges: j=1/2 ⊗ j=1/2 ⊗ j=1/2 → j=1/2
# Two intermediate couplings: intermediate spin can be 0 or 1
key = (1, 1, 1, 1)
dirs = [Direction.IN, Direction.IN, Direction.IN, Direction.OUT]
bridge = Bridge.from_block(group, key, dirs)
print(bridge.om_dimension)   # 2

See Also

Notes

  • Bridge is a @dataclass; all fields are set at construction time and validated by __post_init__.
  • cgspec is shared between Bridge instances produced by non-mutating operations such as .clone(). Only weights is copied.