🧮 Exact Statevector Simulation
The Statevector Simulator, implemented in simulator.py, is the exact execution engine of the QVM. It represents the quantum state as a complex vector and applies unitary matrices to evolve the state.
💾 Mathematical State Representation
For an -qubit system, the statevector is a complex NumPy array of size (dtype=complex128):
Under the Born Rule, the amplitudes must satisfy the normalization condition:
Little-Endian Qubit Ordering
QVM uses the Little-Endian convention:
- Qubit is the Least Significant Bit (LSB) (rightmost in bitstrings).
- State index (binary ) corresponds to state (where Qubit is , and all others are ).
- In tensor products, Qubit is the fastest-changing index (rightmost in Kronecker products).
⚡ Vectorized Gate Application
Applying a gate to target qubits requires evolving the statevector . To simulate this efficiently in Python, the simulator uses NumPy vectorization instead of loops.
1. Single-Qubit Gates (Tensor Product)
For a single-qubit gate acting on target qubit , the global operator is:
The simulator builds this matrix using np.kron and multiplies it by the statevector:
def _apply_single_qubit_gate(self, state, gate, target, n):
op_list = [self.I] * n
op_list[n - 1 - target] = gate # Reverse index for little-endian order
full_op = op_list[0]
for i in range(1, n):
full_op = np.kron(full_op, op_list[i])
return full_op @ state2. Multi-Qubit Gates (Vectorized Index Permutation)
Creating a matrix for multi-qubit gates (like CNOT, CZ, SWAP) becomes memory intensive as grows. To avoid this, QVM uses vectorized index permutation to update the statevector directly.
A. CNOT Gate (Control , Target )
A CNOT gate flips the target qubit if the control qubit is .
- Permutation Rule: For all indices where the bit at position is , swap the amplitude with the index where the bit at position is flipped.
def _apply_cnot_gate(self, state, ctrl, target, n):
indices = np.arange(2**n)
# 1. Find indices where control bit is 1
mask = (indices >> ctrl) & 1 == 1
# 2. Compute partner indices by flipping target bit
perm = indices.copy()
perm[mask] = indices[mask] ^ (1 << target)
# 3. Swap amplitudes via index slicing
return state[perm]B. SWAP Gate (Qubits )
A SWAP gate swaps the states of qubits and .
- Permutation Rule: For all indices where the bit at position differs from the bit at position , swap their amplitudes.
def _apply_swap_gate(self, state, q1, q2, n):
indices = np.arange(2**n)
# Find indices where bit_q1 != bit_q2
diff = ((indices >> q1) & 1) != ((indices >> q2) & 1)
perm = indices.copy()
# Flip both bits to find swap partner
perm[diff] = indices[diff] ^ ((1 << q1) | (1 << q2))
return state[perm]C. CZ Gate (Control , Target )
A Controlled-Z gate applies a phase flip (multiplies by ) only if both qubits are in the state.
- Permutation Rule: Identify indices where both bits at positions and are , and multiply those amplitudes by .
def _apply_cz_gate(self, state, ctrl, target, n):
indices = np.arange(2**n)
mask = ((indices >> ctrl) & 1 == 1) & ((indices >> target) & 1 == 1)
result = state.copy()
result[mask] = -result[mask]
return result📉 Projective Measurement and State Collapse
Mid-circuit measurement causes the quantum state to collapse. When measuring a subset of qubits :
- Calculate Probabilities: Computes the probability of each outcome on the measured qubits by summing the squared magnitudes of the matching statevector entries:
- Stochastic Choice: Samples an outcome based on these probabilities.
- State Projection: Set amplitudes of non-matching states to :
- Renormalization: Renormalizes the remaining statevector to preserve probability conservation:
# Project and collapse slice
collapsed = np.zeros_like(statevector)
collapsed[mask] = statevector[mask]
collapsed = collapsed / np.linalg.norm(collapsed)🖥️ Classical Register Operations (classical_op)
The simulator contains a classical register execution engine. When it encounters a classical_op instruction, it evaluates classical bitwise expressions:
| Operator | Action | Implementation |
|---|---|---|
= | Direct copy | mem[target] = val |
& | Bitwise AND | mem[target] = val1 & val2 |
| | Bitwise OR | mem[target] = val1 | val2 |
^ | Bitwise XOR | mem[target] = val1 ^ val2 |
~ | Bitwise NOT | mem[target] = ~val1 & 1 (keeps it single bit) |
This classical logic engine allows QVM to run hybrid quantum-classical algorithms, where future gate decisions are based on classical registers updated by mid-circuit measurements.