Tensor contraction sums over selected indices while keeping the others. Follow the asset axes through a portfolio covariance calculation in NumPy.
Tensor contraction
Tensor contraction sums over paired indices of an indexed array or a product of arrays. The summed indices disappear and the remaining indices label the output axes. In portfolio risk, this turns asset-level covariances into a covariance for each pair of portfolios.
Ordinary matrix multiplication already does this. In (AB)ij=∑kAikBkj, the shared index k is summed and the row and column indices i,j stay. The same idea applies when an array has more than two axes. MIT 6.390 notes on Einstein summation
Sum over assets, keep the portfolios
Let W have one row per portfolio and one column per asset. With fixed weights, each portfolio return is a weighted sum of asset returns. Expanding the covariance of those two sums using the asset covariance matrix Σ gives C=WΣW⊤ with entries
Cpq=a=1∑Nb=1∑NWpaΣabWqb.
For each portfolio pair (p,q), the sum runs over all ordered pairs of assets (a,b). If W is M×N and Σ is N×N, the output is M×M. The diagonal gives portfolio variances and the other entries give covariances between portfolios. William F. Sharpe, Portfolio Covariances
For two fictional portfolios with daily returns measured in percentage points, take
W=(0.6−0.20.41.2),Σ=(4119).
To get C12, fix the first portfolio on the left and the second on the right. The four contributions are −0.48, 0.72, −0.08 and 4.32. They sum to 4.48 percentage points squared. The contributions from the two different-asset pairs differ because the weights on the two sides differ, even though Σ is symmetric.
For portfolios P1 and P2, four asset-pair contributions of −0.48, 0.72, −0.08 and 4.32 sum to 4.48 percentage points squared. The resulting matrix keeps two portfolio axes, with variances 3.36 and 12.64 on the diagonal and 4.48 in both off-diagonal cells.
Each asset-pair term combines two weights with one asset covariance. Summing over both asset indices fills one cell of the portfolio matrix.
Read the output labels first
In NumPy, the explicit einsum string pa,ab,qb->pq keeps p then q and sums out a and b. Each comma-separated group gives the axis order for its input. Both weight inputs use portfolio rows and asset columns, so the same untransposed array appears twice. NumPy reference for numpy.einsum
If asset_cov becomes a stack with axes scenario, asset, asset and the weights stay fixed, pa,sab,qb->spq keeps one covariance matrix per scenario. Leaving s off the explicit output would sum across scenarios.
Check the asset order as well as the shape. Swapping the asset order in Σ alone leaves the shape and symmetry intact but pairs weights with the wrong assets.
The SelfMadeQuant lesson shows how to check the asset axis when computing a portfolio return series. The full lesson requires membership. Vectorisation