"""Random OpenQASM 2.0 circuit generator.
This module provides functions for generating random quantum circuits in OpenQASM
2.0 format. It supports random gate selection, measurement generation, and
circuit construction from opcode lists.
Key exports:
build_qasm_gate: Build a single QASM gate string.
build_full_measurements: Generate measurement instructions for all qubits.
build_measurements: Generate measurement instructions for specified qubits.
random_qasm: Generate a complete random QASM program.
build_qasm_from_opcodes: Convert a list of opcodes to QASM code.
"""
import random
from typing import List
from .qasm_spec import available_qasm_gates
from uniqc.circuit_builder.qcircuit import OpcodeType
__all__ = [
"build_qasm_gate",
"build_full_measurements",
"build_measurements",
"random_qasm",
"build_qasm_from_opcodes",
]
[docs]
def build_qasm_gate(gate, qubits, params, qreg_name="q"):
"""
Build a QASM gate string with the given gate, qubits, and parameters.
Args:
gate (str): name of the gate
qubits (List[int]): list of qubits the gate acts on
params (List[float]): list of parameters of the gate
Returns:
str: a QASM gate string
"""
if not qubits:
raise ValueError("No qubits specified for gate")
gate_with_params = f"{gate}"
if params:
param_strs = [f"{param}" for param in params]
gate_with_params += "("
gate_with_params += ",".join(param_strs)
gate_with_params += ")"
qreg_strs = [f"{qreg_name}[{qubit}]" for qubit in qubits]
qreg_str = ",".join(qreg_strs)
qasm_gate = f"{gate_with_params} {qreg_str};"
return qasm_gate
[docs]
def build_full_measurements(n_qubits, qreg_name="q", creg_name="c"):
"""
Build a QASM string that measures all qubits in the given qreg to the given creg.
Args:
n_qubits (int): number of qubits to measure
qreg_name (str): name of the qreg to measure from
creg_name (str): name of the creg to measure to
Returns:
List[str]: a list of QASM strings that measure all qubits in the given qreg to the given creg.
"""
measure_instructions = []
for i in range(n_qubits):
measure_instructions.append(f"measure {qreg_name}[{i}] -> {creg_name}[{i}];")
return measure_instructions
[docs]
def build_measurements(measure_qbit_cbit_pairs, qreg_name="q", creg_name="c"):
"""
Build a QASM string that measures specified qubits to classical bits.
Args:
measure_qbit_cbit_pairs: Iterable of (qubit_index, cbit_index) tuples.
qreg_name (str): name of the quantum register.
creg_name (str): name of the classical register.
Returns:
List[str]: a list of QASM measurement instructions.
"""
measure_instructions = []
for qbit, cbit in measure_qbit_cbit_pairs:
measure_instructions.append(f"measure {qreg_name}[{qbit}] -> {creg_name}[{cbit}];")
return measure_instructions
[docs]
def random_qasm(n_qubits, n_gates, instruction_set=available_qasm_gates, measurements=True):
"""
Generate a random QASM code with n_qubits and n_gates from the given instruction set.
Args:
n_qubits (int): number of qubits in the circuit
n_gates (int): number of gates in the circuit
instruction_set (Dict): list of valid QASM instructions
Returns:
str: a random QASM code
"""
qasm = [
"OPENQASM 2.0;",
'include "qelib1.inc";',
f"qreg q[{n_qubits}];",
f"creg c[{n_qubits}];",
"// auto-generated by uniqc;",
]
instructions = list(instruction_set.keys())
for i in range(n_gates):
gate = random.choice(instructions)
nqubit = instruction_set[gate]["qubit"]
nparam = instruction_set[gate]["params"]
qubits = random.sample(range(n_qubits), nqubit)
params = [random.uniform(0, 2 * 3.14159) for _ in range(nparam)]
qasm_gate = build_qasm_gate(gate, qubits, params, "q")
qasm.append(qasm_gate)
if measurements:
qasm.extend(build_full_measurements(n_qubits, "q", "c"))
return "\n".join(qasm)
[docs]
def build_qasm_from_opcodes(opcode_list: List[OpcodeType], measure_qbit_cbit=None, qreg_name="q", creg_name="c"):
"""
Generate a QASM code from a list of opcodes.
Args:
opcode_list (List[Tuple[str, List[int], List[float]]]): a list of tuples containing the gate name, qubits, and parameters
measure_qbit_cbit (List[Tuple[int, int]]): a list of tuples containing the qubit and cbit to measure
qreg_name (str): name of the qreg to measure from
creg_name (str): name of the creg to measure to
Returns:
str: a QASM code
"""
# determine the number of qubits in the circuit
n_qubits = 0
for opcode in opcode_list:
qubits = opcode[1]
if isinstance(qubits, int):
qubits = [qubits]
n_qubits = max(n_qubits, max(qubits) + 1)
# build the QASM code
qasm = [
"OPENQASM 2.0;",
'include "qelib1.inc";',
f"qreg {qreg_name}[{n_qubits}];",
f"creg {creg_name}[{n_qubits}];",
"// auto-generated by uniqc;",
]
for opcode in opcode_list:
gate = opcode[0]
qubits = opcode[1]
cbits = opcode[2]
params = opcode[3]
dagger_flag = opcode[4]
control_qubit_set = opcode[5]
# dagger flag and control qubits are not supported yet
if dagger_flag or control_qubit_set:
raise NotImplementedError("Dagger and control qubits are not supported yet")
# build the QASM gate string
qasm_gate = build_qasm_gate(gate, qubits, params, qreg_name)
qasm.append(qasm_gate)
if measure_qbit_cbit is not None:
qbit_list, cbit_list = zip(*measure_qbit_cbit)
qasm.extend(build_measurements(zip(qbit_list, cbit_list), qreg_name, creg_name))
return "\n".join(qasm)