QRAM data structures: pointers, offsets, and random writes

English · 简体中文

QMem abstracts QRAM resources into C-style array access: base addresses, constant and quantum offsets, multidimensional views, and random reads and writes. All of the addressing is Python generation-stage sugar — what lands in the RIR is only register arithmetic, Load, and Store (see the RIR specification, section 3.2). The API is documented in QRAM pointer-style reads and writes.

Pointers and reads

QMem binds a QRAM resource already declared in the builder. ptr() returns a pointer to cell 0; integer addition and subtraction produce constant offsets; passing a register view yields a pointer holding a quantum address. Dereferencing with load is the existing XOR-Load: |addr⟩|d⟩ ↦ |addr⟩|d ⊕ M[addr]⟩, with superposed addresses supported for free.

>>> from oracq import Builder, QRAM, QMem, UInt, simulate
>>> b = Builder("demo", {"idx": UInt(2), "out": UInt(4)}, {"rom": QRAM(2, 4)})
>>> mem = QMem(b, "rom")
>>> (mem.ptr() + 1).load(b["out"])
>>> state = simulate(b.finish().program(), {"rom": [10, 11, 12, 13]})
>>> sorted(state.amplitudes.items())
[((0, 11), (1+0j))]

Quantum offsets (p + b["idx"]) and multidimensional flattening require register addition: QMem automatically synthesizes bit-by-bit carry adders and shift-concatenation; the temporary address registers are cleaned by a whole-block Adjoint after dereferencing, keeping the module unitary. The gate cost of address arithmetic is counted faithfully in resource estimation. When the address expression is exactly a single full-width register with no constant component, no addressing arithmetic is introduced.

Multidimensional views

shape declares row-major flattening; subscripts may be integers, register views, or slices, and the width of a quantum subscript must satisfy 2^width not exceeding that dimension’s length. A slice returns a re-addressed subarray view.

>>> b = Builder("grid", {"row": UInt(2), "out": UInt(4)}, {"rom": QRAM(4, 4)})
>>> grid = QMem(b, "rom", shape=(4, 4))
>>> grid[b["row"], 2].load(b["out"])
>>> state = simulate(b.finish().program(), {"rom": list(range(16))}, initial={"row": 3})
>>> sorted(state.amplitudes.items())
[((3, 14), (1+0j))]

Random writes

store is the RIR Store instruction: M[addr] := data. Memory cells are modeled classically and random writes carry no gate cost (counted separately as qram_writes in resource estimation). At execution the address and data registers must be in a definite basis state; a write under a superposed address has no linear semantics and the executor raises immediately. Structurally, Store cannot appear inside a Control or Adjoint body, and modules containing Store have neither the controlled nor the adjoint capability.

>>> b = Builder("write", {"addr": UInt(2), "val": UInt(4), "out": UInt(4)}, {"ram": QRAM(2, 4)})
>>> mem = QMem(b, "ram")
>>> mem[b["addr"]].store(b["val"])
>>> mem.ptr(3).load(b["out"])
>>> state = simulate(b.finish().program(), {"ram": [0, 0, 0, 0]}, initial={"addr": 1, "val": 9})
>>> sorted(state.amplitudes.items())
[((1, 9, 0), (1+0j))]

A subsequent Load reads the evolved memory: in the example above cell 1 now holds 9 while cell 3 is still 0. The OriginIR-ext export lowers Store to the QRAMWRITE extension line; the text executors (UnifiedQuantum, PySparQ) do not yet accept runtime writes and raise at the execution entry when a program contains Store; text export is unaffected.

Capability overview

Capability

Form

Lowering result

Constant offset

mem.ptr(2) + 3, p - 1

add_const (modulo 2^address width)

Quantum pointer

mem.ptr(b["addr"])

direct addressing or shift-concatenation

Quantum offset

p + b["idx"]

when overlapping the pointer range, a carry adder is synthesized (Toffoli/CNOT)

Multidimensional indexing

grid[b["row"], 3]

shift-concatenation; no adder needed when the dimensions occupy disjoint bit segments

Subarray view

grid[2:]

constant re-addressing, no instructions

The full semantic tests are in tests/core/test_qmem.py.