Skip to content

Commit

Permalink
feature: Gate and Circuit inversion (#311)
Browse files Browse the repository at this point in the history
Introduces the ability to take adjoints of Gates, gate Instructions and noiseless Circuits.
  • Loading branch information
speller26 authored May 9, 2022
1 parent 614029a commit 46b6473
Show file tree
Hide file tree
Showing 15 changed files with 293 additions and 108 deletions.
11 changes: 11 additions & 0 deletions src/braket/circuits/angled_gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.

import copy
import math
from typing import List, Optional, Sequence, Union

Expand Down Expand Up @@ -87,6 +88,16 @@ def bind_values(self, **kwargs):
"""
raise NotImplementedError

def adjoint(self) -> List[Gate]:
"""Returns the adjoint of this gate as a singleton list.
Returns:
List[Gate]: A list containing the gate with negated angle.
"""
new = copy.copy(self)
new._parameters = [-angle for angle in self._parameters]
return [new]

def __eq__(self, other):
if isinstance(other, AngledGate):
if isinstance(self.angle, FreeParameterExpression):
Expand Down
32 changes: 23 additions & 9 deletions src/braket/circuits/circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,10 +144,9 @@ def depth(self) -> int:
return self._moments.depth

@property
def instructions(self) -> Iterable[Instruction]:
def instructions(self) -> List[Instruction]:
"""Iterable[Instruction]: Get an `iterable` of instructions in the circuit."""

return self._moments.values()
return list(self._moments.values())

@property
def result_types(self) -> List[ResultType]:
Expand Down Expand Up @@ -1031,6 +1030,22 @@ def _flatten(addable):

return self

def adjoint(self) -> Circuit:
"""Returns the adjoint of this circuit.
This is the adjoint of every instruction of the circuit, in reverse order. Result types,
and consequently basis rotations will stay in the same order at the end of the circuit.
Returns:
Circuit: The adjoint of the circuit.
"""
circ = Circuit()
for instr in reversed(self.instructions):
circ.add(instr.adjoint())
for result_type in self._result_types:
circ.add_result_type(result_type)
return circ

def diagram(self, circuit_diagram_class=AsciiCircuitDiagram) -> str:
"""
Get a diagram for the current circuit.
Expand All @@ -1050,9 +1065,9 @@ def to_ir(self) -> Program:
If the circuit is sent over the wire, this method is called before it is sent.
Returns:
(Program): An AWS quantum circuit description program in JSON format.
Program: A Braket quantum circuit description program in JSON format.
"""
ir_instructions = [instr.to_ir() for instr in self.instructions]
ir_instructions = [instruction.to_ir() for instruction in self.instructions]
ir_results = [result_type.to_ir() for result_type in self.result_types]
ir_basis_rotation_instructions = [
instr.to_ir() for instr in self.basis_rotation_instructions
Expand Down Expand Up @@ -1192,10 +1207,10 @@ def __add__(self, addable: AddableTypes) -> Circuit:

def __repr__(self) -> str:
if not self.result_types:
return f"Circuit('instructions': {list(self.instructions)})"
return f"Circuit('instructions': {self.instructions})"
else:
return (
f"Circuit('instructions': {list(self.instructions)}"
f"Circuit('instructions': {self.instructions}"
+ f", 'result_types': {self.result_types})"
)

Expand All @@ -1205,8 +1220,7 @@ def __str__(self):
def __eq__(self, other):
if isinstance(other, Circuit):
return (
list(self.instructions) == list(other.instructions)
and self.result_types == other.result_types
self.instructions == other.instructions and self.result_types == other.result_types
)
return NotImplemented

Expand Down
15 changes: 15 additions & 0 deletions src/braket/circuits/compiler_directive.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.

from __future__ import annotations

from typing import Sequence, Tuple

from braket.circuits.operator import Operator
Expand Down Expand Up @@ -41,6 +43,19 @@ def ascii_symbols(self) -> Tuple[str, ...]:
"""Tuple[str, ...]: Returns the ascii symbols for the compiler directive."""
return self._ascii_symbols

def counterpart(self) -> CompilerDirective:
"""Returns the "opposite" counterpart to this compiler directive.
For example, the counterpart of a directive that starts a box
is the directive that ends the box.
Returns:
CompilerDirective: The counterpart compiler directive
"""
raise NotImplementedError(
f"Compiler directive {self.name} does not have counterpart implemented"
)

def to_ir(self, *args, **kwargs):
raise NotImplementedError("to_ir has not been implemented yet.")

Expand Down
6 changes: 6 additions & 0 deletions src/braket/circuits/compiler_directives.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ class StartVerbatimBox(CompilerDirective):
def __init__(self):
super().__init__(["StartVerbatim"])

def counterpart(self) -> CompilerDirective:
return EndVerbatimBox()

def to_ir(self, *args, **kwargs):
return ir.StartVerbatimBox.construct()

Expand All @@ -37,5 +40,8 @@ class EndVerbatimBox(CompilerDirective):
def __init__(self):
super().__init__(["EndVerbatim"])

def counterpart(self) -> CompilerDirective:
return StartVerbatimBox()

def to_ir(self, *args, **kwargs):
return ir.EndVerbatimBox.construct()
27 changes: 21 additions & 6 deletions src/braket/circuits/gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.

from typing import Any, Optional, Sequence
from __future__ import annotations

from typing import Any, List, Optional, Sequence, Tuple

from braket.circuits.quantum_operator import QuantumOperator
from braket.circuits.qubit_set import QubitSet
Expand Down Expand Up @@ -41,6 +43,16 @@ def __init__(self, qubit_count: Optional[int], ascii_symbols: Sequence[str]):
"""
super().__init__(qubit_count=qubit_count, ascii_symbols=ascii_symbols)

def adjoint(self) -> List[Gate]:
"""Returns a list of gates that implement the adjoint of this gate.
This is a list because some gates do not have an inverse defined by a single existing gate.
Returns:
List[Gate]: The gates comprising the adjoint of this gate.
"""
raise NotImplementedError(f"Gate {self.name} does not have adjoint implemented")

def to_ir(self, target: QubitSet) -> Any:
"""Returns IR object of quantum operator and target
Expand All @@ -51,16 +63,19 @@ def to_ir(self, target: QubitSet) -> Any:
"""
raise NotImplementedError("to_ir has not been implemented yet.")

@property
def ascii_symbols(self) -> Tuple[str, ...]:
"""Tuple[str, ...]: Returns the ascii symbols for the quantum operator."""
return self._ascii_symbols

def __eq__(self, other):
if isinstance(other, Gate):
return self.name == other.name
return False
return isinstance(other, Gate) and self.name == other.name

def __repr__(self):
return f"{self.name}('qubit_count': {self.qubit_count})"
return f"{self.name}('qubit_count': {self._qubit_count})"

@classmethod
def register_gate(cls, gate: "Gate"):
def register_gate(cls, gate: Gate):
"""Register a gate implementation by adding it into the Gate class.
Args:
Expand Down
Loading

0 comments on commit 46b6473

Please sign in to comment.