Source code for uniqc.circuit_builder.qram
"""Quantum RAM (QRAM) data structure.
Provides a simple binary-addressed classical memory used by quantum circuits.
A QRAM is declared with an address size and data size; at runtime it stores
integers (0 <= value < 2^data_size) indexed by addresses (0 <= addr < 2^addr_size).
Example usage in OriginIR-ext::
QRAMDECL my_ram 3,6
QINIT 9
CREG 0
my_ram q[0],q[1],q[2],q[3],q[4],q[5],q[6],q[7],q[8]
The above declares ``my_ram`` as a QRAM with 3-bit address and 6-bit data
(9 qubits total). Callers index it as
``my_ram q[0],q[1],q[2],q[3],q[4],q[5],q[6],q[7],q[8]`` where q[0..2] are
the address bits (q[0] = LSB) and q[3..8] are the data bits (q[3] = LSB).
"""
__all__ = ["QRAM"]
MAX_QUBITS = 30
[docs]
class QRAM:
"""A simple binary-addressed quantum RAM.
Args:
name: Identifier for this QRAM instance.
addr_size: Number of address qubits (determines number of entries).
data_size: Number of data qubits (determines max storable value).
"""
def __init__(self, name: str, addr_size: int, data_size: int):
if addr_size <= 0 or data_size <= 0:
raise ValueError(
f"QRAM '{name}': addr_size and data_size must be positive integers, "
f"got addr_size={addr_size}, data_size={data_size}."
)
if addr_size + data_size > MAX_QUBITS:
raise ValueError(
f"QRAM '{name}': total qubits ({addr_size}+{data_size}={addr_size + data_size}) "
f"exceeds maximum ({MAX_QUBITS})."
)
self.name = name
self.addr_size = addr_size
self.data_size = data_size
self._data = [0] * (1 << addr_size)
@property
def total_qubits(self) -> int:
"""Total number of qubits required: address + data."""
return self.addr_size + self.data_size
@property
def num_entries(self) -> int:
"""Number of addressable entries: 2^addr_size."""
return len(self._data)
@property
def max_value(self) -> int:
"""Maximum storable value: 2^data_size - 1."""
return (1 << self.data_size) - 1
[docs]
def read(self, addr: int) -> int:
"""Read the value stored at *addr*.
Args:
addr: Address index (0 <= addr < 2^addr_size).
Returns:
The stored integer.
Raises:
IndexError: If *addr* is out of range.
"""
if not (0 <= addr < len(self._data)):
raise IndexError(
f"QRAM '{self.name}': address {addr} out of range "
f"[0, {len(self._data) - 1}]."
)
return self._data[addr]
[docs]
def write(self, addr: int, value: int) -> None:
"""Write *value* at address *addr*.
Args:
addr: Address index (0 <= addr < 2^addr_size).
value: Integer to store (0 <= value < 2^data_size).
Raises:
IndexError: If *addr* is out of range.
ValueError: If *value* exceeds the data capacity.
"""
if not (0 <= addr < len(self._data)):
raise IndexError(
f"QRAM '{self.name}': address {addr} out of range "
f"[0, {len(self._data) - 1}]."
)
if not (0 <= value <= self.max_value):
raise ValueError(
f"QRAM '{self.name}': value {value} exceeds data capacity "
f"[0, {self.max_value}] (data_size={self.data_size})."
)
self._data[addr] = value
[docs]
def reset(self, value: int = 0) -> None:
"""Reset all entries to *value* (default 0).
Args:
value: Value to fill every address with.
Raises:
ValueError: If *value* exceeds the data capacity.
"""
if not (0 <= value <= self.max_value):
raise ValueError(
f"QRAM '{self.name}': value {value} exceeds data capacity "
f"[0, {self.max_value}] (data_size={self.data_size})."
)
self._data = [value] * len(self._data)
def __repr__(self) -> str:
return (
f"QRAM(name={self.name!r}, addr_size={self.addr_size}, "
f"data_size={self.data_size}, num_entries={self.num_entries})"
)
def __eq__(self, other) -> bool:
if not isinstance(other, QRAM):
return NotImplemented
return (
self.name == other.name
and self.addr_size == other.addr_size
and self.data_size == other.data_size
and self._data == other._data
)