Engineering Case Study: Simulating 30+ Qubits with StateVector & MPS Tensor Networks

Quantum simulation becomes difficult very quickly. A state-vector simulator requires 2^n complex amplitudes for n qubits, meaning that increasing the number of qubits isn't a linear scaling problem.
While building Itachi Quantum Studio, I wanted to understand how far a practical C++20 simulation engine could go—and when switching from an exact state-vector representation to a tensor-network representation would make more sense.
The result is a multi-engine simulation architecture combining an optimized StateVector simulator with a Matrix Product State (MPS) backend.
The StateVector Problem
For an n-qubit pure state:
|ψ⟩ = Σ αᵢ|i⟩
the simulator stores 2^n complex amplitudes.
That gives a simple progression:
| Qubits | Amplitudes |
|---|---|
| 10 | 1,024 |
| 20 | ~1 million |
| 30 | ~1.07 billion |
At 30 qubits, the state contains more than one billion complex amplitudes.
Assuming 16 bytes per complex double, the raw state vector alone requires roughly 16 GiB of memory.
And that's before considering temporary buffers, circuit operations, telemetry, and other application overhead.
So simply increasing the qubit count isn't a sustainable strategy.
Why C++20?
The simulation core is implemented in modern C++20.
The goal was to keep the computationally expensive operations away from the browser and make use of native performance features.
The core uses:
C++20
Eigen for numerical operations
OpenMP for parallel execution
AVX2 SIMD optimization
Native memory management
For a state-vector simulator, operations frequently touch large portions of the state array. Memory access patterns therefore matter almost as much as the mathematical operation itself.
Applying Single-Qubit Gates
A single-qubit gate operates on pairs of amplitudes.
For a target qubit, amplitudes are grouped into pairs:
[a₀, a₁]
and transformed using the gate matrix:
[b₀] [u₀₀ u₀₁] [a₀][b₁] = [u₁₀ u₁₁] [a₁]
The challenge isn't the matrix multiplication itself.
The challenge is efficiently finding the correct amplitude pairs across a potentially huge state vector.
This is where careful indexing, contiguous memory access where possible, parallelization, and SIMD optimization become important.
Reaching 30+ Qubits
The StateVector backend is intended for exact simulation where the memory requirements are practical.
The important point is that 30+ qubits doesn't mean every arbitrary 30+ qubit circuit will be inexpensive.
State-vector simulation has exponential memory requirements regardless of how optimized the implementation is.
This makes the choice of simulation representation critical.
That led to the second engine.
Matrix Product States
An MPS represents a many-qubit state as a chain of tensors rather than one enormous vector.
Conceptually:
|ψ⟩ → A₁ — A₂ — A₃ — ... — Aₙ
Each tensor contains local information about a qubit and correlations with neighboring tensors.
The key parameter is the bond dimension.
For low-entanglement systems, the bond dimensions can remain relatively small compared with the full Hilbert-space dimension.
This can dramatically reduce the computational and memory requirements.
The Trade-Off
MPS isn't a magic replacement for state vectors.
Highly entangled states can cause bond dimensions to grow substantially.
As the bond dimension increases, tensor contractions become more expensive and the representation can approach the complexity we're trying to avoid.
Therefore:
StateVector → exact and straightforward, but exponentially expensive
MPS → potentially much more scalable, but dependent on entanglement structure
This distinction is central to the multi-engine architecture.
Why Have Both?
The simulator can use different representations depending on the circuit and experiment.
For relatively small systems where exact amplitudes are practical, StateVector is useful.
For larger low-entanglement circuits, MPS can provide a much more efficient representation.
This makes the architecture less dependent on a single simulation strategy.
The broader goal is to eventually make engine selection increasingly intelligent rather than forcing users to understand the implementation details before running an experiment.
MPS and Truncation
After tensor operations, bond dimensions can grow.
To control this, tensor decompositions such as SVD can be used to identify less significant components.
A simplified workflow is:
Tensor contraction
→ SVD
→ Singular-value analysis
→ Bond truncation
→ Compressed MPS
The trade-off is straightforward:
More aggressive truncation → lower computational cost but potentially greater approximation error
Less truncation → greater accuracy but higher computational cost
This makes truncation parameters an important part of an MPS simulator.
Building a Unified Simulation API
One of the architectural decisions I found particularly useful was separating the frontend from the simulation representation.
The circuit is represented independently.
The backend receives the circuit and executes it using the selected simulation engine.
The result is converted into a common telemetry representation for the frontend.
That means the same circuit UI can eventually work with:
StateVector
MPS
Stabilizer simulation
Density-matrix/Lindblad simulation
without requiring four different frontends.
Visualization as a Debugging Tool
The simulation output isn't only used to calculate a final answer.
It also feeds the platform's visualization layer.
For single-qubit states, the simulator can expose Bloch coordinates:
(rx, ry, rz)
along with phase, probability, and purity information.
The frontend renders this telemetry through Three.js/WebGL.
This turned out to be useful beyond education.
Visualization can expose unexpected state behavior much faster than inspecting a large numerical array.
What I Learned
The biggest lesson from working on 30+ qubit simulation is that qubit count alone isn't enough to describe simulation difficulty.
The representation matters.
For exact state-vector simulation, memory grows exponentially with qubit count.
For MPS, the important factor becomes the entanglement structure and resulting bond dimensions.
That leads to a more useful question:
What representation is appropriate for this circuit?
That's the direction I'm taking with Itachi Quantum Studio.
The platform currently combines StateVector and MPS simulation with additional engines for stabilizer and noisy-system simulation, giving different circuit types different computational paths.
The project is still evolving, particularly around automatic engine selection, performance profiling, tensor-network optimization, and larger-scale experiments.
Try the platform: https://itachi-quantum.onrender.com
