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

feature: Introduce Validating Emulators with Noise Models to the SDK #1017

Open
wants to merge 114 commits into
base: main
Choose a base branch
from
Open
Changes from 7 commits
Commits
Show all changes
114 commits
Select commit Hold shift + click to select a range
ea85675
feat: Introduce emulator criterion
ltnln Jun 3, 2024
28ec84e
feat: Introduce NativeGateCriterion + Unit Tests
ltnln Jun 3, 2024
f41c52b
feat: Introduce SupportedGateCriterion + Unit Tests
ltnln Jun 3, 2024
fee7888
feat: Introduce Connectivity Criterion + Unit Tests
ltnln Jun 3, 2024
6f216ab
feat: Add __init__.py to emulators/criteria module
ltnln Jun 3, 2024
4e909e5
feat: Add OpenQASM3.0-to-Pytket translations
ltnln Jun 5, 2024
e3e4593
feat: Introduce PytketProgramContext to generate PytketCircuits from …
ltnln Jun 5, 2024
eda10b7
feat: Begin emulator pass implementation
ltnln Jun 6, 2024
096aae7
feat: Change gate translations to use pytket OpType objects instead o…
ltnln Jun 6, 2024
0483fb9
Merge branch 'add_pytket_context' into add_tket_mapping_pass
ltnln Jun 6, 2024
a801ad4
feat: Introduce Pytket-to-Openqasm Translators
ltnln Jun 7, 2024
36eff02
fix: Covert from radians to Tket half-turns in PytketContext
ltnln Jun 7, 2024
a328916
feat: add MEASUREMENT_REGISTER_NAME to translation constants
ltnln Jun 7, 2024
94b4226
feat: Introduce EmulatorPass abstraction and have EmulatorCriterion i…
ltnln Jun 7, 2024
1d37b0a
fix: Update __init__.pys for emulator_pass and criteria directories
ltnln Jun 7, 2024
52e0465
fix: Correct import paths in emulator_passes source and tests
ltnln Jun 7, 2024
467c27c
feat: Introduce Gate Connectivity Criterion
ltnln Jun 10, 2024
5dca87d
fix: Correct qubit order in error print statement
ltnln Jun 10, 2024
4c92658
feat: Add GateConnectivityCriterion tests
ltnln Jun 10, 2024
88c9530
feat: Basic AWS Noise Model
ltnln Jun 12, 2024
1ff8cfa
fix: Discard IonQ Single Qubit Gate Fidelities and use Gate classes i…
ltnln Jun 14, 2024
3355759
Merge branch 'emulation_criteria_basic' into end_to_end_prototype
ltnln Jun 14, 2024
edef55d
fix: Include EmulatorCriterion in __init__.py for emulator_passes module
ltnln Jun 14, 2024
14d09e8
feat: Introduce Emulator Interface and AwsEmulator
ltnln Jun 14, 2024
c229adc
fix: Fix gate device noise model file name typo
ltnln Jun 14, 2024
362ef45
Merge branch 'add_gate_connectivity' into end_to_end_prototype
ltnln Jun 14, 2024
74fecd0
fix: Remove stray print statement
ltnln Jun 18, 2024
28e79bc
feat: Introduce emulators and instantiate emulators within AWS device…
ltnln Jun 18, 2024
dc4a9cf
feat: Introduce Qubit Count Criteria
ltnln Jun 18, 2024
56e730e
feat: Move NoiseModel generation to a set of helper functions and cal…
ltnln Jun 18, 2024
cf0af85
fix: Clean Up Branch
ltnln Jun 18, 2024
d802eaf
Merge branch 'add_pytket_context' into end_to_end_prototype
ltnln Jun 19, 2024
b14cf12
feat: Complete tket_to_qasm3 translations
ltnln Jun 19, 2024
d648b87
fix: Return Qasm3 string instead of program context from tket_to_qasm…
ltnln Jun 19, 2024
0ecd0fa
fix: Add tket_to_qasm3 to pytket_translator module __init__.py
ltnln Jun 19, 2024
bc19df5
feat: Introduce LexiRoutingPass
ltnln Jun 19, 2024
7c1ac20
feat: Split out mapping into a run() dispatch with a pytket circuit a…
ltnln Jun 19, 2024
ad202d7
fix: Add LexiRoutingPass to emulator_passes module __init__.py
ltnln Jun 19, 2024
50c53cd
fix: Implement __eq__ in qubit_count_criterion and fix error print st…
ltnln Jun 19, 2024
10a1dee
feat: AwsDevices add mapping/routing pass to their emulator based on …
ltnln Jun 19, 2024
49809ba
feat: Add validation and compile methods to AwsDevice
ltnln Jun 19, 2024
a3f8459
fix: Update connectivity criterion to validate connectivity of ALL 2-…
ltnln Jun 19, 2024
4c8039b
fix: Remove stray print statement
ltnln Jun 19, 2024
d4bd18c
fix: Add classical indices to add_measure instruction in Tket Context
ltnln Jun 20, 2024
d9c9ede
fix: Add classical targets option to add_measure instruction in Brake…
ltnln Jun 20, 2024
5fc4437
Merge branch 'main' into end_to_end_prototype
ltnln Jun 24, 2024
e2c4b55
fix: Update add_instruction to update circuit _measurement_targets at…
ltnln Jun 24, 2024
695c415
Merge branch 'introduce_classical_measure_targets' into end_to_end_pr…
ltnln Jun 24, 2024
f1de87a
feat: Merge SupportedGateCriterion and NativeGateCriterion classes in…
ltnln Jun 24, 2024
8c1b3ec
fix: Allow GateConnectivityCriterion to be instantiated with a graph …
ltnln Jun 25, 2024
d38432b
feat: Allow ConnectivityGraphs to be instantiated with a graph marked…
ltnln Jun 25, 2024
a4906d9
feat: Add QubitCountCriterion tests and check if qubit_count is negat…
ltnln Jun 25, 2024
39bbfaa
change: remove SpportedGateCriterion and NativeGateCriterion after re…
ltnln Jun 25, 2024
dcc061e
feat: Add tests for tket_to_qasm3 translation, remove extraneous comm…
ltnln Jun 25, 2024
4b207ee
change: Remove references to NativeGateCriterion and SupportedGateCri…
ltnln Jun 25, 2024
80f94d0
Merge branch 'amazon-braket:main' into end_to_end_prototype
Altanali Jun 25, 2024
7268799
fix: Add set apply_noise_model flag to false in Emulator.run to preve…
ltnln Jun 25, 2024
b236055
Merge branch 'main' into introduce_classical_measure_targets
Altanali Jun 25, 2024
a55edb2
change: Target default simulator branch with classical register indic…
ltnln Jun 26, 2024
e39941d
fix: Run linter and fix formatting/complexity
ltnln Jun 26, 2024
9416ffb
Merge branch 'introduce_classical_measure_targets' into end_to_end_pr…
ltnln Jun 26, 2024
ac49677
feat: Run Linter
ltnln Jun 26, 2024
7b3ebf3
fix: Revert default simulator versions and dependent round_trip tests
ltnln Jun 26, 2024
b5828ad
Update src/braket/circuits/braket_program_context.py
speller26 Jun 26, 2024
6b0ad92
Update src/braket/circuits/braket_program_context.py
speller26 Jun 26, 2024
ece64a4
Update src/braket/circuits/braket_program_context.py
speller26 Jun 26, 2024
7caf75e
Merge branch 'main' into introduce_classical_measure_targets
speller26 Jun 26, 2024
19b78ab
Merge branch 'main' of https://github.com/amazon-braket/amazon-braket…
ltnln Jun 26, 2024
51296d9
Merge branch 'introduce_classical_measure_targets'
ltnln Jun 26, 2024
532451c
Merge branch 'introduce_classical_measure_targets'
ltnln Jun 26, 2024
e254410
Merge branch 'main' into main
speller26 Jun 26, 2024
ae3a0e3
fix: Add round_trip test for classical target tracking during measure…
ltnln Jun 26, 2024
8756960
change: Run Linter
ltnln Jun 26, 2024
82d24e3
Merge branch 'main' into prevent_verbatim_circuit_mapping
ltnln Jun 27, 2024
c6e60f1
fix: Remove measurements from Pytket circuit before performing mapping
ltnln Jun 27, 2024
5bec8d4
feat: Add decompose bridge pass after Tket Routing
ltnln Jun 27, 2024
40bf179
feat: Add emulator names to error messages and use AwsDevice names wh…
ltnln Jun 27, 2024
3e1b47c
fix: Fix mistake with applying RB calibration data if sRB data is una…
ltnln Jun 28, 2024
3f5094e
fix: Fix add_pass to correctly check if iterable supplied as argument
ltnln Jul 1, 2024
66703d7
fix: Return nothing in AwsDevice validate if program is valid.
ltnln Jul 1, 2024
ac070e2
change: Remove tket passes and files, fix emulator_interface.py filen…
ltnln Jul 4, 2024
c37bac4
fix: Update EmulatorInterface import in Emulator module
ltnln Jul 4, 2024
545c8ce
fix: Remove references to LexiRouting pass throughout repo.
ltnln Jul 4, 2024
afbb96c
feat!: Run linter, add comments/documentation, add unit tests for emu…
ltnln Jul 9, 2024
934b0ae
test!: Add unit tests and run linters
ltnln Jul 10, 2024
b787222
test: Add AwsNoise model tests
ltnln Jul 10, 2024
8080de7
Merge branch 'main' into only_validation
speller26 Jul 10, 2024
6cd4ffc
test: Reach 100% coverage, run linters
ltnln Jul 15, 2024
1e16ba1
change: Change task_specification type in validate documentation
ltnln Jul 15, 2024
87d342b
Merge branch 'only_validation' of github.com:Altanali/amazon-braket-s…
ltnln Jul 15, 2024
fdfa8c0
fix: Use explicit ProgramType as Python3.9 does not support type para…
ltnln Jul 15, 2024
20c59a2
test: Add test for IonQ create_noise_model dispatch for native gates …
ltnln Jul 15, 2024
073fb4e
fix: Place local_simulator._simulator_device setup in a fixture rathe…
ltnln Jul 15, 2024
3c9fd67
fix: Compare arn strings against explicit enum string values
ltnln Jul 15, 2024
0601c63
change: Update documentation, add check to GateConnectivityCriterion …
ltnln Jul 18, 2024
c5b9fc3
change: Clean up GateConnectivityCriterion instantiation logic.
ltnln Jul 18, 2024
34b94ae
change: Add Raises documentation and make dispatched functions privat…
ltnln Jul 22, 2024
70881c6
Merge branch 'amazon-braket:main' into only_validation
Altanali Jul 29, 2024
b698648
change: Use 'validator' in place of 'criterion', fix usage of generic…
ltnln Jul 29, 2024
e1cb307
change: Reorganize modules to place all gate-device emulator passes i…
ltnln Jul 30, 2024
3b500a3
fix: Correct __init__.pys for emulator_passes module and remove circu…
ltnln Jul 30, 2024
7d7279e
change: Rename emulator_passes directory to emulation_passes
ltnln Jul 30, 2024
8c63da6
change: Remove outdated connectivity_criterion.py file
ltnln Jul 30, 2024
df11f66
Merge branch 'main' into only_validation
Altanali Jul 30, 2024
a79e07d
fix: Use deepcopy before applying emulation passes to a program, fix …
ltnln Jul 30, 2024
96e917a
Merge branch 'only_validation' of github.com:Altanali/amazon-braket-s…
ltnln Jul 30, 2024
f30379c
fix: Fix Emulator raising a new Exception instance from the original …
ltnln Jul 30, 2024
9bddfbc
change: Change error message when trying to create a NoiseModel from …
ltnln Jul 30, 2024
4626332
feat: Replace 'EmulationPass' with more general BasePass class
ltnln Aug 2, 2024
e3ca8a5
change: Remove mentions of 'emulation' from the BasePass module.
ltnln Aug 2, 2024
baccbb1
change: Fix style in BasePass.py
ltnln Aug 2, 2024
371fc8f
Merge branch 'main' into only_validation
speller26 Dec 4, 2024
5aaa69c
Update aws_noise_models.py
rmshaffer Dec 6, 2024
6f04be3
Merge branch 'main' into only_validation
rmshaffer Dec 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/braket/emulators/pytket_translator/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from braket.emulators.pytket_translator.translations import (
PYTKET_TO_QASM,
COMPOSED_GATES,
QASM_TO_PYTKET
)
from braket.emulators.pytket_translator.pytket_program_context import PytketProgramContext
51 changes: 51 additions & 0 deletions src/braket/emulators/pytket_translator/composed_gates.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from typing import List

from pytket.circuit import Circuit, OpType


class ComposedGates:
@staticmethod
def add_pswap(circ: Circuit, arguments, qubits: List[int]):
assert len(arguments) == 1
assert len(qubits) == 2
circ.add_gate(OpType.ZZPhase, arguments, [qubits[0], qubits[1]])
circ.add_gate(OpType.SWAP, [qubits[0], qubits[1]])
circ.add_phase(arguments[0] / 2)

@staticmethod
def add_cphaseshift00(circ: Circuit, arguments, qubits: List[int]):
assert len(arguments) == 1
assert len(qubits) == 2
circ.add_gate(OpType.X, [qubits[1]])
circ.add_gate(OpType.CX, [qubits[1], qubits[0]])
circ.add_gate(OpType.CU1, arguments, [qubits[1], qubits[0]])
circ.add_gate(OpType.CX, [qubits[1], qubits[0]])
circ.add_gate(OpType.X, [qubits[1]])

@staticmethod
def add_cphaseshift01(circ: Circuit, arguments, qubits: List[int]):
assert len(arguments) == 1
assert len(qubits) == 2
circ.add_gate(OpType.CX, [qubits[1], qubits[0]])
circ.add_gate(OpType.CU1, arguments, [qubits[1], qubits[0]])
circ.add_gate(OpType.CX, [qubits[1], qubits[0]])

@staticmethod
def add_cphaseshift10(circ: Circuit, arguments, qubits: List[int]):
assert len(arguments) == 1
assert len(qubits) == 2
circ.add_gate(OpType.CX, [qubits[0], qubits[1]])
circ.add_gate(OpType.CU1, arguments, [qubits[0], qubits[1]])
circ.add_gate(OpType.CX, [qubits[0], qubits[1]])

@staticmethod
def add_cphaseshift(circ: Circuit, arguments, qubits: List[int]):
assert len(arguments) == 1
assert len(qubits) == 2
circ.add_gate(OpType.CU1, arguments, [qubits[0], qubits[1]])

@staticmethod
def add_prx(circ: Circuit, arguments, qubits: List[int]):
assert len(arguments) == 2
assert len(qubits) == 1
circ.add_gate(OpType.PhasedX, arguments, qubits)
108 changes: 108 additions & 0 deletions src/braket/emulators/pytket_translator/pytket_program_context.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
from braket.default_simulator.openqasm.program_context import AbstractProgramContext
from pytket.circuit import (
Circuit,
OpType,
Unitary1qBox,
Unitary2qBox,
Unitary3qBox
)
from pytket.unit_id import Qubit, Bit
from typing import Optional, Union, Any
from sympy import Expr
from braket.emulators.pytket_translator.translations import (
QASM_TO_PYTKET,
COMPOSED_GATES
)
import numpy as np
from sympy import Symbol, pi


class PytketProgramContext(AbstractProgramContext):
def __init__(self, circuit: Optional[Circuit] = None):
"""Inits a `PytketProgramContext`.
Args:
circuit (Optional[Circuit]): A partially-built Pytket circuit to continue building with this
context. Default: None.
"""
super().__init__()
self._circuit = circuit or Circuit()
self._qubits_set = set()

@property
def circuit(self) -> Circuit:
"""Returns the Pytket circuit being built in this context."""
return self._circuit

def is_builtin_gate(self, name: str) -> bool:
user_defined_gate = self.is_user_defined_gate(name)
result = (name in QASM_TO_PYTKET or COMPOSED_GATES) and not user_defined_gate
return result

def add_gate_instruction(
self, gate_name: str, target: tuple[int, ...], params, ctrl_modifiers: list[int], power: int
):
self._check_and_update_qubits(target)
# Convert from Braket's radians to TKET's half-turns
params = [param / pi for param in params]
if gate_name in QASM_TO_PYTKET:
op = QASM_TO_PYTKET[gate_name]
if len(params) > 0:
self._circuit.add_gate(op, *params, target)
else:
self._circuit.add_gate(op, target)
elif gate_name in COMPOSED_GATES:
COMPOSED_GATES[gate_name](self._circuit, *params, target)
else:
raise ValueError(f"Gate {gate_name} is not supported in pytket translations.")


def _check_and_update_qubits(self, target: tuple[int, ...]):
for qubit in target:
if qubit not in self._qubits_set:
new_qubit = Qubit(index=qubit)
self._qubits_set.add(qubit)
self._circuit.add_qubit(new_qubit)

def add_phase_instruction(self, target, phase_value):
self.add_gate_instruction("gphase", target, phase_value)

def add_measure(self, target: tuple[int]):
if len(target) == 0:
return
self._check_and_update_qubits(target)
for index, qubit in enumerate(target):
self._circuit.add_bit(Bit(qubit))
self._circuit.Measure(qubit, qubit)

def add_custom_unitary(
self,
unitary: np.ndarray,
target: tuple[int, ...],
):
num_targets = len(target)
if not (1 <= num_targets <= 3):
raise ValueError("At most 3 qubit gates are supported for custom unitary operations.")

operator_qubit_count = int(np.log2(unitary.shape[0]))
if operator_qubit_count != num_targets:
raise ValueError(
f"Operator qubit count {operator_qubit_count} must be equal to size of target qubit set {target}"
)

self._check_and_update_qubits(target)
if num_targets == 1:
unitary_box = Unitary1qBox(unitary)
self._circuit.add_unitary1qbox(unitary_box, *target)
elif num_targets == 2:
unitary_box = Unitary2qBox(unitary)
self._circuit.add_unitary2qbox(unitary_box, *target)
elif num_targets == 3:
unitary_box = Unitary3qBox(unitary)
self._circuit.add_unitary3qbox(unitary_box, *target)


def handle_parameter_value(self, value: Union[float, Expr]) -> Any:
if isinstance(value, Expr):
return value.evalf()
return value
2 changes: 2 additions & 0 deletions src/braket/emulators/pytket_translator/qasm3_gen/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from braket.emulators.pytket_translator.qasm3_gen.tket_to_qasm3 import tket_to_qasm3
from braket.emulators.pytket_translator.qasm3_gen.qasm_context import QasmContext
34 changes: 34 additions & 0 deletions src/braket/emulators/pytket_translator/qasm3_gen/qasm_context.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from collections import namedtuple
from typing import Dict, List, Union
from sympy import Expr, Symbol, pi
from braket.emulators.pytket_translator.translations import MEASUREMENT_REGISTER_NAME

"""A single measurement: from qubit index to the bit expression."""
Measurement = namedtuple("Measurement", ("qubit", "bit"))

"""A gate operation, with a given name, classical args, and qubits"""
Gate = namedtuple("Gate", ("name", "args", "qubits"))


class QasmContext:

def __init__(self, input_parameters: Dict[str, str]):
self.input_parameters = input_parameters
self.num_bits: int = 0
self.gates: List[Gate] = []
self.measurements: List[Measurement] = []


def set_num_bits(self, num_bits: int) -> None:
self.num_bits = num_bits

def add_gate(self, name: str, args: List[Union[Expr, Symbol]], qubits: List[int]):
print("Adding gate: ", name, args, qubits)
self.gates.append(Gate(name, args, qubits))


def add_measurement(self, qubit: int, cbit: int):
print("Adding measurement: ", qubit, cbit)
self.measurements.append(Measurement(qubit, f"{MEASUREMENT_REGISTER_NAME}[{cbit}]"))


114 changes: 114 additions & 0 deletions src/braket/emulators/pytket_translator/qasm3_gen/qasm_writer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
from abc import ABC, abstractmethod
import io
from braket.emulators.pytket_translator.qasm3_gen.qasm_context import (
QasmContext,
Gate,
Measurement
)
from braket.emulators.pytket_translator.translations import MEASUREMENT_REGISTER_NAME

class QasmWriter(ABC):
"""Abstract class for OpenQASM program writing. It handles basic program writing flow,
but methods can be overwritten by subclasses to modify output behavior.
"""

def __init__(self, context: QasmContext):
"""Initialize a new QasmWriter from a context containing program information.
The writer does not modify the context.
"""
self.context = context

def get_program(self) -> str:
"""Return the OpenQASM3 program program string from the constructor argument
ProgramContext.
"""
stream = io.StringIO()
stream.write(self.get_program_header())
stream.write(self.get_input_declarations())
stream.write(self.get_classical_bit_declarations())
# stream.write(self.get_verbatim_pragma())
# stream.write(self.get_boxed_program_body())
stream.write(self.get_body())
stream.write(self.get_measurements())
return stream.getvalue()

def get_program_header(self) -> str:
return "OPENQASM 3.0;\n"

@abstractmethod
def get_input_declarations(self) -> str:
"""Return input declaration statements.
For example:
"input float theta;\ninput float alpha;\n"
"""

def get_classical_bit_declarations(self) -> str:
return (
f"bit[{self.context.num_bits}] {MEASUREMENT_REGISTER_NAME};\n"
if self.context.num_bits
else ""
)


def get_verbatim_pragma(self) -> str:
return f"#pragma braket verbatim\n"

def get_boxed_program_body(self) -> str:
return f"box {{\n{self.get_body()}}}\n"

def get_body(self) -> str:
stream = io.StringIO()
for gate in self.context.gates:
stream.write(self.get_gate(gate))
return stream.getvalue()

def get_gate(self, gate: Gate) -> str:
return f"{gate.name}{self.get_gate_args(gate)} {self.get_gate_qubits(gate)};\n"

@abstractmethod
def get_gate_args(self, gate: Gate) -> str:
"""Return gate arguments.
For example:
"0.5,pi*theta"
"""

def get_gate_qubits(self, gate: Gate) -> str:
return ",".join(self.format_qubit(q) for q in gate.qubits)

def get_measurements(self) -> str:
return "\n".join(
f"{meas.bit} = measure {self.format_qubit(meas.qubit)};"
for meas in self.context.measurements
)

def format_qubit(self, qubit: int) -> str:
return f"${qubit}"


class BasicQasmWriter(QasmWriter):
"""This writer returns human-readable, basic OpenQASM.
For example:
OPENQASM 3.0;
input angle theta;
bit[1] c;
#pragma braket verbatim
box {
rx(pi*theta) $0;
}
c[0] = measure $0;
"""

def get_input_declarations(self) -> str:
stream = io.StringIO()
for param_name, param_type in self.context.input_parameters.items():
stream.write(f"input {param_type} {param_name};\n")
return stream.getvalue()

def get_gate_args(self, gate: Gate) -> str:
return f"({','.join(str(arg) for arg in gate.args)})" if gate.args else ""
107 changes: 107 additions & 0 deletions src/braket/emulators/pytket_translator/qasm3_gen/tket_to_qasm3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
from braket.emulators.pytket_translator import PYTKET_TO_QASM
from pytket.circuit import Circuit, OpType, Node
from sympy import Expr, pi, Symbol
from typing import Dict, Union, List, Set, Optional
from dataclasses import dataclass
from functools import singledispatchmethod
from braket.emulators.pytket_translator.qasm3_gen.qasm_context import QasmContext



@dataclass
class Qasm3:
program: str


def tket_to_qasm3(
circuit: Circuit,
input_parameters: Dict[str, str]=None,
gate_overrides: Dict[OpType, str]=None
) -> Qasm3:
ticket_visitor = TketCircuitVisitor(QasmContext(input_parameters), gate_overrides)
ticket_visitor.walk_circuit(circuit)
return ticket_visitor.context

class TketCircuitVisitor:
def __init__(self, context, gate_overrides):
self.context = context
self.gate_overrides = gate_overrides
self._measured_nodes: Set[Node] = set()


def walk_circuit(self, circuit: Circuit):
self.context.set_num_bits(len(circuit.bits))
for command in circuit:
self._visit_command(command)

def _visit_command(self, command: Node):
op = command.op
self._validate_args_not_measured(command.args)
optype = op.type
if optype == OpType.CircBox:
self._visit_box(command, optype)
elif optype == OpType.Measure:
self._visit_measure(command, optype)
else:
self._visit_gate(command, optype)

def _validate_args_not_measured(self, args):
for arg in args:
if arg in self._measured_nodes:
raise ValueError(
"Circuit QASM cannot be generated as circuit contains midcircuit "
f"measurements on qubit: {arg}"
)


def _visit_box(self, command: Node, optype):
circ = command.op.get_circuit()
for command in circ:
self._visit_command(command)

def _visit_measure(self, command: Node, optype):
qubit_node = command.args[0]
qubit = qubit_node.index[0]
cbit = command.args[1].index
self.context.add_measurement(qubit, cbit)
self._measured_nodes.add(qubit_node)

# @_visit_op.register
# def _(self, command: Node, optype: OpType.CustomGate):
# gate_name = command.op.gate.name
# if gate_name not in SUPPORTED_CUSTOM_GATES:
# raise ValueError(f"Encountered unsupported custom gate {gate_name}")
# self._visit_gate(gate_name, command.op.params, command.args)


def _visit_gate(self, command: Node, optype):
"""
Check to see if this operation is a gate known by OpenQASM3.0; if it is, retrieve the appropriate translation
and add the operation to the context.
"""
gate_name: str
if optype in self.gate_overrides:
gate_name = self.gate_overrides[optype]
elif optype in PYTKET_TO_QASM:
gate_name = PYTKET_TO_QASM[optype]
else:
raise ValueError(f"Operation {optype} cannot be translated to OpenQASM3.0.")

qubits = command.args
params = command.op.params
print("args: ", params)
print("qubits: ", qubits)

params = self._gate_angles_in_radians(params)
qubits = [q.index[0] for q in qubits]
self.context.add_gate(gate_name, params, qubits)




def _gate_angles_in_radians(self, params):
return [self._tau_to_radians(param) for param in params]

def _tau_to_radians(self, arg: Union[float, Expr, Symbol]):
return pi * arg

66 changes: 66 additions & 0 deletions src/braket/emulators/pytket_translator/translations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from braket.emulators.pytket_translator.composed_gates import ComposedGates
from pytket.circuit import OpType

"""The measurement register identifier."""
MEASUREMENT_REGISTER_NAME = "c"

"""
OpenQASM-3.0 to Pytket Name Translations
"""
QASM_TO_PYTKET = {
"gphase": OpType.Phase,
"i": OpType.noop,
"h": OpType.H,
"x": OpType.X,
"y": OpType.Y,
"z": OpType.Z,
"cv": OpType.CV,
"cnot": OpType.CX,
"cy": OpType.CY,
"cz": OpType.CZ,
"ecr": OpType.ECR,
"s": OpType.S,
"si": OpType.Sdg,
"t": OpType.T,
"ti": OpType.Tdg,
"v": OpType.V,
"vi": OpType.Vdg,
"phaseshift": OpType.U1,
"rx": OpType.Rx,
"ry": OpType.Ry,
"rz": OpType.Rz,
"U": OpType.U3,
"swap": OpType.SWAP,
"iswap": OpType.ISWAPMax,
"xy": OpType.ISWAP,
"xx": OpType.XXPhase,
"yy": OpType.YYPhase,
"zz": OpType.ZZPhase,
"ccnot": OpType.CCX,
"cswap": OpType.CSWAP,
"unitary": OpType.U3,
"gpi": OpType.GPI,
"gpi2": OpType.GPI2,
"ms": OpType.AAMS
}


COMPOSED_GATES = {
"cphaseshift": ComposedGates.add_cphaseshift,
"cphaseshift00": ComposedGates.add_cphaseshift00,
"cphaseshift01": ComposedGates.add_cphaseshift01,
"cphaseshift10": ComposedGates.add_cphaseshift10,
"pswap": ComposedGates.add_pswap,
"prx": ComposedGates.add_prx,
}

"""
Pytket to OpenQASM-3.0 Name Translations
"""
PYTKET_TO_QASM = {optype: qasm_name for qasm_name, optype in QASM_TO_PYTKET.items()}

# For gates which have multiple valid OpenQASM names, like "cx" and "CX", we overwrite
# the values to make sure we use the preferred name.
PYTKET_TO_QASM[OpType.CX] = "cx" # prefer over "CX"
PYTKET_TO_QASM[OpType.U3] = "u3" # prefer over "U"
PYTKET_TO_QASM[OpType.Rz] = "rz" # prefer over "Rz"