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

Labeled gate pulse gate #7130

Closed

Conversation

nkanazawa1989
Copy link
Contributor

@nkanazawa1989 nkanazawa1989 commented Oct 13, 2021

Summary

This PR enables to identify custom gate differentiated by the instruction label. This will be supported by #5885 in more sophisticated way. Meanwhile, a new pass LabelIdentifier (I don't like the name though) pass supports the pulse gate definition for the labeled gates.

(motivation)

This is requirements from Qiskit Experiments to support interleaved RB experiments with custom gate. Interleaved element should be defined as a gate with one of predefined names to be converted into Clifford instance (because RB cannot handle gates that don't belong to the Clifford group). To meet this requirements, we need to distinguish the interleaved gate from the standard gate with instruction labels. e.g. When we measure a CX gate that we calibrate, we want to interleave that gate. To perform IRB, we need to construct reference sequence as well, that may include another CX gate. We usually want to use backend-calibrated gate as a reference in this situation. So somehow we need to distinguish them.

(sample code)

Add labeled custom gate to instmap.

from qiskit import circuit, pulse, transpile
from qiskit.test.mock import FakeCasablanca

backend = FakeCasablanca()
instmap = backend.defaults().instruction_schedule_map

my_cx = circuit.library.CXGate(label="custom")

fake_cx_sched = pulse.ScheduleBlock()
fake_cx_sched.append(pulse.Play(pulse.Constant(1600, 0.1), pulse.ControlChannel(0)))

instmap.add(my_cx, (0, 1), fake_cx_sched)

This gate is registered with the unique name.

>>> instmap.instructions  
['cx', 'id', 'measure', 'rz', 'sx', 'u1', 'u2', 'u3', 'x', 'cx_custom']

Then, create circuit with custom gate.

qc = circuit.QuantumCircuit(2)
qc.cx(0,1)
qc.append(my_cx, [0, 1])
>>> qc.draw()
           custom 
q_0: ──■─────■────
     ┌─┴─┐ ┌─┴─┐  
q_1: ┤ X ├─┤ X ├──
     └───┘ └───┘  

The custom gate is transpiled as standard CX gate (if I set opt level 1 these gates are cancelled out).

qc_transpiled = transpile(qc, backend=backend, optimization_level=0)

The output circuit looks like the same, however, its name is replaced by cx_custom with the corresponding pulse gate extracted from the backend. This program can be actually executed on IBM backends that support pulse gate.

>>> qc_transpiled.draw()
                     custom 
      q_0 -> 0 ──■─────■────
               ┌─┴─┐ ┌─┴─┐  
      q_1 -> 1 ┤ X ├─┤ X ├──
               └───┘ └───┘  
ancilla_0 -> 2 ─────────────
                            
ancilla_1 -> 3 ─────────────
                            
ancilla_2 -> 4 ─────────────
                            
ancilla_3 -> 5 ─────────────
                            
ancilla_4 -> 6 ─────────────

>>> qc_transpiled.calibrations        
{
    'cx_custom': {
        ((0, 1), ()): ScheduleBlock(Play(Constant(duration=1600, amp=(0.1+0j)), ControlChannel(0)), name="block0", transform=AlignLeft())
    }
}

Details and comments

TODO

  • unittest
  • reno

for node in dag.topological_op_nodes():
label = getattr(node.op, "label", None)
if label:
node.name = f"{node.name}_{label}"
Copy link
Member

Choose a reason for hiding this comment

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

This behavior seems specific to a particular use-case and likely not something users would want in general. e.g. This would replace all gates with a label to {name}_{label} whenever using pulse gates, even if {name}_{label} is outside the basis_gates, so I'd prefer there be an easier way for users to opt-in to this behavior when needed.

Copy link
Contributor Author

@nkanazawa1989 nkanazawa1989 Oct 13, 2021

Choose a reason for hiding this comment

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

That's good point. Other option would be initializing this pass with instmap and let the pass check the map if the {name}_{label} exists for the qargs (EDIT: to make sure it can be replaced by pulse gate afterward, i.e. this pass is inserted only when pulse gate pass is active). I think #6403 is good for this usecase, but seems like it will take a bit more time to merge in.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in 1d2d540 This checks instmap before changing gate name. For example,

my_cx1 = circuit.library.CXGate(label="custom")
my_cx2 = circuit.library.CXGate(label="dummy")

instmap.add(my_cx1, (0, 1), fake_cx_sched)

qc = circuit.QuantumCircuit(2)
qc.cx(0,1)
qc.append(my_cx1, [0, 1])
qc.append(my_cx2, [0, 1])

qc_transpiled = transpile(qc, backend=backend, optimization_level=0)

>>> [inst.name for inst, _, _ in qc_transpiled.data]
['cx', 'cx_custom', 'cx']

Copy link
Member

@chriseclectic chriseclectic left a comment

Choose a reason for hiding this comment

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

I don't see why you need to add a new pass that renames nodes in a DAG since this sort of thing may have issues with other passes that rely on names (ie basis gates like @kdk said).

Since you already updated the _get_instruction_string method in InstructionScheduleMap so that it's add, has and get etc methods will look for labels, can't you just update the PulseGates pass to use this information?

In PulseGates.supported and PulseGates.get_calibration it is currently passing the name string to the schedule map: https://github.com/Qiskit/qiskit-terra/blob/40deaf108b12ec7ef8c9d9ddb414a88a969f834e/qiskit/transpiler/passes/calibration/builders.py#L459 but you should just be able to just change this to pass the instruciton itself like self._inst_map.has(node_op) so it can use the updated _get_instruction_name for labelled instructions.

@nkanazawa1989
Copy link
Contributor Author

Indeed this is what I was first trying to implement. However, the format of calibration in qobj is

Dict[name: str, Dict[Tuple[qubits: Tuple[int, ...], parameters: Tuple[float, ...]], schedule: Schedule]]

and there is no place to define the label here. Even though the InstructionScheduleMap is keyed on Instruction with a name and label, qobj doesn't distinguish two (or more) labeled instructions. In the backend, it entirely overrides calibration regardless of the label.

To use the pulse gate, we need to give unique name to the circuit instruction, then attach the calibration to the uniquely named instruction. This is why the instruction name is updated in the DAG.

@nkanazawa1989
Copy link
Contributor Author

Close this PR. Below trick works without modification of terra.

new_inst = copy(inst)
new_inst.name = "some_new_inst_name"

qc = QuantumCircuit(inst.num_qubits, inst.num_clbits)
qc.append(inst, range(inst.num_qubits), range(inst.num_clbits))

new_inst._definition = qc

@nkanazawa1989 nkanazawa1989 deleted the upgrade/custom_label_support branch November 25, 2022 02:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants