Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify logic by using properties of input DAG. #48

Merged
merged 2 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 89 additions & 41 deletions benchmarking/run_benchmarks.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.

if __name__ == '__main__':
if __name__ == "__main__":
import sys
sys.path.append('..')

sys.path.append("..")

from qiskit import transpile
from qiskit.circuit import QuantumCircuit
Expand All @@ -29,43 +30,59 @@
pass_manager = PassManager(ZXPass())


def _benchmark(subdir: str, circuit_name: str, as_plugin: bool = False) -> Tuple[float, float, float]:
def _benchmark(
subdir: str, circuit_name: str, as_plugin: bool = False
) -> Tuple[float, float, float]:
print(f"Circuit name: {circuit_name}")

qc = QuantumCircuit.from_qasm_file(f"QASMBench/{subdir}/{circuit_name}/{circuit_name}.qasm")
opt_qc = transpile(qc, basis_gates=['u3', 'cx'], optimization_level=3)
qc = QuantumCircuit.from_qasm_file(
f"QASMBench/{subdir}/{circuit_name}/{circuit_name}.qasm"
)
opt_qc = transpile(qc, basis_gates=["u3", "cx"], optimization_level=3)
if as_plugin:
zx_qc = transpile(qc, optimization_method="zxpass", optimization_level=3)
else:
zx_qc = pass_manager.run(qc)

print(f"Size - original: {qc.size()}, "
f"optimized: {opt_qc.size()} ({qc.size() / opt_qc.size():.2f}), "
f"zx: {zx_qc.size()} ({qc.size() / zx_qc.size():.2f})")
print(f"Depth - original: {qc.depth()}, "
f"optimized: {opt_qc.depth()} ({qc.depth() / opt_qc.depth():.2f}), "
f"zx: {zx_qc.depth()} ({qc.depth() / zx_qc.depth():.2f})")
print(
f"Size - original: {qc.size()}, "
f"optimized: {opt_qc.size()} ({qc.size() / opt_qc.size():.2f}), "
f"zx: {zx_qc.size()} ({qc.size() / zx_qc.size():.2f})"
)
print(
f"Depth - original: {qc.depth()}, "
f"optimized: {opt_qc.depth()} ({qc.depth() / opt_qc.depth():.2f}), "
f"zx: {zx_qc.depth()} ({qc.depth() / zx_qc.depth():.2f})"
)
print(f"Number of non-local gates - original: {qc.num_nonlocal_gates()}, ", end="")
if qc.num_nonlocal_gates() != 0:
print(f"optimized: {opt_qc.num_nonlocal_gates()}, zx: {zx_qc.num_nonlocal_gates()}, "
f"ratio: {opt_qc.num_nonlocal_gates() / zx_qc.num_nonlocal_gates():.2f}")
print(
f"optimized: {opt_qc.num_nonlocal_gates()}, zx: {zx_qc.num_nonlocal_gates()}, "
f"ratio: {opt_qc.num_nonlocal_gates() / zx_qc.num_nonlocal_gates():.2f}"
)
else:
print("optimized: 0, zx: 0")
print()

return (qc.depth() / opt_qc.depth(),
qc.depth() / zx_qc.depth(),
qc.num_nonlocal_gates() / zx_qc.num_nonlocal_gates() if zx_qc.num_nonlocal_gates() != 0 else 0)
return (
qc.depth() / opt_qc.depth(),
qc.depth() / zx_qc.depth(),
qc.num_nonlocal_gates() / zx_qc.num_nonlocal_gates()
if zx_qc.num_nonlocal_gates() != 0
else 0,
)


def _save_plot(title: str, plot_index: List[str], data: Dict[str, List[float]], ylabel: str) -> None:
def _save_plot(
title: str, plot_index: List[str], data: Dict[str, List[float]], ylabel: str
) -> None:
width = 0.35
fig, ax = plt.subplots()
ax.set_title(title)
ax.set_ylabel(ylabel)
x = range(len(plot_index))
ax.bar(x, data['qiskit'], width, label='qiskit')
ax.bar([i + width for i in x], data['pyzx'], width, label='pyzx')
ax.bar(x, data["qiskit"], width, label="qiskit")
ax.bar([i + width for i in x], data["pyzx"], width, label="pyzx")
ax.set_xticks([i + width / 2 for i in x])
ax.set_xticklabels(plot_index, rotation=90)
ax.legend()
Expand All @@ -75,34 +92,65 @@ def _save_plot(title: str, plot_index: List[str], data: Dict[str, List[float]],

def run_benchmarks() -> None:
# List of circuits to benchmark, based on: https://github.com/Qiskit/qiskit/issues/4990#issuecomment-1157858632
small_benchmarks = ['wstate_n3', 'linearsolver_n3', 'fredkin_n3', 'dnn_n2', 'qrng_n4', 'adder_n4', 'deutsch_n2',
'cat_state_n4', 'basis_trotter_n4', 'qec_en_n5', 'toffoli_n3', 'grover_n2', 'hs4_n4', 'qaoa_n3',
'teleportation_n3', 'lpn_n5', 'vqe_uccsd_n4', 'quantumwalks_n2', 'variational_n4', 'qft_n4',
'iswap_n2', 'bell_n4', 'basis_change_n3', 'vqe_uccsd_n6', 'ising_n10', 'simon_n6', 'qpe_n9',
'qaoa_n6', 'bb84_n8', 'vqe_uccsd_n8', 'adder_n10', 'dnn_n8']
medium_benchmarks = ['bv_n14', 'multiplier_n15', 'sat_n11', 'qft_n18']

depth_ratio: Dict[str, List[float]] = {'qiskit': [], 'pyzx': []}
num_nonlocal_ratio: Dict[str, List[float]] = {'qiskit': [], 'pyzx': []}
small_benchmarks = [
"wstate_n3",
"linearsolver_n3",
"fredkin_n3",
"dnn_n2",
"qrng_n4",
"adder_n4",
"deutsch_n2",
"cat_state_n4",
"basis_trotter_n4",
"qec_en_n5",
"toffoli_n3",
"grover_n2",
"hs4_n4",
"qaoa_n3",
"teleportation_n3",
"lpn_n5",
"vqe_uccsd_n4",
"quantumwalks_n2",
"variational_n4",
"qft_n4",
"iswap_n2",
"bell_n4",
"basis_change_n3",
"vqe_uccsd_n6",
"ising_n10",
"simon_n6",
"qpe_n9",
"qaoa_n6",
"bb84_n8",
"vqe_uccsd_n8",
"adder_n10",
"dnn_n8",
]
medium_benchmarks = ["bv_n14", "multiplier_n15", "sat_n11", "qft_n18"]

depth_ratio: Dict[str, List[float]] = {"qiskit": [], "pyzx": []}
num_nonlocal_ratio: Dict[str, List[float]] = {"qiskit": [], "pyzx": []}
plot_index = []
for benchmark in small_benchmarks:
qiskit_depth, zx_depth, non_local_ratio = _benchmark('small', benchmark)
depth_ratio['pyzx'].append(zx_depth)
depth_ratio['qiskit'].append(qiskit_depth)
num_nonlocal_ratio['pyzx'].append(non_local_ratio)
num_nonlocal_ratio['qiskit'].append(1 if non_local_ratio != 0 else 0)
qiskit_depth, zx_depth, non_local_ratio = _benchmark("small", benchmark)
depth_ratio["pyzx"].append(zx_depth)
depth_ratio["qiskit"].append(qiskit_depth)
num_nonlocal_ratio["pyzx"].append(non_local_ratio)
num_nonlocal_ratio["qiskit"].append(1 if non_local_ratio != 0 else 0)
plot_index.append(benchmark)
for benchmark in medium_benchmarks:
qiskit_depth, zx_depth, non_local_ratio = _benchmark('medium', benchmark)
depth_ratio['pyzx'].append(zx_depth)
depth_ratio['qiskit'].append(qiskit_depth)
num_nonlocal_ratio['pyzx'].append(non_local_ratio)
num_nonlocal_ratio['qiskit'].append(1 if non_local_ratio != 0 else 0)
qiskit_depth, zx_depth, non_local_ratio = _benchmark("medium", benchmark)
depth_ratio["pyzx"].append(zx_depth)
depth_ratio["qiskit"].append(qiskit_depth)
num_nonlocal_ratio["pyzx"].append(non_local_ratio)
num_nonlocal_ratio["qiskit"].append(1 if non_local_ratio != 0 else 0)
plot_index.append(benchmark)

_save_plot('Depth compression ratio', plot_index, depth_ratio, 'depth_ratio')
_save_plot('Ratio of non-local gates', plot_index, num_nonlocal_ratio, 'num_nonlocal_ratio')
_save_plot("Depth compression ratio", plot_index, depth_ratio, "depth_ratio")
_save_plot(
"Ratio of non-local gates", plot_index, num_nonlocal_ratio, "num_nonlocal_ratio"
)


if __name__ == '__main__':
if __name__ == "__main__":
run_benchmarks()
44 changes: 20 additions & 24 deletions test/test_zxpass.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@
from zxpass import ZXPass


def _run_zxpass(qc: QuantumCircuit, optimize: Optional[Callable[[zx.Circuit], zx.Circuit]] = None) -> bool:
def _run_zxpass(
qc: QuantumCircuit, optimize: Optional[Callable[[zx.Circuit], zx.Circuit]] = None
) -> bool:
zxpass = ZXPass(optimize)
pass_manager = PassManager(zxpass)
zx_qc = pass_manager.run(qc)
Expand Down Expand Up @@ -62,8 +64,7 @@ def test_basic_circuit() -> None:


def test_custom_optimize() -> None:
"""Test custom optimize method.
"""
"""Test custom optimize method."""
qc = QuantumCircuit(4)
qc.h(0)
qc.h(1)
Expand All @@ -82,40 +83,36 @@ def optimize(circ: zx.Circuit) -> zx.Circuit:


def test_measurement() -> None:
"""Test a circuit with a measurement.
"""
q = QuantumRegister(1, 'q')
c = ClassicalRegister(1, 'c')
"""Test a circuit with a measurement."""
q = QuantumRegister(1, "q")
c = ClassicalRegister(1, "c")
qc = QuantumCircuit(q, c)
qc.h(q[0])
qc.measure(q[0], c[0])
qc.h(q[0])

dag = qiskit.converters.circuit_to_dag(qc)
zxpass = ZXPass()
circuits_and_nodes = zxpass._dag_to_circuits_and_nodes(dag) # pylint: disable=protected-access
circuits_and_nodes = zxpass._dag_to_circuits_and_nodes( # pylint: disable=protected-access
dag
)
assert len(circuits_and_nodes) == 3
assert circuits_and_nodes[1] == dag.op_nodes()[1]


def test_conditional_gate() -> None:
"""Test a circuit with a conditional gate.
"""
q = QuantumRegister(1, 'q')
c = ClassicalRegister(1, 'c')
"""Test a circuit with a conditional gate."""
q = QuantumRegister(1, "q")
c = ClassicalRegister(1, "c")
qc = QuantumCircuit(q, c)
qc.h(q[0]).c_if(c, 0)

assert _run_zxpass(qc)


def test_unitary() -> None:
"""Test a circuit with a unitary gate.
"""
matrix = [[0, 0, 0, 1],
[0, 0, 1, 0],
[1, 0, 0, 0],
[0, 1, 0, 0]]
"""Test a circuit with a unitary gate."""
matrix = [[0, 0, 0, 1], [0, 0, 1, 0], [1, 0, 0, 0], [0, 1, 0, 0]]
qc = QuantumCircuit(2)
qc.unitary(matrix, [0, 1])

Expand All @@ -140,17 +137,16 @@ def test_pyzx_issue_102() -> None:
qc.ccz(2, 1, 0)
qc.s(1)
qc.ccx(2, 1, 0)
qc.crz(0.2*np.pi, 0, 1)
qc.rz(0.8*np.pi, 1)
qc.cry(0.4*np.pi, 2, 1)
qc.crx(0.02*np.pi, 2, 0)
qc.crz(0.2 * np.pi, 0, 1)
qc.rz(0.8 * np.pi, 1)
qc.cry(0.4 * np.pi, 2, 1)
qc.crx(0.02 * np.pi, 2, 0)

assert _run_zxpass(qc)


def test_random_circuits() -> None:
"""Test random circuits.
"""
"""Test random circuits."""
for _ in range(20):
num_qubits = np.random.randint(4, 9)
depth = np.random.randint(10, 21)
Expand Down
4 changes: 3 additions & 1 deletion zxpass/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ class ZXPlugin(PassManagerStagePlugin): # pylint: disable=too-few-public-method
"""Plugin class for optimization stage with :class:`~.ZXPass`."""

def pass_manager(
self, pass_manager_config: PassManagerConfig, optimization_level: Optional[int] = None
self,
pass_manager_config: PassManagerConfig,
optimization_level: Optional[int] = None,
) -> PassManager:
return PassManager([ZXPass()])
Loading
Loading