forked from Qiskit/qiskit
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add benchmarking tests (Qiskit#12603)
* added circuit functions * updated circuits functions * added qasm files * added benchmarking metrics * cleaning up circuits * updated tests * updated tests * formatting * removed unused import * clifford synthesis circ test * lint test
- Loading branch information
Showing
6 changed files
with
265,092 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
# 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) |
Oops, something went wrong.