amazon-braket / amazon-braket-sdk-python

A Python SDK for interacting with quantum devices on Amazon Braket
https://aws.amazon.com/braket/
Apache License 2.0
294 stars 118 forks source link

Support for adding barriers and delays to `Circuit` #974

Closed rmshaffer closed 2 weeks ago

rmshaffer commented 1 month ago

Describe the feature you'd like

Users should be able to add barriers and delays to Circuit objects using the same syntax as gates.

Implementing barrier and delay instructions should involve minimal complexity. OpenQASM supports both the barrier and delay instructions at the program level, so we could simply add these instructions in global scope in the resulting OpenQASM program.

For example, the code may look like:

circ = Circuit().x(0).barrier([0, 1]).x(0).delay(0, 1e-6)
print(circ)

which would output something like:

T  : |0|1|2|     3     |

q0 : -X-|-X-delay(1e-6)-
        |            
q1 : ---|---------------

T  : |0|1|2|     3     |

How would this feature be used? Please describe.

The barrier and delay instructions are useful when precise timing of quantum operations is needed within a program. One example is in https://github.com/amazon-braket/amazon-braket-sdk-python/issues/417, where the time of measurement needs to be determined precisely.

Describe alternatives you've considered

Today, the Braket service supports barrier and delay as OpenPulse instructions, which is a workaround for achieving this functionality. For example, the program below contains a barrier on qubits $0 and $1 inside a cal block:

program = Program(source="""OPENQASM 3.0;
bit[1] b;
x $0;
cal {
    waveform t = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
    play(q0_drive, t);
    barrier $0, $1;
}
b[0] = measure $0;
""")
rmshaffer commented 1 month ago

An implementation of Barrier would probably look something like this:

from braket.circuits import Gate, Instruction, circuit

class Barrier(Gate):
    def __init__(self, qubit_count):
        super().__init__(qubit_count=qubit_count, ascii_symbols=[f"||" for i in range(qubit_count)])

    def bind_values(self, **kwargs):
        raise NotImplementedError

    @property
    def _qasm_name(self):
        return f"barrier"

    def __hash__(self):
        return hash((self.name, self.qubit_count, self.qubit_count))

    @staticmethod
    @circuit.subroutine(register=True)
    def barrier(target):
        return Instruction(Barrier(len(target)), target=target)

Gate.register_gate(Barrier)

Example usage of Barrier would then look like:

from braket.circuits import Circuit

circ = Circuit().barrier([0,1,2])
print(circ.to_ir("OPENQASM").source)

which would output:

OPENQASM 3.0;
bit[3] b;
qubit[3] q;
barrier q[0], q[1], q[2];
b[0] = measure q[0];
b[1] = measure q[1];
b[2] = measure q[2];

An implementation of Delay would look similar, except that it would also take a parameter representing the duration of the delay.

Manvi-Agrawal commented 1 month ago

@rmshaffer I would like to work on this issue

Manvi-Agrawal commented 1 month ago

@rmshaffer could you please look at PR https://github.com/amazon-braket/amazon-braket-sdk-python/pull/980. Is it okay to have a delay gate that supports delay in ns or do we want to support more units like dt?

rmshaffer commented 1 month ago

@rmshaffer could you please look at PR #980. Is it okay to have a delay gate that supports delay in ns or do we want to support more units like dt?

Reviewing now. To answer your question here, we should specify delay in seconds to be consistent with the existing pulse API for delay: https://github.com/amazon-braket/amazon-braket-sdk-python/blob/1c46ca7f18f68b963ba06edf7bb719258e838a16/src/braket/pulse/pulse_sequence.py#L166-L181

rmshaffer commented 2 weeks ago

Closing this unitaryHACK issue, since the changes have been merged into a feature branch. Thank you @Manvi-Agrawal for your contribution! 🥳

PR #1002 has been opened to track the remaining changes to merge this feature to main.