Usage in Pipelines¶
BladeMask serves as the foundational type used by MVTensor, MVMatrix,
MVProductMatrix, and the solver functions. This page explains how each
system uses blade masks and how mask compatibility is enforced.
In MVTensor¶
Each axis of an MVTensor carries a BladeMask (or None for batch axes).
The mask determines:
- How many elements are on that axis (len(mask)).
- Which blade each position along the axis represents.
- Compatibility checks when two tensors are aligned (contraction, broadcast).
from pytanga.tensor import MVTensor
mask = BladeMask.full(alg)
t = MVTensor.zeros([mask, mask, mask]) # each axis = 8 elements
# t.masks = (mask, mask, mask)
When two MVTensor axes align (e.g. in tensor contraction), both must carry
the same mask or both None. A mask on one axis with None on the other
is rejected — the semantics are ambiguous.
In MVMatrix¶
MVMatrix wraps a 2‑D numpy array with a single row_mask:
from pytanga.matrix import MVMatrix
mat = MVMatrix(data=np.zeros((8, 3)), row_mask=full)
# 8 rows (one per blade), 3 columns (3 multivectors)
The row_mask maps each row index to a blade ID. When converting between
MV and MVMatrix, the mask defines the ordering:
- to_matrix(mv, mask) fills rows in mask.ids order.
- from_matrix(mat) reconstructs an MV using mat.row_mask.
In MVProductMatrix¶
MVProductMatrix carries three masks, one per tensor axis:
| Mask | Axis | Meaning |
|---|---|---|
a_mask |
dim 0 | A‑subspace (list of MVs, or blades of one MV) |
c_mask |
dim 1 | Output subspace — determines rows of each matrix |
b_mask |
dim 2 | Unknown X subspace — determines columns of each matrix |
from pytanga.matrix.product import product_matrix
M = product_matrix(A, a_mask=a_mask, b_mask=b_mask, c_mask=c_mask)
# M.a_mask, M.b_mask, M.c_mask
The three masks together define the dimensions of the linear system:
(|a_mask|, |c_mask|, |b_mask|). When |c_mask| == |b_mask|, the system
is square.
In the solver pipeline¶
The solver pipeline uses blade masks to determine the subspace of the unknown. See Blade Mask Pipeline for the full description. In summary:
a_mask = BladeMask(A) # blades of known operand
c_mask = BladeMask(Y) # blades of result
b_mask = inverse_blade_mask(a_mask, c_mask) # blades of unknown
Named bases on geometry masks¶
Geometry.mask_for(typ) (and the module-level mask_for) return a BladeMask
whose basis_names / basis_vectors describe the canonical directions of that
type in that algebra. The algebra display basis (e.g. einf/eo for N3,
e0/e0i for PGA3) is attached automatically, and types with a reduced
physical-DOF basis (e.g. TwistBivector's 6 directions over 9 raw blades)
declare it inline in the per-algebra mask_for_<type> function. The mask ids
themselves are hard-coded per type (never derived from a sample instance). See
MVTensor.get_array for consuming these bases.
Compatibility enforcement¶
Every operation that aligns two axes checks that both masks:
1. Belong to the same algebra (or at least one is None for batch axes).
2. Have the same blade IDs (same set, same order).
3. Have the same length — the axis sizes must match.
A mask of None on both axes is compatible (both are batch axes). A mask
of None on one axis with a concrete BladeMask on the other is not
compatible — the framework cannot know how to align a batch axis with a
blade‑specific axis.
These checks prevent silent bugs where blades from different algebras or subspaces are accidentally aligned.