The SparseState Class¶
The SparseState class is the core data structure of PySparQ; it represents a sparse quantum state. It internally manages a std::vector<System> and stores only basis states with non-zero amplitudes.
Sparse Representation Principles¶
A traditional full state-vector simulator stores \(2^n\) amplitudes (\(n\) being the number of qubits), regardless of whether most of them are zero. PySparQ uses a sparse representation:
Only basis states with
amplitude ≠ 0are storedEach
Systemrepresents one computational basis stateWhen the number of superposed states is finite, the storage complexity is polynomial
Uniqueness Rule¶
A core invariant of SparseState is: the register-value combination of every ``System`` must be unique.
If an operation on the basis states of a SparseState produces two System objects with identical register values, this means quantum interference has occurred — in that case the amplitudes of the two System objects should be added and they should be merged into a single System. This process is usually performed automatically by the sort-merge-unique mechanism inside operators.
import pysparq as ps
ps.System.clear()
ps.System.add_register("q", ps.UnsignedInteger, 2)
# SparseState() default construction: creates a single |q=0⟩ basis state
state = ps.SparseState()
print(f"basis-state count: {state.size()}") # 1
# Apply Hadamard to create 2^2 = 4 basis states
ps.Hadamard_Int_Full("q")(state)
print(f"basis-state count: {state.size()}") # 4
Basic Usage¶
Creating a SparseState¶
Important
The default constructor of SparseState() automatically creates a |0...0⟩ initial state (i.e. a single System whose register values are all 0 and whose amplitude is 1). You usually do not need to manually construct System objects to create a SparseState.
import pysparq as ps
ps.System.clear()
ps.System.add_register("q", ps.UnsignedInteger, 10)
# Default construction: creates a single |q=0⟩ basis state (amplitude 1)
state = ps.SparseState()
Accessing Basis States¶
# Get the list of basis states
for system in state.basis_states:
print(f"amplitude: {system.amplitude}")
# Access by index
first = state[0]
last = state.basis_states[-1]
# Get the number of basis states
n = state.size()
# Check whether the state is empty
if state.empty():
print("state is empty")
State Evolution Example¶
The following example shows how a SparseState evolves under operator actions:
import pysparq as ps
ps.System.clear()
ps.System.add_register("q", ps.UnsignedInteger, 2)
# SparseState creates the |q=0⟩ initial state by default
state = ps.SparseState()
print("initial state:")
ps.pprint(state)
# Output:
# StatePrint (mode=Detail)
# |(0)q : UInt2 |
# 1.000000+0.000000i q=|0>
# Apply Hadamard (partial superposition: apply H to bit 0)
ps.Hadamard_Int("q", 1)(state)
print("\nafter Hadamard:")
ps.pprint(state)
# Output:
# StatePrint (mode=Detail)
# |(0)q : UInt2 |
# 0.707107+0.000000i q=|0>
# 0.707107+0.000000i q=|2>
# Full Hadamard (all output states)
ps.Hadamard_Int_Full("q")(state)
print("\nafter full Hadamard:")
ps.pprint(state)
# Output:
# StatePrint (mode=Detail)
# |(0)q : UInt2 |
# 0.500000+0.000000i q=|0>
# 0.500000+0.000000i q=|1>
# 0.500000+0.000000i q=|2>
# 0.500000+0.000000i q=|3>
State Printing Modes¶
ps.StatePrint(state, mode) and ps.pprint(state, mode) support several display modes:
Mode |
Value |
Description |
Example output |
|---|---|---|---|
|
0 |
Default mode, decimal values |
|
|
1 |
Detailed mode, with register header and amplitudes |
|
|
2 |
Binary representation |
|
|
4 |
Probability view |
|
# Different display modes
ps.pprint(state) # Detail (default)
print(ps.StatePrint(state, mode=ps.StatePrintDisplay.Default)) # Default
print(ps.StatePrint(state, mode=ps.StatePrintDisplay.Binary)) # Binary
print(ps.StatePrint(state, mode=ps.StatePrintDisplay.Prob)) # Prob
# Specify the precision
print(ps.StatePrint(state, mode=ps.StatePrintDisplay.Default, precision=15))
print(ps.StatePrint(state, mode=ps.StatePrintDisplay.Default, precision=4))
Clearing Near-Zero Amplitudes¶
# Remove basis states with |amplitude|² < epsilon
ps.ClearZero(epsilon=1e-10)(state)
# Normalize the state
ps.Normalize()(state)
Merging Duplicate Basis States¶
When multiple basis states have identical register values, their amplitudes are merged automatically:
# The merge_system function: merges the amplitudes of two identical basis states
# Usually called automatically inside operators
Iterator Support¶
SparseState supports the standard Python iterator protocol:
# Forward iteration
for system in state.basis_states:
print(system.amplitude)
# Reverse iteration
for system in reversed(state.basis_states):
print(system.amplitude)
# Index access
first = state.basis_states[0]
last = state.basis_states[-1]
# Slicing
first_three = state.basis_states[:3]