-
Notifications
You must be signed in to change notification settings - Fork 118
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
feature: Add delay and barrier for circuits #993
Changes from all commits
a6faeac
8c05927
704439f
fbfc53a
0ffcdac
aa2d365
40069fa
f29f49f
9358d25
2bba6ba
5f15e12
ff8295f
55018e9
24946be
ef44828
d8ece2e
d7887e5
6689001
8538e1e
c44e576
57a42c1
2752a5e
eb5af0b
a5745a4
112cc3b
79945c3
5f097e0
2adb6f6
ce359f5
a07eadd
43c8156
ebc96f0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,203 @@ | ||
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"). You | ||
# may not use this file except in compliance with the License. A copy of | ||
# the License is located at | ||
# | ||
# http://aws.amazon.com/apache2.0/ | ||
# | ||
# or in the "license" file accompanying this file. This file is | ||
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF | ||
# ANY KIND, either express or implied. See the License for the specific | ||
# language governing permissions and limitations under the License. | ||
|
||
from __future__ import annotations | ||
|
||
import math | ||
from collections.abc import Sequence | ||
from enum import Enum | ||
from functools import singledispatch | ||
from typing import Optional, Union | ||
|
||
from braket.circuits.free_parameter_expression import FreeParameterExpression | ||
from braket.circuits.gate import Gate | ||
from braket.circuits.parameterizable import Parameterizable | ||
|
||
|
||
class SiTimeUnit(Enum): | ||
"""Possible Si unit time types""" | ||
|
||
s = "s" | ||
ms = "ms" | ||
us = "us" | ||
ns = "ns" | ||
|
||
def __str__(self): | ||
return self.value | ||
|
||
def __repr__(self) -> str: | ||
return self.__str__() | ||
|
||
|
||
class DurationGate(Gate, Parameterizable): | ||
"""Class `DurationGate` represents a quantum gate that operates on N qubits and a duration.""" | ||
|
||
def __init__( | ||
self, | ||
duration: Union[FreeParameterExpression, float], | ||
qubit_count: Optional[int], | ||
ascii_symbols: Sequence[str], | ||
): | ||
"""Initializes an `DurationGate`. | ||
|
||
Args: | ||
duration (Union[FreeParameterExpression, float]): The duration of the gate in seconds | ||
or expression representation. | ||
qubit_count (Optional[int]): The number of qubits that this gate interacts with. | ||
ascii_symbols (Sequence[str]): ASCII string symbols for the gate. These are used when | ||
printing a diagram of a circuit. The length must be the same as `qubit_count`, and | ||
index ordering is expected to correlate with the target ordering on the instruction. | ||
For instance, if a CNOT instruction has the control qubit on the first index and | ||
target qubit on the second index, the ASCII symbols should have `["C", "X"]` to | ||
correlate a symbol with that index. | ||
|
||
Raises: | ||
ValueError: If the `qubit_count` is less than 1, `ascii_symbols` are `None`, or | ||
`ascii_symbols` length != `qubit_count`, or `duration` is `None` | ||
""" | ||
super().__init__(qubit_count=qubit_count, ascii_symbols=ascii_symbols) | ||
if duration is None: | ||
raise ValueError("duration must not be None") | ||
if isinstance(duration, FreeParameterExpression): | ||
self._parameters = [duration] | ||
else: | ||
# explicit casting in case duration is e.g. np.float32 | ||
self._parameters = [float(duration)] | ||
|
||
@property | ||
def parameters(self) -> list[Union[FreeParameterExpression, float]]: | ||
"""Returns the parameters associated with the object, either unbound free parameters or | ||
bound values. | ||
|
||
Returns: | ||
list[Union[FreeParameterExpression, float]]: The free parameters or fixed value | ||
associated with the object. | ||
""" | ||
return self._parameters | ||
|
||
@property | ||
def duration(self) -> Union[FreeParameterExpression, float]: | ||
"""Returns the duration of the gate | ||
|
||
Returns: | ||
Union[FreeParameterExpression, float]: The duration of the gate | ||
in seconds. | ||
""" | ||
return self._parameters[0] | ||
|
||
def bind_values(self, **kwargs) -> DurationGate: | ||
"""Takes in parameters and attempts to assign them to values. | ||
|
||
Returns: | ||
DurationGate: A new Gate of the same type with the requested parameters bound. | ||
|
||
Raises: | ||
NotImplementedError: Subclasses should implement this function. | ||
""" | ||
raise NotImplementedError | ||
|
||
def __eq__(self, other: DurationGate): | ||
return ( | ||
isinstance(other, DurationGate) | ||
and self.name == other.name | ||
and _durations_equal(self.duration, other.duration) | ||
) | ||
|
||
def __repr__(self): | ||
return f"{self.name}('duration': {self.duration}, 'qubit_count': {self.qubit_count})" | ||
|
||
def __hash__(self): | ||
return hash((self.name, self.qubit_count, self.duration)) | ||
|
||
|
||
@singledispatch | ||
def _durations_equal( | ||
duration_1: Union[FreeParameterExpression, float], | ||
duration_2: Union[FreeParameterExpression, float], | ||
) -> bool: | ||
return isinstance(duration_2, float) and math.isclose(duration_1, duration_2) | ||
|
||
|
||
@_durations_equal.register | ||
def _(duration_1: FreeParameterExpression, duration_2: FreeParameterExpression): | ||
return duration_1 == duration_2 | ||
|
||
|
||
def _duration_str(duration: Union[FreeParameterExpression, float]) -> str: | ||
"""Returns the string represtntion of the duration of the gate. | ||
|
||
Returns: | ||
str : The duration of the gate in string | ||
representation to convienient SI units | ||
in ("s", "ms", "us", "ns"). | ||
|
||
Note: | ||
This is used in ASCII and OPENQASM code generation, so please | ||
do not play around with whitespaces here. | ||
|
||
>> delay[30ns] q[4]; # VALID QASM | ||
>> delay[30 ns] q[4]; # INVALID QASM | ||
|
||
""" | ||
if isinstance(duration, FreeParameterExpression): | ||
return str(duration) | ||
else: | ||
# Currently, duration is truncated to 2 decimal places. | ||
# Same as angle in AngledGate). | ||
DURATION_MAX_DIGITS = 2 | ||
|
||
if duration >= 1: | ||
return f"{round(duration, DURATION_MAX_DIGITS)}{SiTimeUnit.s}" | ||
elif duration >= 1e-3: | ||
return f"{round(1e3 * duration, DURATION_MAX_DIGITS)}{SiTimeUnit.ms}" | ||
elif duration >= 1e-6: | ||
return f"{round(1e6 * duration, DURATION_MAX_DIGITS)}{SiTimeUnit.us}" | ||
else: | ||
return f"{round(1e9 * duration, DURATION_MAX_DIGITS)}{SiTimeUnit.ns}" | ||
|
||
|
||
def duration_ascii_characters( | ||
gate_name: str, duration: Union[FreeParameterExpression, float] | ||
) -> str: | ||
"""Generates a formatted ascii representation of a duration gate. | ||
|
||
Args: | ||
gate_name (str): The name of the gate. | ||
duration (Union[FreeParameterExpression, float]): The duration | ||
of the gate in seconds. | ||
|
||
Returns: | ||
str: Returns the ascii representation for a duration gate. | ||
|
||
""" | ||
return f"{gate_name}({_duration_str(duration)})" | ||
|
||
|
||
def bind_duration(gate: DurationGate, **kwargs: FreeParameterExpression | str) -> DurationGate: | ||
"""Gets the duration with all values substituted in that are requested. | ||
|
||
Args: | ||
gate (DurationGate): The subclass of DurationGate for which the duration is being obtained. | ||
**kwargs (FreeParameterExpression | str): The named parameters that are being filled | ||
for a particular gate. | ||
|
||
Returns: | ||
DurationGate: A new gate of the type of the DurationGate originally used with all | ||
duration updated. | ||
""" | ||
new_duration = ( | ||
gate.duration.subs(kwargs) | ||
if isinstance(gate.duration, FreeParameterExpression) | ||
else gate.duration | ||
) | ||
return type(gate)(duration=new_duration, qubit_count=gate.qubit_count) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,6 +32,12 @@ | |
get_angle, | ||
) | ||
from braket.circuits.basis_state import BasisState, BasisStateInput | ||
from braket.circuits.duration_gate import ( | ||
DurationGate, | ||
_duration_str, | ||
bind_duration, | ||
duration_ascii_characters, | ||
) | ||
from braket.circuits.free_parameter import FreeParameter | ||
from braket.circuits.free_parameter_expression import FreeParameterExpression | ||
from braket.circuits.gate import Gate | ||
|
@@ -3847,6 +3853,77 @@ def pulse_gate( | |
Gate.register_gate(PulseGate) | ||
|
||
|
||
class Barrier(Gate): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sold yet on whether it should be a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jcjaskula-aws , @rmshaffer , did you get a chance to brainstorm if its okay to proceed with Barrier as a Gate? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree with @jcjaskula-aws - I think There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Manvi-Agrawal were you able to try converting this to be a |
||
r"""Barrier gate.""" | ||
|
||
def __init__(self, qubit_count): | ||
super().__init__(qubit_count=qubit_count, ascii_symbols=["||"] * qubit_count) | ||
|
||
def bind_values(self, **kwargs) -> Any: | ||
Manvi-Agrawal marked this conversation as resolved.
Show resolved
Hide resolved
|
||
raise NotImplementedError | ||
|
||
@property | ||
def _qasm_name(self) -> str: | ||
return "barrier" | ||
|
||
@staticmethod | ||
@circuit.subroutine(register=True) | ||
def barrier(target: QubitSetInput) -> Instruction: | ||
r"""Barrier gate. | ||
|
||
Args: | ||
target (QubitSetInput): Target qubit(s) | ||
|
||
Examples: | ||
>>> circ = Circuit().barrier(target = [0, 1, 2]) | ||
""" | ||
return Instruction(Barrier(len(QubitSet(target))), target=QubitSet(target)) | ||
|
||
|
||
Gate.register_gate(Barrier) | ||
|
||
|
||
class Delay(DurationGate): | ||
r"""Delay gate. Applies delay in seconds.""" | ||
|
||
def __init__(self, qubit_count, duration): | ||
super().__init__( | ||
qubit_count=qubit_count, | ||
Manvi-Agrawal marked this conversation as resolved.
Show resolved
Hide resolved
|
||
duration=duration, | ||
ascii_symbols=[duration_ascii_characters("delay", duration)] * qubit_count, | ||
) | ||
|
||
def bind_values(self, **kwargs) -> DurationGate: | ||
return bind_duration(self, **kwargs) | ||
|
||
@property | ||
def _qasm_name(self) -> str: | ||
return f"delay[{_duration_str(self.duration)}]" | ||
|
||
def __hash__(self): | ||
return hash((self.name, self.qubit_count, self.duration)) | ||
|
||
@staticmethod | ||
@circuit.subroutine(register=True) | ||
def delay( | ||
target: QubitSetInput, duration: Union[FreeParameterExpression, float] | ||
) -> Instruction: | ||
r"""Delay gate. Applies delay in seconds. | ||
|
||
Args: | ||
target (QubitSetInput): Target qubit(s) | ||
duration (Union[FreeParameterExpression, float]): Delay in | ||
seconds or in expression representation. | ||
|
||
Examples: | ||
>>> circ = Circuit().delay(target = [0, 1, 2], duration = 30e-9) | ||
""" | ||
return Instruction(Delay(len(QubitSet(target)), duration), target=QubitSet(target)) | ||
|
||
|
||
Gate.register_gate(Delay) | ||
|
||
|
||
def format_complex(number: complex) -> str: | ||
"""Format a complex number into <a> + <b>im to be consumed by the braket unitary pragma | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a note, there is quite a bit of duplication between this
DurationGate
class and the existingAngledGate
class. Did you consider refactoring to make a common base class for instructions with a singlefloat
parameter? I guess this would also ideally extend to theDoubleAngledGate
andTripleAngledGate
classes - so maybe outside the scope of this PR and could be tracked by a separate issue.