"""
This file is used to convert the opcode to various quantum code formats.
"""
from typing import List, Optional, Tuple, Union
from .translate_qasm2_oir import OriginIR_QASM2_dict, get_QASM2_from_opcode, decompose_mcu_qasm_text
__all__ = [
"make_header_originir",
"make_header_qasm",
"make_measure_originir",
"make_measure_qasm",
"opcode_to_line_originir",
"opcode_to_line_qasm",
"OpcodeType",
"QubitType",
"CbitType",
"ParameterType",
]
QubitType = Union[List[int], int]
CbitType = Union[List[int], int]
ParameterType = Optional[Union[List[float], float]]
OpcodeType = Tuple[str, QubitType, CbitType, ParameterType, set, bool]
[docs]
def opcode_to_line_originir(opcode: OpcodeType) -> str:
"""
Convert the given opcode to OriginIR line format.
``opcode`` is a tuple with layout
``(operation, qubit, cbit, parameter, dagger_flag, control_qubits_set)``:
- ``operation`` (``str``): name of the operation.
- ``qubit`` (``QubitType``): qubit(s) the operation is applied to.
- ``cbit`` (``CbitType``): classical bit(s) the result is stored in.
- ``parameter`` (``ParameterType``): parameter(s) of the operation.
- ``dagger_flag`` (``bool``): whether the operation is daggered.
- ``control_qubits_set`` (``set``): set of control qubits.
Type aliases used above::
QubitType = Union[List[int], int]
CbitType = Union[List[int], int]
ParameterType = Optional[Union[List[float], float]]
OpcodeType = Tuple[str, QubitType, CbitType, ParameterType, set, bool]
Args:
opcode (OpcodeType): The given opcode to be converted.
Returns:
str: The converted OriginIR line format.
"""
(operation, qubit, cbit, parameter, dagger_flag, control_qubits_set) = opcode
# operation qubits (,parameter?) (,cbits?) (dagger?) (control?)
if not operation:
raise RuntimeError("Unexpected error. Operation is empty.")
ret = ""
ret += operation
if isinstance(qubit, list):
ret += " "
ret += ", ".join([f"q[{q}]" for q in qubit])
else:
ret += f" q[{qubit}]"
if parameter is not None and (not hasattr(parameter, "__len__") or len(parameter) > 0):
ret += ", ("
if hasattr(parameter, "__iter__") and not isinstance(parameter, str):
ret += ", ".join([str(p) for p in parameter])
else:
ret += str(parameter)
ret += ")"
if cbit:
ret += ", "
ret += f"c[{cbit}]" if cbit else ""
if dagger_flag:
ret += " dagger"
if control_qubits_set:
ret += " controlled_by ("
ret += ", ".join([f"q[{q}]" for q in control_qubits_set])
ret += ")"
# print(ret)
return ret
[docs]
def make_measure_originir(measure_list: List[int]):
"""
Generate the measure statement of OriginIR code for the given measure list.
Args:
measure_list (List[int]): The list of qubits to be measured.
Returns:
str: The measure statement of OriginIR code.
"""
ret = ""
for i, meas_qubit in enumerate(measure_list):
ret += "MEASURE q[{}], c[{}]\n".format(meas_qubit, i)
return ret
[docs]
def opcode_to_line_qasm(opcode: OpcodeType, qubit_num: Optional[int] = None) -> str:
"""
Convert the given opcode to QASM line format.
For gates with ≥ 4 control qubits, a multi-line decomposition is returned
(Toffoli-ladder for MCX; conjugation or ABC decomposition for other gates).
The *qubit_num* argument must be supplied in that case so workspace qubits
can be located; otherwise a NotImplementedError is raised.
Args:
opcode (OpcodeType): The given opcode to be converted.
qubit_num (Optional[int]): Total number of qubits in the circuit (needed
for multi-controlled gate decomposition with ≥ 4 controls).
Returns:
str: The converted QASM line format (potentially multi-line for
multi-controlled gate decompositions).
"""
operation, qubit, cbit, parameter = get_QASM2_from_opcode(opcode)
# Sentinel returned by get_QASM2_from_opcode for n≥4 control gates.
if operation == "_MCU_DECOMP_":
controls_list, target, gate_qasm, params, _ = qubit # type: ignore[misc]
if qubit_num is None:
raise NotImplementedError(
f"Multi-controlled {gate_qasm} with ≥4 controls cannot be "
"decomposed without knowing the circuit's qubit count. "
"Pass qubit_num to opcode_to_line_qasm."
)
return decompose_mcu_qasm_text(controls_list, target, qubit_num, gate_qasm, params)
# operation qubits (,parameter?) (,cbits?) (dagger?) (control?)
if not operation:
raise RuntimeError("Unexpected error. Operation is empty.")
ret = ""
ret += operation
if parameter is not None:
if isinstance(parameter, list):
parameter_str = ", ".join([str(p) for p in parameter])
else:
parameter_str = str(parameter)
ret += f"({parameter_str})"
if isinstance(qubit, list):
ret += " "
ret += ", ".join([f"q[{q}]" for q in qubit])
else:
ret += f" q[{qubit}]"
if cbit:
raise NotImplementedError("uniqc does not support cbit in QASM code.")
ret += ";"
return ret
[docs]
def make_measure_qasm(measure_list: List[int]) -> str:
"""
Generate the measure statement of QASM code for the given measure list.
Args:
measure_list (List[int]): The list of qubits to be measured.
Returns:
str: The measure statement of QASM code.
"""
ret = ""
for i, meas_qubit in enumerate(measure_list):
ret += "measure q[{}] -> c[{}];\n".format(meas_qubit, i)
return ret