qiskit/test/benchmarks/manipulate.py

179 lines
6.1 KiB
Python

# This code is part of Qiskit.
#
# (C) Copyright IBM 2023
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
# pylint: disable=no-member,invalid-name,missing-docstring,no-name-in-module
# pylint: disable=attribute-defined-outside-init,unsubscriptable-object
# pylint: disable=unused-wildcard-import,wildcard-import,undefined-variable
import os
import numpy as np
from qiskit import QuantumCircuit
from qiskit.converters import circuit_to_dag
from qiskit.circuit import CircuitInstruction, Qubit, library
from qiskit.dagcircuit import DAGCircuit
from qiskit.passmanager import PropertySet
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from .utils import multi_control_circuit
GATES = {
"id": library.IGate(),
"x": library.XGate(),
"y": library.YGate(),
"z": library.ZGate(),
"cx": library.CXGate(),
"cz": library.CZGate(),
}
TWIRLING_SETS_NAMES = {
"cx": [
["id", "z", "z", "z"],
["id", "x", "id", "x"],
["id", "y", "z", "y"],
["id", "id", "id", "id"],
["z", "x", "z", "x"],
["z", "y", "id", "y"],
["z", "id", "z", "id"],
["z", "z", "id", "z"],
["x", "y", "y", "z"],
["x", "id", "x", "x"],
["x", "z", "y", "y"],
["x", "x", "x", "id"],
["y", "id", "y", "x"],
["y", "z", "x", "y"],
["y", "x", "y", "id"],
["y", "y", "x", "z"],
],
"cz": [
["id", "z", "id", "z"],
["id", "x", "z", "x"],
["id", "y", "z", "y"],
["id", "id", "id", "id"],
["z", "x", "id", "x"],
["z", "y", "id", "y"],
["z", "id", "z", "id"],
["z", "z", "z", "z"],
["x", "y", "y", "x"],
["x", "id", "x", "z"],
["x", "z", "x", "id"],
["x", "x", "y", "y"],
["y", "id", "y", "z"],
["y", "z", "y", "id"],
["y", "x", "x", "y"],
["y", "y", "x", "x"],
],
}
TWIRLING_SETS = {
key: [[GATES[name] for name in twirl] for twirl in twirls]
for key, twirls in TWIRLING_SETS_NAMES.items()
}
def _dag_from_twirl(gate_2q, twirl):
dag = DAGCircuit()
# or use QuantumRegister - doesn't matter
qubits = (Qubit(), Qubit())
dag.add_qubits(qubits)
dag.apply_operation_back(twirl[0], (qubits[0],), (), check=False)
dag.apply_operation_back(twirl[1], (qubits[1],), (), check=False)
dag.apply_operation_back(gate_2q, qubits, (), check=False)
dag.apply_operation_back(twirl[2], (qubits[0],), (), check=False)
dag.apply_operation_back(twirl[3], (qubits[1],), (), check=False)
return dag
def circuit_twirl(qc, twirled_gate="cx", seed=None):
rng = np.random.default_rng(seed)
twirl_set = TWIRLING_SETS.get(twirled_gate, [])
out = qc.copy_empty_like()
for instruction in qc.data:
if instruction.operation.name != twirled_gate:
out._append(instruction)
else:
# We could also scan through `qc` outside the loop to know how many
# twirled gates we'll be dealing with, and RNG the integers ahead of
# time - that'll be faster depending on what percentage of gates are
# twirled, and how much the Numpy overhead is.
twirls = twirl_set[rng.integers(len(twirl_set))]
control, target = instruction.qubits
out._append(CircuitInstruction(twirls[0], (control,), ()))
out._append(CircuitInstruction(twirls[1], (target,), ()))
out._append(instruction)
out._append(CircuitInstruction(twirls[2], (control,), ()))
out._append(CircuitInstruction(twirls[3], (target,), ()))
return out
def dag_twirl(dag, twirled_gate="cx", seed=None):
# This mutates `dag` in place.
rng = np.random.default_rng(seed)
twirl_set = TWIRLING_DAGS.get(twirled_gate, [])
twirled_gate_op = GATES[twirled_gate].base_class
to_twirl = dag.op_nodes(twirled_gate_op)
twirl_indices = rng.integers(len(twirl_set), size=(len(to_twirl),))
for index, op_node in zip(twirl_indices, to_twirl):
dag.substitute_node_with_dag(op_node, twirl_set[index])
return dag
TWIRLING_DAGS = {
key: [_dag_from_twirl(GATES[key], twirl) for twirl in twirls]
for key, twirls in TWIRLING_SETS.items()
}
class TestCircuitManipulate:
def setup(self):
qasm_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "qasm")
self.qft_qasm = os.path.join(qasm_dir, "dtc_100_cx_12345.qasm")
self.qft_qc = QuantumCircuit.from_qasm_file(self.qft_qasm)
self.qv_qasm = os.path.join(qasm_dir, "qv_N100_12345.qasm")
self.qv_qc = QuantumCircuit.from_qasm_file(self.qv_qasm)
self.dtc_qasm = os.path.join(qasm_dir, "dtc_100_cx_12345.qasm")
self.dtc_qc = QuantumCircuit.from_qasm_file(self.dtc_qasm)
self.translate = generate_preset_pass_manager(1, basis_gates=["rx", "ry", "rz", "cz"])
def time_DTC100_twirling(self):
"""Perform Pauli-twirling on a 100Q QV
circuit
"""
out = circuit_twirl(self.dtc_qc)
return out
def time_multi_control_decompose(self):
"""Decompose a multi-control gate into the
basis [rx, ry, rz, cz]
"""
circ = multi_control_circuit(16)
self.translate.property_set = PropertySet()
out = self.translate.run(circ)
return out
def time_QV100_basis_change(self):
"""Change a QV100 circuit basis from [rx, ry, rz, cx]
to [sx, x, rz, cz]
"""
self.translate.property_set = PropertySet()
out = self.translate.run(self.qv_qc)
return out
def time_DTC100_twirling_dag(self):
"""Perform Pauli-twirling on a 100Q QV
circuit
"""
self.translate.property_set = PropertySet()
out = self.translate.run(self.qv_qc)
return circuit_to_dag(out)