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

[WIP] Pulse-efficient Transpiler Passes #6653

Open
wants to merge 78 commits into
base: main
Choose a base branch
from

Conversation

catornow
Copy link
Contributor

@catornow catornow commented Jun 28, 2021

Summary

This PR implements a new pass manager called pulse-efficient that allows users to transpile circuits to RZX gate-based and pulse-efficient circuits. The circuits typically have shorter pulse schedule durations and higher fidelities in comparison to the standard digital CNOT-based transpired circuits (see Reference https://arxiv.org/pdf/2105.01063.pdf).
First, all consecutive two-qubit operations in a circuit are consolidated. The main component of the pass manager is a new transpiler pass EchoRZXWeylDecomposition. This transpiler pass leverages Cartan's decomposition of arbitrary two-quit gates and decomposes the two-quit gates in terms of echoed RZX gates.
Lastly, calibrations are added to the RZX gates by leveraging the RZXCalibrationBuilderNoEcho, see PR #6300. Most importantly, no additional calibration is needed.

Details and comments

The output of this self-contained code should illustrate the transpilation of a randomly chosen circuit to an RZX gate-based, pulse-efficient circuit.

from qiskit import QuantumCircuit, transpile, schedule, IBMQ

import numpy as np

IBMQ.load_account()
provider = IBMQ.get_provider(...)

backend = provider.get_backend('ibmq_paris')

# random circuit

delta = 8 * np.pi / 5
epsilon = np.pi / 2
eta = -5.1
theta = 0.02

qc = QuantumCircuit(3)
qc.ryy(theta, 0, 1)
qc.s(0)
qc.rz(eta, 1)
qc.rzz(epsilon, 0, 1)
qc.swap(0, 1)
qc.cx(1, 0)
qc.rzz(delta, 1, 2)
qc.swap(1, 2)
qc.h(2)

print('Original circuit:')
print(qc)

qct = transpile(qc, backend)

print('Transpiled circuit:')
print(qct)

# new optimization level
qc_pulse_efficient = transpile(qc, backend, optimization_level='pulse_efficient')

print('Pulse-efficient, RZX-based circuit:')
print(qc_pulse_efficient)

# Compare the schedule durations
print('Duration of standard CNOT-based circuit:')
print(schedule(qct, backend).duration)
print('Duration of pulse-efficient circuit:')
print(schedule(qc_pulse_efficient, backend).duration)

Output (I removed the ancilla qubits for better readability):

Original circuit:
     ┌────────────┐   ┌───┐                 ┌───┐                   
q_0: ┤0           ├───┤ S ├─────■─────────X─┤ X ├───────────────────
     │  RYY(0.02) │┌──┴───┴───┐ │ZZ(π/2)  │ └─┬─┘                   
q_1: ┤1           ├┤ RZ(-5.1) ├─■─────────X───■───■──────────X──────
     └────────────┘└──────────┘                   │ZZ(8π/5)  │ ┌───┐
q_2: ─────────────────────────────────────────────■──────────X─┤ H ├
                                                               └───┘
Transpiled circuit:
global phase: π/2
                 ┌────┐                                    ┌────────┐┌────┐»
        q_0 -> 0 ┤ √X ├──■──────────────────────────────■──┤ RZ(-π) ├┤ √X ├»
                 ├────┤┌─┴─┐┌────────────────────────┐┌─┴─┐├────────┤├────┤»
        q_1 -> 1 ┤ √X ├┤ X ├┤ RZ(0.0200000000000000) ├┤ X ├┤ RZ(-π) ├┤ √X ├»
                 └────┘└───┘└────────────────────────┘└───┘└────────┘└────┘»
        q_2 -> 2 ──────────────────────────────────────────────────────────»
                                                                           »

                                                                           »
«                   ┌──────────┐                 ┌───┐     ┌───┐     »
«        q_0 -> 0 ──┤ RZ(-π/2) ├───■─────────────┤ X ├──■──┤ X ├─────»
«                 ┌─┴──────────┴┐┌─┴─┐┌─────────┐└─┬─┘┌─┴─┐└─┬─┘     »
«        q_1 -> 1 ┤ RZ(-1.9584) ├┤ X ├┤ RZ(π/2) ├──■──┤ X ├──■────■──»
«                 └─────────────┘└───┘└─────────┘     └───┘     ┌─┴─┐»
«        q_2 -> 2 ──────────────────────────────────────────────┤ X ├»
«                                                               └───┘»

Pulse-efficient, RZX-based circuit:
global phase: 7π/4
      ┌──────────┐ ┌────┐┌──────────┐┌────────────┐┌───┐┌───────────┐»
q_0: ─┤ RZ(-π/2) ├─┤ √X ├┤ RZ(-π/2) ├┤0           ├┤ X ├┤0          ├»
     ┌┴──────────┴┐├────┤├──────────┤│  RZX(-π/4) │└───┘│  RZX(π/4) │»
q_1: ┤ RZ(2.7541) ├┤ √X ├┤ RZ(-π/2) ├┤1           ├─────┤1          ├»
     └─┬────────┬─┘├────┤├─────────┬┘└────────────┘     └───────────┘»
q_2: ──┤ RZ(-π) ├──┤ √X ├┤ RZ(π/2) ├─────────────────────────────────»
       └────────┘  └────┘└─────────┘                                 »
«        ┌────┐   ┌────────────────┐┌───┐┌───────────────┐  ┌───┐   ┌─────────┐»
«q_0: ───┤ √X ├───┤0               ├┤ X ├┤0              ├──┤ X ├───┤ RZ(π/2) ├»
«     ┌──┴────┴──┐│  RZX(-0.78162) │└───┘│  RZX(0.78162) │┌─┴───┴──┐└──┬────┬─┘»
«q_1: ┤ RZ(-π/2) ├┤1               ├─────┤1              ├┤ RZ(-π) ├───┤ √X ├──»
«     └──────────┘└────────────────┘     └───────────────┘└────────┘   └────┘  »
«q_2: ─────────────────────────────────────────────────────────────────────────»
«                                                                              »
«                                                                    »
«q_0: ───────────────────────────────────────────────────────────────»
«     ┌─────────────┐┌────┐┌────────┐┌────────────┐     ┌───────────┐»
«q_1: ┤ RZ(-3.1231) ├┤ √X ├┤ RZ(-π) ├┤1           ├─────┤1          ├»
«     └─────────────┘└────┘└────────┘│  RZX(-π/4) │┌───┐│  RZX(π/4) │»
«q_2: ───────────────────────────────┤0           ├┤ X ├┤0          ├»
«                                    └────────────┘└───┘└───────────┘»
«                                                                             »
«q_0: ────────────────────────────────────────────────────────────────────────»
«     ┌──────────┐┌────────────┐     ┌───────────┐ ┌────────┐┌────┐┌─────────┐»
«q_1: ┤ RZ(-π/2) ├┤1           ├─────┤1          ├─┤ RZ(-π) ├┤ √X ├┤ RZ(π/2) ├»
«     └──┬────┬──┘│  RZX(-π/4) │┌───┐│  RZX(π/4) │┌┴────────┤├────┤└─────────┘»
«q_2: ───┤ √X ├───┤0           ├┤ X ├┤0          ├┤ RZ(π/2) ├┤ √X ├───────────»
«        └────┘   └────────────┘└───┘└───────────┘└─────────┘└────┘           »
«                                                                   
«q_0: ──────────────────────────────────────────────────────────────
«     ┌─────────────┐     ┌────────────┐┌─────────┐┌────┐           
«q_1: ┤1            ├─────┤1           ├┤ RZ(π/2) ├┤ √X ├───────────
«     │  RZX(-π/20) │┌───┐│  RZX(π/20) │└┬────────┤├────┤┌─────────┐
«q_2: ┤0            ├┤ X ├┤0           ├─┤ RZ(-π) ├┤ √X ├┤ RZ(π/2) ├
«     └─────────────┘└───┘└────────────┘ └────────┘└────┘└─────────┘
Duration of standard CNOT-based circuit:
18496
Duration of pulse-efficient circuit:
9568

catornow and others added 30 commits April 26, 2021 16:06
Implemented Daniel's suggestions

Co-authored-by: Daniel Egger <[email protected]>
…the RZZ and the SWAP Gate to the StandardEquivalenceLibrary
catornow and others added 20 commits June 27, 2021 12:01
@catornow catornow requested review from chriseclectic and a team as code owners June 28, 2021 12:12
@@ -58,7 +59,8 @@ def transpile(
dt: Optional[float] = None,
approximation_degree: Optional[float] = None,
seed_transpiler: Optional[int] = None,
optimization_level: Optional[int] = None,
inst_map: Dict[str, Dict[Tuple[int], Schedule]] = None,
optimization_level: Union[Optional[int], Optional[str]] = None,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the algorithms, that create circuits and execute them, this is presently done via a QuantumInstance that wraps a backend and has common circuit support for the algorithms, that also allows an end user to set circuit aspects such as transpiler options. See here https://github.com/Qiskit/qiskit-terra/blob/3bb72d340fcf9552d4a6bd54b675399578c726db/qiskit/utils/quantum_instance.py#L71
So if this is introduced as a string I think the above should follow suit and be changed likewise. As the code just stores and passes this on I think its a minor change there just for the typehint.

As a very minor comment I think the typehint can be written more simply as Optional[Union[int, str]] = None

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually think a better interface for this is the FullPassManager being introduced in: #6403 with for the extra passes the user just builds a post-optimization pass manager that runs these additional passes and sets that on level 3. I don't think we want to use optimization level like this.

@ajavadia ajavadia self-assigned this Jul 2, 2021
Copy link
Contributor

@eggerdj eggerdj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@catornow Lets align this with #6403. With the changes suggested below you probably don't need to make any changes to transpile.py and passmanager_config.py.

@mtreinish Do you think it makes sense to add methods to preset_passmanagers/pulse_efficient.py to return instance of FullPassManager with the different optimization levels? This would save users some work from having to assemble these themselves.

from qiskit.transpiler.preset_passmanagers.level0 import level_0_pass_manager


def pulse_efficient_pass_manager(pass_manager_config: PassManagerConfig) -> PassManager:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def pulse_efficient_pass_manager(pass_manager_config: PassManagerConfig) -> PassManager:
def generate_pulse_efficient_post_opt(backend) -> PassManager:

Comment on lines +59 to +60
inst_map = pass_manager_config.inst_map
basis_gates = pass_manager_config.basis_gates
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could look something like

Suggested change
inst_map = pass_manager_config.inst_map
basis_gates = pass_manager_config.basis_gates
inst_map = backend.defaults().instruction_schedule_map
basis_gates = backend.basis_gates

Comment on lines +84 to +85
# pm = PassManager()
pm = level_3_pass_manager(pass_manager_config)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# pm = PassManager()
pm = level_3_pass_manager(pass_manager_config)
pm = PassManager()

@jakelishman
Copy link
Member

There's been no activity on this PR for several years now, but the synthesis code in it still looks potentially useful. If so, this probably ought to be re-targetted to become a UnitarySynthesis plugin.

@ShellyGarion ShellyGarion added the mod: pulse Related to the Pulse module label Jan 13, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
mod: pulse Related to the Pulse module
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants