Suppose we want to run the circuit
on a quantum computer with qubit connectivity
from pauliopt.topologies import Topology
n_qubits = 4
no_topology = Topology.complete(n_qubits)
topology = Topology.line(n_qubits)
from pauliopt.phase import PhaseCircuit, PhaseGadget
from pauliopt.utils import pi
g = [(0,1), (1,2), (2,3), (3,0), (0,), (1,), (2,), (3,)]
angle = pi/8
gadgets = [PhaseGadget("X", angle, qubits) for qubits in g] \
+ [PhaseGadget("Z", angle, qubits) for qubits in g]
example = PhaseCircuit(n_qubits, gadgets)
draw_circuit(example, no_topology)
Number of CNOTs: 16
We can write this circuit as phase gadgets in ZX
Which we can describe using binary matrices (Mixed ZX-phase polynomial [1])
[1] https://www.qplconference.org/proceedings2022/QPL_2022_paper_133.pdf
Originally only naive synthesis. Now with the addition of Steiner-GraySynth [2], ParitySynth [3], and PermRowCol [4].
[2] https://arxiv.org/pdf/2004.06052.pdf (QPL2020)
[3] Vandaele, V., Martiel, S., & de Brugière, T. G. (2022). Phase polynomials synthesis algorithms for NISQ architectures and beyond. Quantum Science and Technology, 7(4), 045027.
[4] https://www.qplconference.org/proceedings2022/QPL_2022_paper_99.pdf
draw_circuit(example, topology, method="naive")
Number of CNOTs: 32
draw_circuit(example, topology, method="paritysynth", \
cx_synth="naive")
Number of CNOTs: 32
draw_circuit(example, topology, method="steiner-graysynth", \
cx_synth="naive")
Number of CNOTs: 32
draw_circuit(example, topology, method="steiner-graysynth", \
cx_synth="permrowcol", reallocate=True)
Number of CNOTs: 21
Find CNOTs to add the the circuit before synthesis to simplify synthesis
from pauliopt.phase import OptimizedPhaseCircuit
n_iter, cx_block = 1000, 3
opt = OptimizedPhaseCircuit(example.copy(), topology, cx_block,\
phase_method="paritysynth", cx_method="permrowcol",reallocate=True)
opt.anneal(n_iter)
draw_circuit(opt)
Number of CNOTs: 23
opt = OptimizedPhaseCircuit(example, topology, cx_block,\
phase_method="steiner-graysynth", cx_method="permrowcol",reallocate=True)
opt.anneal(n_iter)
draw_circuit(opt)
Number of CNOTs: 18
Iteratively improve both intial and output qubit mapping
Picture from [5].
[5] Li, G., Ding, Y., & Xie, Y. (2019, April). Tackling the qubit mapping problem for NISQ-era quantum devices. In Proceedings of the Twenty-Fourth International Conference on Architectural Support for Programming Languages and Operating Systems (pp. 1001-1014).
from pauliopt.phase import reverse_traversal
rt_iter = 10
opt_kwargs = {
"phase_method": "paritysynth",
"cx_method": "permrowcol",
"reallocate": True
}
opt = reverse_traversal(example, topology, cx_block,\
rt_iter, 0, opt_kwargs)
print("Qubit numbering:", opt._qubit_mapping)
draw_circuit(opt)
Qubit numbering: range(0, 4) Number of CNOTs: 24
opt_kwargs["phase_method"] = "steiner-graysynth"
opt = reverse_traversal(example, topology, cx_block,\
rt_iter, 0, opt_kwargs)
print("Qubit numbering:", opt._qubit_mapping)
draw_circuit(opt)
Qubit numbering: range(0, 4) Number of CNOTs: 21
opt = reverse_traversal(example, topology, cx_block,\
rt_iter, n_iter, opt_kwargs)
print("Qubit numbering:", opt._qubit_mapping)
draw_circuit(opt)
Qubit numbering: range(0, 4) Number of CNOTs: 18
opt = reverse_traversal_anneal(example, topology, cx_block,\
rt_iter, n_iter, opt_kwargs)
print("Qubit numbering:", opt._qubit_mapping)
draw_circuit(opt)
Qubit numbering: [3, 0, 1, 2] Number of CNOTs: 16
Code is open source and available on
Please use, improve, and leave feedback.
Paper: https://arxiv.org/pdf/2304.08814.pdf Note that the results in v1 of the paper are incorrect due to a bug that has since been fixed.