Register Management¶
Registers are the core abstraction of PySparQ’s “Register Level Programming”. Unlike traditional qubit-level programming, PySparQ raises the level of operations to quantum registers, which makes algorithm development more intuitive.
The Nature of Registers¶
A register in the System hosts storage of n uint64_t values. Each register has a name, a type, and a bit width, and its value is stored as a uint64_t. This design allows us to encode quantum states in a multi-register form such as \(|a\rangle|b\rangle|c\rangle\) without having to care about how the underlying qubits are encoded.
For example, the QRAM access \(|i\rangle|0\rangle \to |i\rangle|d[i]\rangle\) requires only an address register i and a data register d: the QRAMLoad operator completes the mapping directly at the register level, without managing any qubits at all.
where each \(|a_j\rangle |b_j\rangle |c_j\rangle\) corresponds to one entry of the registers array in a System.
Adding Registers: AddRegister¶
Adding a register is equivalent to taking the tensor product with a \(|0\rangle\) register. The new register has the initial value 0 in all existing basis states.
import pysparq as ps
ps.System.clear()
ps.System.add_register("a", ps.UnsignedInteger, 4)
state = ps.SparseState()
# Add register "b": equivalent to |a⟩ ⊗ |0⟩
ps.AddRegister("b", ps.UnsignedInteger, 4)(state)
# The "b" value of every basis state in state is 0
ps.pprint(state)
AddRegister updates both the static metadata (name_register_map) and the register values of all existing basis states.
You can also use AddRegisterWithHadamard to apply a Hadamard while adding the register, directly creating a uniform superposition:
# Add "q" and create a uniform superposition of |0⟩, |1⟩, ..., |2^n - 1⟩
ps.AddRegisterWithHadamard("q", ps.UnsignedInteger, 2)(state)
Removing Registers: RemoveRegister¶
Removing a register is equivalent to taking the PartialTrace over that register, i.e. eliminating its contribution to the quantum state. In a simulation this has the same effect as measuring the register and then discarding the measurement result.
# Remove register "b": equivalent to a PartialTrace over "b"
ps.RemoveRegister("b")(state)
Important
RemoveRegister checks, before executing, whether the register is entangled with the remaining registers (via TestRemovable). If entanglement exists, the removal raises an exception, because in that case the PartialTrace over a single register can no longer be treated simply as discarding it.
Splitting Registers: SplitRegister¶
SplitRegister splits one register into two: the original register keeps the high bits, and the new register receives the low bits.
ps.System.clear()
ps.System.add_register("full", ps.UnsignedInteger, 8)
state = ps.SparseState()
ps.Init_Unsafe("full", 0b10110011)(state)
# Split: the original "full" keeps the high 4 bits, the new "low" receives the low 4 bits
ps.SplitRegister("full", "low", 4)(state)
# "full" = 0b1011 (high 4 bits), "low" = 0b0011 (low 4 bits)
The split process:
Add the new register (it receives the low bits)
Shrink the bit width of the original register
In all basis states, keep the high bits of the original value in the original register and write the low bits into the new register
Combining Registers: CombineRegister¶
CombineRegister merges two registers into one: the value of the first register is shifted left and concatenated with the value of the second register.
# Combine: "full" = (full << 4) + low
ps.CombineRegister("full", "low")(state)
# "full" = 0b10110011
The merge process:
Extend the bit width of the first register (by the width of the second)
In all basis states, shift the first register’s value left by the second register’s width and add the second register’s value
Remove the second register
PartialTrace: Measurement¶
In PySparQ, PartialTrace is treated as being equivalent to a measurement. This is reasonable at the simulation level — taking a partial trace over some registers is the same as measuring them and discarding the results.
PartialTrace provides three modes:
Class |
Behavior |
Return value |
|---|---|---|
|
Random measurement: randomly pick a measurement outcome according to the probability distribution, then collapse the state |
|
|
Selective collapse: keep the basis states whose specified register has a specified value, and renormalize the remaining ones |
Normalized probability |
|
Range collapse: keep the basis states whose specified register value lies within a given range |
Normalized probability |
import pysparq as ps
ps.System.clear()
ps.System.add_register("a", ps.UnsignedInteger, 2)
ps.System.add_register("b", ps.UnsignedInteger, 2)
state = ps.SparseState()
ps.Hadamard_Int("a")(state)
ps.Hadamard_Int("b")(state)
# Randomly measure register "a"
measured_values, prob = ps.PartialTrace("a")(state)
print(f"Measured values: {measured_values}, probability: {prob}")
# The value of "a" in state collapses to the measurement result
# Selective collapse: force "b" = 2
prob = ps.PartialTraceSelect("b", [2])(state)
# Keep only the basis states with "a" = 1 and normalize
prob = ps.PartialTraceSelect("a", [1])(state)
Register Stack Operations: Push / Pop¶
Push and Pop provide stack management for temporary registers, which is useful when an algorithm needs temporary variables:
# Push: push a temporary register onto the stack
ps.Push("temp", ps.UnsignedInteger, 4)(state)
# Perform computations with the temporary register...
# Pop: pop the temporary register
ps.Pop()(state)
Summary: Register Operations and the Quantum State¶
Operation |
Physical meaning |
Effect on the quantum state |
|---|---|---|
|
Tensor product with \(|0\rangle\) |
Every basis state gains one register with value 0 |
|
PartialTrace (measure and discard) |
Eliminates the register, provided it is not entangled |
|
Splits one subsystem into two |
Number of basis states unchanged, number of registers +1 |
|
Merges two subsystems into one |
Number of basis states unchanged, number of registers -1 |
|
Measurement |
Collapses the state according to the probabilities, reducing the number of basis states |
|
Push/pop of temporary registers |
Auxiliary operations that save/restore register state |
API Reference¶
For the complete API documentation of the register management operators, see the following operator reference pages:
or consult the Operator Reference section for the complete list of all operators.