qiskit-community/qiskit-braket-provider

Supporting verbatim circuit execution

dexter2206 opened this issue · 6 comments

What is the expected enhancement?

I noticed that it is not currently possible to define a circuit in Qiskit and then run it on AWS device in verbatim mode and/or with disabled qubit rewiring. This feature seems to be very important, especially in the field of benchmarking quantum devices, when one needs to have precise control of what circuits are being executed. Is there any chance this could get implemented?

One possibility would be to introduce a verbatim and disable_qubit_rewiring options in AWSBraketBackend.run method, and then act adequately by wrapping the circuit in verbatim instruction before execution or adding disable_qubit_rewiring=True.

Hey @dexter2206 ! Thanks for the submission!

I think qubits rewiring is happening on transpilation step. So, if you do not transpile circuit and send it directly, then it should be executed as given.

Maybe I'm wrong and verbatim is happening on QASM level?

Can you spill a little more light on it @dexter2206?

Thanks for your response @IceKhan13 ! I think that unless disable_qubit_rewiring=True is passed, the rewiring is done by Braket automatically. Similarly, the circuit being sent is compiled for a given device unless it is explicitly wrapped in a verbatim box.

Verification should be simple: I can send a circuit with incompatible gateset and/or incompatible qubit connectivity to a physical device and see if it is executed. If yes, the rewiring/compilation is happening automatically and we would need some explicit actions to prevent it. I will verify this once Lucy or Aspen M-2 are available in my region.

Consider the following piece of code, which runs CNOT(0, 1) on Lucy device in verbatim mode.

from braket.circuits import Circuit
from braket.aws import AwsDevice

subcircuit = Circuit().cnot(0, 1)
circuit = Circuit().add_verbatim_box(subcircuit)

device = AwsDevice("arn:aws:braket:eu-west-2::device/qpu/oqc/Lucy")
device.run(circuit, shots=10)

This code correctly raises an error, because Lucy device doesn't support CNOT gate natively. However, this cannot be reproduced using qiskit_braket_provider, because there is no way of adding verbatim box. By default, circuits without verbatim boxes are compiled by Braket, and hence for instance the following code does not raise an error

from qiskit import QuantumCircuit
from qiskit_braket_provider import AWSBraketProvider

backend = AWSBraketProvider().get_backend("Lucy")
qc = QuantumCircuit(2)
qc.cnot(0, 1)
backend.run(qc, shots=10)

@dexter2206 got it! thank you!

I've looked into it and it looks like we can have couple of ways to resolve this:

  1. Easy-peasy way: we need to propagate some of the parameters for braket jobs. Like you suggested disable_qubit_rewiring is a good option. So, our code will look something like
from qiskit import QuantumCircuit
from qiskit_braket_provider import AWSBraketProvider

backend = AWSBraketProvider().get_backend("Lucy")
qc = QuantumCircuit(2)
qc.cnot(0, 1)
backend.run(qc, shots=10, disable_qubit_rewiring=True)
  1. Trickier: is to introduce new VerbatimInstruction and convert it forth and back in adapter

I like first approach. Literal code changes will be changing this lines

batch_task: AwsQuantumTaskBatch = self._device.run_batch(
braket_circuits, shots=options.get("shots")
)

into

batch_task: AwsQuantumTaskBatch = self._device.run_batch(
    braket_circuits, *options
)

If you want to fix it and have a contribution, I can assign it to you 😄 if not, I will do it

@IceKhan13 Actually verbatim instructions and qubit rewiring are bit different, sorry for including only the former in the example. Anyway, the difference is:

  • disable_qubit_rewiring is an argument to run_batch (or run), and disables reassignment of qubits. So, for instance, if you define a circuit comprising CNOT(0, 3) you are sure it runs on qubits with indices 0 and 3. If not provided, the Braket compiler (or probably third partly software that Braket uses to run specific circuit) may decide to run your circuits on different qubits.
  • verbatim boxes are part of circuits, as illustrated in the example above.

The main point is that verbatim boxes cannot be just passed through **options.

Fortunately, at least from my experience, one typically runs only one verbatim box, and verbatim and non-verbatim parts are not mixed. Therefore, what I suggest is a slight extensions to your first idea, i.e.:

  • pass disable_qubit_rewiring through **options
  • check if options contains verbatim key with value set to True. If so, wrap each circuit in a verbatim box before sending them.

This would roughly look like this:

if options.get("verbatim", False):
    braket_circuits = [convert_to_verbatim(c) for c in braket_circuits]

batch_task: AwsQuantumTaskBatch = self._device.run_batch(
    braket_circuits, *options
)

and _convert_to_verbatim would look roughly like this

def convert_to_verbatim(circuit):
    new_circuit = Circuit()
    new_circuit.add_verbatim_box(circuit)
    return new_circuit

I can contribute if you like the solution outlined above :)

@dexter2206 I love it 😄 !