Sparse State Evolution Example

This tutorial shows how a SparseState evolves under operator applications, and further demonstrates the use of composite operators such as QFT and QRAM.

[ ]:
import pysparq as ps

ps.System.clear()

# Create a 2-bit register
ps.System.add_register("q", ps.UnsignedInteger, 2)

# Initial state
state = ps.SparseState()

print("Initial state:")
ps.pprint(state)
print(f"\nNumber of basis states: {state.size()}")

Hadamard Transform

Hadamard creates a quantum superposition.

[ ]:
# Hadamard_Int: apply Hadamard to a specified number of qubits
ps.Hadamard_Int("q", 1)(state)

print("After Hadamard_Int(q, 1):")
ps.pprint(state)
print(f"\nNumber of basis states: {state.size()}")
[ ]:
# Hadamard_Int_Full: apply a full Hadamard to all qubits
ps.Hadamard_Int_Full("q")(state)

print("After Hadamard_Int_Full(q):")
ps.pprint(state)
print(f"\nNumber of basis states: {state.size()}")

State Print Modes

StatePrint supports several display modes.

[ ]:
print("Default mode:")
print(ps.StatePrint(state, mode=ps.StatePrintDisplay.Default))
[ ]:
print("Binary mode:")
ps.pprint(state, mode=ps.StatePrintDisplay.Binary)
[ ]:
print("Prob mode:")
ps.pprint(state, mode=ps.StatePrintDisplay.Prob)

Arithmetic Operation Evolution

[ ]:
ps.System.clear()

# Create registers
ps.System.add_register("a", ps.UnsignedInteger, 2)
ps.System.add_register("b", ps.UnsignedInteger, 2)
ps.System.add_register("sum", ps.UnsignedInteger, 2)

state = ps.SparseState()

# Initialize
ps.Init_Unsafe("a", 1)(state)
ps.Init_Unsafe("b", 2)(state)

print("Initial state:")
ps.pprint(state)
[ ]:
# Addition: sum ^= a + b
ps.Add_UInt_UInt("a", "b", "sum")(state)

print("After Add_UInt_UInt:")
ps.pprint(state)
# sum = 0 ^ (1 + 2) = 3
[ ]:
# Applying it again undoes it (XOR mechanism)
ps.Add_UInt_UInt("a", "b", "sum")(state)

print("After applying Add_UInt_UInt again:")
ps.pprint(state)
# sum = 3 ^ 3 = 0

Arithmetic on Superpositions

[ ]:
ps.System.clear()

ps.System.add_register("x", ps.UnsignedInteger, 2)
ps.System.add_register("y", ps.UnsignedInteger, 2)

state = ps.SparseState()

# x is in superposition
ps.Hadamard_Int_Full("x")(state)

# y is initialized to a constant
ps.Init_Unsafe("y", 1)(state)

print("Initial superposition:")
ps.pprint(state)
[ ]:
# In-place addition: y += x (each branch computed independently)
ps.Add_UInt_UInt_InPlace("x", "y")(state)

print("After Add_UInt_UInt_InPlace:")
ps.pprint(state)
# Each branch: y = 1 + x
[ ]:
# Undo
ps.Add_UInt_UInt_InPlace("x", "y").dag(state)

print("After dagger:")
ps.pprint(state)

Accessing Basis-State Data

[ ]:
# Iterate over all basis states
print("Iterating over basis states:")
for i, system in enumerate(state.basis_states):
    x_id = ps.System.get_id("x")
    y_id = ps.System.get_id("y")

    x_val = system.get(x_id).value
    y_val = system.get(y_id).value
    amp = system.amplitude

    print(f"  basis state {i}: x={x_val}, y={y_val}, amplitude={amp}")

QRAM Data Loading

Load classical data in bulk through QRAM while in superposition:

[ ]:
import numpy as np

ps.System.clear()
n_addr, n_data = 3, 4
ps.System.add_register("addr", ps.UnsignedInteger, n_addr)
ps.System.add_register("data", ps.UnsignedInteger, n_data)

state = ps.SparseState()

# Classical data (8 memory locations)
memory = np.array([1, 3, 5, 7, 2, 4, 6, 8], dtype=np.uint64)

# Superpose the address register → query all addresses at once
ps.Hadamard_Int("addr")(state)

# QRAM load: data = memory[addr]
qram = ps.QRAMCircuit_qutrit(n_addr, n_data, memory)
ps.QRAMLoad(qram, "addr", "data")(state)

# The state holds the amplitudes of all (addr, memory[addr]) pairs
print(ps.StatePrint()(state))
print(f"\nNumber of basis states: {state.size()}")

QFT Transform

[ ]:
ps.System.clear()
ps.System.add_register("reg", ps.UnsignedInteger, 3)
state = ps.SparseState()

ps.Init_Unsafe("reg", 1)(state)
print("Initial:")
print(ps.StatePrint()(state))

ps.QFT("reg")(state)
print("After QFT:")
print(ps.StatePrint()(state))

ps.InverseQFT("reg")(state)
print("After inverse QFT:")
print(ps.StatePrint()(state))  # restored to |1⟩

Summary

  • SparseState stores only the non-zero basis states; their count grows with superposition

  • Hadamard creates superpositions

  • Arithmetic operations act on every basis state (sparse traversal)

  • SelfAdjointOperator restores the original value when applied twice; BaseOperator is undone with dag()

  • QRAM enables bulk lookup of classical data under superposition

  • QFT / InverseQFT are used in phase-estimation-style algorithms