qiskit
qiskit copied to clipboard
Commuting2qGateGrouper pass
The pass Commuting2qGateGrouper groups the commuting multi-qubit gates into Commuting2qBlock.
from qiskit.transpiler import PassManager
from qiskit.transpiler.passes import CommutationAnalysis
from qiskit.transpiler.passes.routing import Commuting2qGateGrouper
from qiskit.circuit.library import TwoLocal
circuit = TwoLocal(5, "ry", "cz", entanglement='pairwise').decompose()
print(circuit.draw('text', fold=-1))
passmanager = PassManager()
passmanager.append([Commuting2qGateGrouper()])
result = passmanager.run(circuit)
print(result.draw('text', fold=-1))
┌──────────┐ ┌──────────┐ ┌───────────┐ ┌───────────┐
q_0: ┤ Ry(θ[0]) ├─■─┤ Ry(θ[5]) ├─────────────■─┤ Ry(θ[10]) ├──────────────■─┤ Ry(θ[15]) ├─────────────
├──────────┤ │ └──────────┘┌──────────┐ │ └───────────┘┌───────────┐ │ └───────────┘┌───────────┐
q_1: ┤ Ry(θ[1]) ├─■──────■──────┤ Ry(θ[6]) ├─■───────■──────┤ Ry(θ[11]) ├─■───────■──────┤ Ry(θ[16]) ├
├──────────┤ │ ├──────────┤ │ ├───────────┤ │ ├───────────┤
q_2: ┤ Ry(θ[2]) ├─■──────■──────┤ Ry(θ[7]) ├─■───────■──────┤ Ry(θ[12]) ├─■───────■──────┤ Ry(θ[17]) ├
├──────────┤ │ ├──────────┤ │ ├───────────┤ │ ├───────────┤
q_3: ┤ Ry(θ[3]) ├─■──────■──────┤ Ry(θ[8]) ├─■───────■──────┤ Ry(θ[13]) ├─■───────■──────┤ Ry(θ[18]) ├
├──────────┤ │ ├──────────┤ │ ├───────────┤ │ ├───────────┤
q_4: ┤ Ry(θ[4]) ├────────■──────┤ Ry(θ[9]) ├─────────■──────┤ Ry(θ[14]) ├─────────■──────┤ Ry(θ[19]) ├
└──────────┘ └──────────┘ └───────────┘ └───────────┘
┌──────────┐┌─────────────────────┐┌──────────┐┌─────────────────────┐┌───────────┐┌─────────────────────┐┌───────────┐
q_0: ┤ Ry(θ[0]) ├┤0 ├┤ Ry(θ[5]) ├┤0 ├┤ Ry(θ[10]) ├┤0 ├┤ Ry(θ[15]) ├
├──────────┤│ │├──────────┤│ │├───────────┤│ │├───────────┤
q_1: ┤ Ry(θ[1]) ├┤1 ├┤ Ry(θ[6]) ├┤1 ├┤ Ry(θ[11]) ├┤1 ├┤ Ry(θ[16]) ├
├──────────┤│ │├──────────┤│ │├───────────┤│ │├───────────┤
q_2: ┤ Ry(θ[2]) ├┤2 Commuting 2q gates ├┤ Ry(θ[7]) ├┤2 Commuting 2q gates ├┤ Ry(θ[12]) ├┤2 Commuting 2q gates ├┤ Ry(θ[17]) ├
├──────────┤│ │├──────────┤│ │├───────────┤│ │├───────────┤
q_3: ┤ Ry(θ[3]) ├┤3 ├┤ Ry(θ[8]) ├┤3 ├┤ Ry(θ[13]) ├┤3 ├┤ Ry(θ[18]) ├
├──────────┤│ │├──────────┤│ │├───────────┤│ │├───────────┤
q_4: ┤ Ry(θ[4]) ├┤4 ├┤ Ry(θ[9]) ├┤4 ├┤ Ry(θ[14]) ├┤4 ├┤ Ry(θ[19]) ├
└──────────┘└─────────────────────┘└──────────┘└─────────────────────┘└───────────┘└─────────────────────┘└───────────┘
Testing is needed
Thank you for opening a new pull request.
Before your PR can be merged it will first need to pass continuous integration tests and be reviewed. Sometimes the review process can be slow, so please be patient.
While you're waiting, please feel free to review other open PRs. While only a subset of people are authorized to approve pull requests for merging, everyone is encouraged to review open pull requests. Doing reviews helps reduce the burden on the core team and helps make the project's code better for everyone.
One or more of the the following people are requested to review this:
- @Qiskit/terra-core
Pull Request Test Coverage Report for Build 2423044318
- 76 of 77 (98.7%) changed or added relevant lines in 5 files are covered.
- No unchanged relevant lines lost coverage.
- Overall coverage increased (+0.02%) to 84.408%
| Changes Missing Coverage | Covered Lines | Changed/Added Lines | % |
|---|---|---|---|
| qiskit/transpiler/passes/routing/commuting_2q_gate_routing/commuting_2q_gate_grouper.py | 66 | 67 | 98.51% |
| <!-- | Total: | 76 | 77 |
| Totals | |
|---|---|
| Change from base Build 2409841148: | 0.02% |
| Covered Lines: | 54635 |
| Relevant Lines: | 64727 |
💛 - Coveralls
this is fine but at some point we should really do the generic things described here: https://github.com/Qiskit/qiskit-terra/issues/5775
Because all these things that we collect can be described by some condition on the gates inspected, to see whether they should be placed in the bucket or not. Here the condition is being a 2-qubit gate and commuting with the block, in #7361 the condition is being a linear gate (cx, swap, etc.), in Collect2qBlocks the condition is being a 2-qubit gate. Soon we will need to collect all cliffords. etc.
we should really do the generic things described here: #5775
I agree, at least with the general principle. However, that seems to be a bigger project than this one.
Some preliminary testing from my end. First comment is that this plays nicely with the commuting gate router. For instance:
from qiskit.transpiler import PassManager, CouplingMap, Layout
from qiskit.transpiler.passes import CommutationAnalysis
from qiskit.transpiler.passes.routing.swap_strategies import Commuting2qGateGrouper, SwapStrategy
from qiskit.transpiler.passes import FullAncillaAllocation
from qiskit.transpiler.passes import EnlargeWithAncilla
from qiskit.transpiler.passes import ApplyLayout
from qiskit.transpiler.passes import SetLayout
from qiskit.transpiler.passes import Commuting2qGateRouter
from qiskit.circuit.library import TwoLocal
circuit = TwoLocal(5, "ry", "cz", entanglement='full', reps=2).decompose()
circuit.draw('mpl', fold=-1)
swap_strat = SwapStrategy.from_line([0, 1, 2, 3, 4])
backend_cmap = CouplingMap(couplinglist=[(0, 1), (1, 2), (1, 3), (3, 4), (4, 5), (5, 6)])
initial_layout = Layout.from_intlist([0, 1, 3, 4, 5], *circuit.qregs)
passmanager = PassManager(
[
CommutationAnalysis(),
Commuting2qGateGrouper(),
Commuting2qGateRouter(swap_strat),
SetLayout(initial_layout),
FullAncillaAllocation(backend_cmap),
EnlargeWithAncilla(),
ApplyLayout(),
]
)
result = passmanager.run(circuit)
result.draw('mpl', fold=-1)
transpiles this

into this

This also looks good:
from qiskit import QuantumCircuit
from qiskit.transpiler import PassManager
from qiskit.transpiler.passes import CommutationAnalysis
from qiskit.transpiler.passes.routing.swap_strategies import Commuting2qGateGrouper
circ = QuantumCircuit(8)
circ.cz(0, 1)
circ.cz(2, 3)
circ.cz(0, 2)
circ.cz(1, 3)
circ.cz(0, 3)
circ.cz(5, 6)
circ.cz(6, 7)
circ.cz(5, 7)
print(circ.draw("text"))
passmanager = PassManager(
[
CommutationAnalysis(),
Commuting2qGateGrouper(),
]
)
print(passmanager.run(circ).draw("text"))
results in
q_0: ─■──■─────■─
│ │ │
q_1: ─■──┼──■──┼─
│ │ │
q_2: ─■──■──┼──┼─
│ │ │
q_3: ─■─────■──■─
q_4: ────────────
q_5: ─■─────■────
│ │
q_6: ─■──■──┼────
│ │
q_7: ────■──■────
┌─────────────────────┐
q_0: ┤0 ├
│ │
q_1: ┤1 ├
│ Commuting 2q gates │
q_2: ┤2 ├
│ │
q_3: ┤3 ├
└─────────────────────┘
q_4: ───────────────────────
┌─────────────────────┐
q_5: ┤0 ├
│ │
q_6: ┤1 Commuting 2q gates ├
│ │
q_7: ┤2 ├
└─────────────────────┘
The second example posted by @eggerdj highlights that this is not quite grouping the commuting 2-qubit gates (because the top gates and bottom gates also commute). Rather it is grouping 2q gates that commute and act on contiguous qubits. This should be in the docstrings.
I agree, at least with the general principle. However, that seems to be a bigger project than this one.
Only slightly bigger. But it simplifies the transpiler quite a lot and prevents a bunch of ad-hoc passes that keep creeping in. I just saw another pass today that groups 2-qubit clifford gates.