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 match exactly (same sectors, same dimensions).
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 non-merged axes don't match exactly |
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 must have identical sectors and dimensions
- Charge conservation is maintained in the output tensor
Description¶
Creates a direct sum (block diagonal concatenation) of multiple tensors. All tensors must have:
- Same number of indices
- Matching directions for corresponding indices
- Same symmetry groups
- Matching tags
When charges collide, blocks are placed on the block diagonal.
See Also¶
- Arithmetic Operations: Basic operations
- Tensor: Main tensor class
- Examples: Arithmetic
Notes¶
Resulting tensor has combined sectors from all input tensors. For colliding charges, blocks are arranged diagonally (not summed). For non-Abelian groups, linearly dependent components are automatically compressed away after the merge.