oplus¶
Direct sum (block diagonal) of tensors.
oplus
¶
oplus(A: Tensor, B: Tensor, axes: Optional[Union[int, str, Sequence[int], Sequence[str]]] = None) -> Tensor
Direct sum of two tensors with selective axis merging.
Combines two tensors by merging their sector structures along specified axes and arranging blocks in a block-diagonal fashion. Axes not specified must have matching dimensions for any charge sectors shared by both tensors; sectors exclusive to one tensor are included in the output via union.
For non-Abelian tensors, blocks are padded with zeros and combined using
block_add, which merges intertwiner weights (collinear weights combine
directly; non-collinear weights are concatenated). block_compress is
then applied to remove any resulting linear dependence among components.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
A
|
Tensor
|
First tensor |
required |
B
|
Tensor
|
Second tensor |
required |
axes
|
Optional[Union[int, str, Sequence[int], Sequence[str]]]
|
Axes to merge. Can be:
|
None
|
Returns:
| Type | Description |
|---|---|
Tensor
|
Direct sum with merged indices on specified axes |
Raises:
| Type | Description |
|---|---|
ValueError
|
If tensors have incompatible structure or if shared sectors on non-merged axes have mismatched dimensions |
Examples:
>>> from nicole import Tensor, U1Group, Direction, Index, Sector, oplus
>>> import torch
>>>
>>> group = U1Group()
>>>
>>> # Example 1: Default - merge all axes
>>> idx_A0 = Index(Direction.OUT, group, sectors=(Sector(0, 2), Sector(1, 3)))
>>> idx_A1 = Index(Direction.IN, group, sectors=(Sector(0, 3), Sector(1, 2)))
>>> A = Tensor.random([idx_A0, idx_A1], seed=1, itags=['i', 'j'])
>>>
>>> idx_B0 = Index(Direction.OUT, group, sectors=(Sector(0, 1), Sector(2, 2)))
>>> idx_B1 = Index(Direction.IN, group, sectors=(Sector(0, 2), Sector(2, 1)))
>>> B = Tensor.random([idx_B0, idx_B1], seed=2, itags=['i', 'j'])
>>>
>>> C = oplus(A, B) # Merges both axes
>>> # C.indices[0] has sectors: [(0, 3), (1, 3), (2, 2)]
>>> # C.indices[1] has sectors: [(0, 5), (1, 2), (2, 1)]
>>>
>>> # Example 2: Selective - merge only first axis
>>> idx_A1_match = Index(Direction.IN, group, sectors=(Sector(0, 5),))
>>> idx_B1_match = Index(Direction.IN, group, sectors=(Sector(0, 5),)) # Must match!
>>> A2 = Tensor.random([idx_A0, idx_A1_match], seed=3, itags=['i', 'j'])
>>> B2 = Tensor.random([idx_B0, idx_B1_match], seed=4, itags=['i', 'j'])
>>>
>>> C2 = oplus(A2, B2, axes=0) # or axes='i', or axes=[0], or axes=['i']
>>> # C2.indices[0] has sectors: [(0, 3), (1, 3), (2, 2)] ← merged
>>> # C2.indices[1] has sectors: [(0, 5)] ← unchanged (matched exactly)
Notes
- Blocks are arranged in a block-diagonal fashion along merged axes
- For merged axes, dimensions add for sectors with the same charge
- Non-merged axes: shared charge sectors must have identical dimensions; sectors exclusive to one tensor are included in the output index via union
- Charge conservation is maintained in the output tensor
Description¶
Creates a direct sum (block diagonal concatenation) of two tensors. The tensors must have:
- The same number of indices
- Matching directions for every corresponding index
- The same symmetry group on every corresponding index
The axes parameter controls which indices are merged (block-diagonal stacking) and which are non-merged (shared). For non-merged axes, only charge sectors that appear in both tensors must agree in dimension. Sectors exclusive to one tensor are included in the output via the union of sectors and contribute their blocks independently.
When charges coincide on merged axes, blocks are placed on the block diagonal (not summed).
See Also¶
- Arithmetic Operations: Basic operations
- Tensor: Main tensor class
- Examples: Arithmetic
Notes¶
- The output index for each merged axis is the union of sectors from both tensors, with dimensions added (
dim_A + dim_Bper shared charge, ordim_A/dim_Bfor exclusive charges). - The output index for each non-merged axis is also the union of sectors, but dimensions of shared charges must be equal (they are not summed).
- For non-Abelian groups, linearly dependent multiplicity components are automatically compressed away after the merge.