← Back to Documentation

Partial Trace Implementation Guide

Overview

The partial trace operation is a fundamental operation in quantum mechanics that allows us to obtain the reduced density matrix of a subsystem by "tracing out" other subsystems. This document outlines the consolidated implementation approach for the quantum package.

Implementation Location

The partial trace implementation should be consolidated in a single location:

// Primary implementation: packages/quantum/src/operators/operator.ts
// In MatrixOperator class

All other implementations should be removed or refactored to use this primary implementation.

Mathematical Background

The partial trace operation $\text{Tr}B(\rho{AB})$ for a composite system $\rho_{AB}$ is defined as:

$\text{Tr}B(\rho{AB}) = \sum_i (I_A \otimes \langle i_B|)\rho_{AB}(I_A \otimes |i_B\rangle)$

where:

Implementation Details

1. Core Implementation

partialTrace(dims: number[], traceOutIndices: number[]): IOperator {
    // Validate dimensions
    const totalDim = dims.reduce((a, b) => a * b, 1);
    if (totalDim !== this.dimension) {
        throw new Error('Product of subsystem dimensions must equal total dimension');
    }

    // Validate trace indices
    if (!traceOutIndices.every(i => i >= 0 && i < dims.length)) {
        throw new Error('Invalid trace out indices');
    }

    // Calculate remaining dimension
    const remainingDim = dims
        .filter((_, i) => !traceOutIndices.includes(i))
        .reduce((a, b) => a * b, 1);

    // Initialize result matrix
    const resultMatrix = Array(remainingDim).fill(null)
        .map(() => Array(remainingDim).fill(null)
            .map(() => math.complex(0, 0)));

    // Perform partial trace using coordinate mapping
    const traceRange = Array(this.dimension).fill(0).map((_, i) => i);

    for (let i = 0; i < remainingDim; i++) {
        for (let j = 0; j < remainingDim; j++) {
            for (const k of traceRange) {
                // Map indices to multi-dimensional coordinates
                const iCoords = indexToCoords(i, dims.filter((_, idx) => 
                    !traceOutIndices.includes(idx)));
                const jCoords = indexToCoords(j, dims.filter((_, idx) => 
                    !traceOutIndices.includes(idx)));
                const kCoords = indexToCoords(k, dims.filter((_, idx) => 
                    traceOutIndices.includes(idx)));
                
                // Combine coordinates
                const fullICoords = combineCoords(iCoords, kCoords, traceOutIndices);
                const fullJCoords = combineCoords(jCoords, kCoords, traceOutIndices);
                
                // Map back to flat indices
                const fullI = coordsToIndex(fullICoords, dims);
                const fullJ = coordsToIndex(fullJCoords, dims);
                
                // Add to result
                resultMatrix[i][j] = math.add(
                    resultMatrix[i][j],
                    this.matrix[fullI][fullJ]
                ) as Complex;
            }
        }
    }

    return new MatrixOperator(resultMatrix);
}

2. Helper Functions

function indexToCoords(index: number, dims: number[]): number[] {
    const coords: number[] = [];
    let remainder = index;
    for (let i = dims.length - 1; i >= 0; i--) {
        coords.unshift(remainder % dims[i]);
        remainder = Math.floor(remainder / dims[i]);
    }
    return coords;
}

function coordsToIndex(coords: number[], dims: number[]): number {
    let index = 0;
    let factor = 1;
    for (let i = coords.length - 1; i >= 0; i--) {
        index += coords[i] * factor;
        factor *= dims[i];
    }
    return index;
}

function combineCoords(coords1: number[], coords2: number[], 
    traceIndices: number[]): number[] {
    const result: number[] = [];
    let i1 = 0;
    let i2 = 0;
    for (let i = 0; i < coords1.length + coords2.length; i++) {
        if (traceIndices.includes(i)) {
            result.push(coords2[i2++]);
        } else {
            result.push(coords1[i1++]);
        }
    }
    return result;
}

3. Standardized Validation

All validation should be consolidated into the validatePartialTrace function in utils/validation.ts:

export function validatePartialTrace(
    dims: number[],
    totalDim: number,
    traceOutIndices: number[]
): void {
    // Validate total dimension
    const dimProduct = dims.reduce((a, b) => a * b, 1);
    if (dimProduct !== totalDim) {
        throw new Error(
            'Product of subsystem dimensions must equal total dimension'
        );
    }

    // Validate trace indices
    if (!traceOutIndices.every(i => i >= 0 && i < dims.length)) {
        throw new Error('Invalid trace out indices');
    }

    // Validate remaining dimension is non-zero
    const remainingDim = dims
        .filter((_, i) => !traceOutIndices.includes(i))
        .reduce((a, b) => a * b, 1);
    if (remainingDim === 0) {
        throw new Error('Cannot trace out all subsystems');
    }
}

Usage Examples

1. Basic Usage

// Create a 4x4 density matrix (2-qubit system)
const matrix = new MatrixOperator(/* ... */);

// Trace out second qubit
const reducedDensity = matrix.partialTrace([2, 2], [1]);

2. Multi-qubit System

// Create an 8x8 density matrix (3-qubit system)
const matrix = new MatrixOperator(/* ... */);

// Trace out first and third qubits
const reducedDensity = matrix.partialTrace([2, 2, 2], [0, 2]);

Common Pitfalls

  1. Dimension Mismatch: Ensure the product of subsystem dimensions equals the total dimension
  2. Invalid Trace Indices: Trace indices must be valid for the given system
  3. Complete Trace: Cannot trace out all subsystems
  4. Order Matters: The order of subsystem dimensions must match the physical system

Performance Considerations

  1. Pre-compute coordinate mappings for repeated operations
  2. Use optimized matrix operations for large systems
  3. Consider sparse matrix representations for high-dimensional systems

Testing Strategy

  1. Unit Tests:

  2. Integration Tests:

Future Improvements

  1. Implement sparse matrix support for large systems
  2. Add optimization for common cases (single qubit trace, etc.)
  3. Add support for symbolic computation
  4. Consider adding parallel processing for large systems

References

  1. Nielsen & Chuang, "Quantum Computation and Quantum Information"
  2. Preskill, J. "Lecture Notes for Physics 229: Quantum Information and Computation"
Last Updated: 2025-07-06