Overview
NumPy's generalized ufunc (gufunc) system for sub-array operations with defined core dimensions.
NEP 5: Generalized Universal Functions
Status: Final | Full Text
Concept
Regular ufuncs operate element-by-element (scalars). Gufuncs operate on sub-arrays with defined core dimensions.
Signature Syntax
Examples
| Function |
Signature |
Description |
add |
(),()->() |
Scalar + scalar |
inner |
(n),(n)->() |
Inner product |
matmul |
(m,n),(n,p)->(m,p) |
Matrix multiplication |
cross |
(3),(3)->(3) |
Cross product |
Broadcasting Rules
- Core dimensions mapped to last axes
- Remaining dimensions are "loop dimensions"
- Loop dimensions broadcast normally
# Signature: (i),(i)->()
a.shape = (3, 5, N) # Core i=N, loop (3, 5)
b.shape = (5, N) # Core i=N, loop (5,) → broadcast to (3, 5)
output.shape = (3, 5)
NEP 20: Signature Enhancements
Status: Final | Full Text
Frozen Dimensions
Use integer instead of variable to require specific size:
(3),(3)->(3) # Cross product: must be size 3
()->(2) # Output always size 2
Flexible Dimensions
Suffix with ? for optional dimensions:
Covers four matmul variants:
| Case |
Input Shapes |
Output |
| matrix × matrix |
(m,n),(n,p) |
(m,p) |
| vector × matrix |
(n),(n,p) |
(p) |
| matrix × vector |
(m,n),(n) |
(m) |
| vector × vector |
(n),(n) |
() |
Behavior
When a flexible dimension is missing:
- Internal computation treats it as size 1
- Output array has that dimension removed (squeezed)
Suggested Implementation for NumSharp
Frozen Dimension Validation
public static void ValidateFrozenDimension(NDArray arr, int axis, int required) {
if (arr.Shape[axis] != required) {
throw new ValueError(
$"Axis {axis} must have size {required}, got {arr.Shape[axis]}");
}
}
// Usage
public static NDArray Cross(NDArray a, NDArray b) {
ValidateFrozenDimension(a, -1, 3);
ValidateFrozenDimension(b, -1, 3);
// ...
}
Flexible Dimension Handling
public static NDArray Matmul(NDArray a, NDArray b) {
bool aIsVector = a.NDim == 1;
bool bIsVector = b.NDim == 1;
// Expand vectors to 2D
var a2d = aIsVector ? a.reshape(1, -1) : a;
var b2d = bIsVector ? b.reshape(-1, 1) : b;
var result = MatrixProduct(a2d, b2d);
// Squeeze based on flexible dims
if (aIsVector && bIsVector) return result.squeeze();
if (aIsVector) return result.squeeze(axis: 0);
if (bIsVector) return result.squeeze(axis: -1);
return result;
}
Signature Parsing (Future)
public class GufuncSignature {
public CoreDimension[] Inputs { get; }
public CoreDimension[] Outputs { get; }
public static GufuncSignature Parse(string sig) {
// Parse "(m?,n),(n,p?)->(m?,p?)"
}
}
public class CoreDimension {
public string Name { get; }
public int? FrozenSize { get; } // null if variable, int if frozen
public bool IsFlexible { get; }
}
Documentation
See docs/neps/NEP05.md, docs/neps/NEP20.md
Overview
NumPy's generalized ufunc (gufunc) system for sub-array operations with defined core dimensions.
NEP 5: Generalized Universal Functions
Status: Final | Full Text
Concept
Regular ufuncs operate element-by-element (scalars). Gufuncs operate on sub-arrays with defined core dimensions.
Signature Syntax
Examples
add(),()->()inner(n),(n)->()matmul(m,n),(n,p)->(m,p)cross(3),(3)->(3)Broadcasting Rules
NEP 20: Signature Enhancements
Status: Final | Full Text
Frozen Dimensions
Use integer instead of variable to require specific size:
Flexible Dimensions
Suffix with
?for optional dimensions:Covers four matmul variants:
(m,n),(n,p)(m,p)(n),(n,p)(p)(m,n),(n)(m)(n),(n)()Behavior
When a flexible dimension is missing:
Suggested Implementation for NumSharp
Frozen Dimension Validation
Flexible Dimension Handling
Signature Parsing (Future)
Documentation
See
docs/neps/NEP05.md,docs/neps/NEP20.md