diff --git a/.gitignore b/.gitignore index 66218865..b9ccf2af 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,4 @@ test/unit_tests/.DS_Store test/unit_tests/braket/.DS_Store test/.benchmarks .DS_Store +.vscode/* diff --git a/src/braket/default_simulator/openqasm/_helpers/quantum.py b/src/braket/default_simulator/openqasm/_helpers/quantum.py index 59d3fb8f..7647b112 100644 --- a/src/braket/default_simulator/openqasm/_helpers/quantum.py +++ b/src/braket/default_simulator/openqasm/_helpers/quantum.py @@ -52,17 +52,16 @@ def is_controlled(phase: QuantumPhase) -> bool: return False -def convert_phase_to_gate(controlled_phase: QuantumPhase) -> QuantumGate: +def convert_phase_to_gate(controlled_phase: QuantumPhase) -> Union[QuantumGate, list[QuantumGate]]: """Convert a controlled quantum phase into a quantum gate""" ctrl_modifiers = get_ctrl_modifiers(controlled_phase.modifiers) first_ctrl_modifier = ctrl_modifiers[-1] - if first_ctrl_modifier.modifier == GateModifierName.negctrl: - raise ValueError("negctrl modifier undefined for gphase operation") if first_ctrl_modifier.argument.value == 1: ctrl_modifiers.pop() else: ctrl_modifiers[-1].argument.value -= 1 - return QuantumGate( + + ctrl_phaseshift = QuantumGate( ctrl_modifiers, Identifier("U"), [ @@ -73,6 +72,17 @@ def convert_phase_to_gate(controlled_phase: QuantumPhase) -> QuantumGate: controlled_phase.qubits, ) + if first_ctrl_modifier.modifier == GateModifierName.negctrl: + X = QuantumGate( + [], + Identifier("x"), + [], + [controlled_phase.qubits[-1]], + ) + return [X, ctrl_phaseshift, X] + else: + return ctrl_phaseshift + def get_ctrl_modifiers(modifiers: list[QuantumGateModifier]) -> list[QuantumGateModifier]: """Get the control modifiers from a list of quantum gate modifiers""" diff --git a/src/braket/default_simulator/openqasm/interpreter.py b/src/braket/default_simulator/openqasm/interpreter.py index 5503b11a..2dc85e59 100644 --- a/src/braket/default_simulator/openqasm/interpreter.py +++ b/src/braket/default_simulator/openqasm/interpreter.py @@ -311,7 +311,8 @@ def _(self, node: QuantumGateDefinition) -> None: def inline_gate_def_body(self, body: list[QuantumStatement]) -> list[QuantumStatement]: inlined_body = [] - for statement in body: + statement = body.pop(0) if body else None + while statement is not None: if isinstance(statement, QuantumPhase): statement.argument = self.visit(statement.argument) statement.modifiers = self.visit(statement.modifiers) @@ -319,6 +320,10 @@ def inline_gate_def_body(self, body: list[QuantumStatement]) -> list[QuantumStat statement = invert_phase(statement) if is_controlled(statement): statement = convert_phase_to_gate(statement) + if isinstance(statement, list): + for gate in reversed(statement): + body.insert(0, gate) + continue # statement is a quantum phase instruction else: inlined_body.append(statement) @@ -358,6 +363,7 @@ def inline_gate_def_body(self, body: list[QuantumStatement]) -> list[QuantumStat ctrl_qubits, pow_modifiers, ) + statement = body.pop(0) if body else None return inlined_body @visit.register diff --git a/src/braket/default_simulator/openqasm/program_context.py b/src/braket/default_simulator/openqasm/program_context.py index 94a09af2..661b6484 100644 --- a/src/braket/default_simulator/openqasm/program_context.py +++ b/src/braket/default_simulator/openqasm/program_context.py @@ -433,7 +433,12 @@ def circuit(self): def __repr__(self): return "\n\n".join( repr(x) - for x in (self.symbol_table, self.variable_table, self.gate_table, self.qubit_mapping) + for x in ( + self.symbol_table, + self.variable_table, + self.gate_table, + self.qubit_mapping, + ) ) def load_inputs(self, inputs: dict[str, Any]) -> None: @@ -789,7 +794,12 @@ def handle_parameter_value(self, value: float | Expr) -> Any: @abstractmethod def add_gate_instruction( - self, gate_name: str, target: tuple[int, ...], params, ctrl_modifiers: list[int], power: int + self, + gate_name: str, + target: tuple[int, ...], + params, + ctrl_modifiers: list[int], + power: int, ): """Abstract method to add Braket gate to the circuit. Args: @@ -862,12 +872,17 @@ def is_builtin_gate(self, name: str) -> bool: user_defined_gate = self.is_user_defined_gate(name) return name in BRAKET_GATES and not user_defined_gate - def add_phase_instruction(self, target: tuple[int], phase_value: int): + def add_phase_instruction(self, target: tuple[int], phase_value: float): phase_instruction = GPhase(target, phase_value) self._circuit.add_instruction(phase_instruction) def add_gate_instruction( - self, gate_name: str, target: tuple[int, ...], params, ctrl_modifiers: list[int], power: int + self, + gate_name: str, + target: tuple[int, ...], + params, + ctrl_modifiers: list[int], + power: int, ): instruction = BRAKET_GATES[gate_name]( target, *params, ctrl_modifiers=ctrl_modifiers, power=power diff --git a/test/unit_tests/braket/default_simulator/openqasm/test_interpreter.py b/test/unit_tests/braket/default_simulator/openqasm/test_interpreter.py index 20b9e2f4..80a89db5 100644 --- a/test/unit_tests/braket/default_simulator/openqasm/test_interpreter.py +++ b/test/unit_tests/braket/default_simulator/openqasm/test_interpreter.py @@ -57,12 +57,14 @@ BoolType, FloatLiteral, FloatType, + GateModifierName, Identifier, IndexedIdentifier, IntegerLiteral, IntType, QuantumGate, QuantumGateDefinition, + QuantumGateModifier, SymbolLiteral, UintType, ) @@ -341,10 +343,12 @@ def test_array_declaration(): base_type=IntType(), dimensions=[IntegerLiteral(2)] ) assert context.get_type("multi_dim") == ArrayType( - base_type=UintType(IntegerLiteral(8)), dimensions=[IntegerLiteral(2), IntegerLiteral(2)] + base_type=UintType(IntegerLiteral(8)), + dimensions=[IntegerLiteral(2), IntegerLiteral(2)], ) assert context.get_type("by_ref") == ArrayType( - base_type=UintType(IntegerLiteral(8)), dimensions=[IntegerLiteral(2), IntegerLiteral(2)] + base_type=UintType(IntegerLiteral(8)), + dimensions=[IntegerLiteral(2), IntegerLiteral(2)], ) assert context.get_type("with_expressions") == ArrayType( base_type=UintType(IntegerLiteral(8)), @@ -437,7 +441,8 @@ def test_indexed_expression(): context = Interpreter().run(qasm) assert context.get_type("multi_dim") == ArrayType( - base_type=UintType(IntegerLiteral(8)), dimensions=[IntegerLiteral(2), IntegerLiteral(2)] + base_type=UintType(IntegerLiteral(8)), + dimensions=[IntegerLiteral(2), IntegerLiteral(2)], ) assert context.get_type("int_from_array") == IntType(IntegerLiteral(8)) assert context.get_type("array_from_array") == ArrayType( @@ -447,7 +452,8 @@ def test_indexed_expression(): base_type=UintType(IntegerLiteral(8)), dimensions=[IntegerLiteral(3)] ) assert context.get_type("using_set_multi_dim") == ArrayType( - base_type=UintType(IntegerLiteral(8)), dimensions=[IntegerLiteral(3), IntegerLiteral(2)] + base_type=UintType(IntegerLiteral(8)), + dimensions=[IntegerLiteral(3), IntegerLiteral(2)], ) assert context.get_value("multi_dim") == ArrayLiteral( @@ -684,7 +690,12 @@ def test_indexed_identifier(): ] ) assert context.get_value("two") == ArrayLiteral( - [BooleanLiteral(False), BooleanLiteral(False), BooleanLiteral(True), BooleanLiteral(False)] + [ + BooleanLiteral(False), + BooleanLiteral(False), + BooleanLiteral(True), + BooleanLiteral(False), + ] ) @@ -1122,15 +1133,52 @@ def test_gphase(): assert np.allclose(simulation.state_vector, [-1 / np.sqrt(2), 0, 0, 1 / np.sqrt(2)]) -def test_no_neg_ctrl_phase(): +def test_neg_ctrl_phase(): qasm = """ - gate bad_phase a { - negctrl @ gphase(π/2); - } + gate some_ctrl_gphase(λ) a, b { ctrl @ negctrl @ gphase(λ) a, b; } + qubit[2] q; + x q[0]; + h q[1]; + some_ctrl_gphase(π) q[0], q[1]; + h q[1]; """ - no_negctrl = "negctrl modifier undefined for gphase operation" - with pytest.raises(ValueError, match=no_negctrl): - Interpreter().run(qasm) + context = Interpreter().run(qasm) + circuit = context.circuit + simulation = StateVectorSimulation(2, 1, 1) + simulation.evolve(circuit.instructions) + assert np.allclose(simulation.state_vector, [0, 0, 0, -1]) + + assert context.get_gate_definition("some_ctrl_gphase") == QuantumGateDefinition( + name=Identifier("some_ctrl_gphase"), + arguments=[Identifier("λ")], + qubits=[Identifier("a"), Identifier("b")], + body=[ + QuantumGate( + modifiers=[], + name=Identifier("x"), + arguments=[], + qubits=[Identifier("b")], + ), + QuantumGate( + modifiers=[ + QuantumGateModifier(modifier=GateModifierName.ctrl, argument=IntegerLiteral(1)) + ], + name=Identifier("U"), + arguments=[ + IntegerLiteral(0), + IntegerLiteral(0), + Identifier("λ"), + ], + qubits=[Identifier("a"), Identifier("b")], + ), + QuantumGate( + modifiers=[], + name=Identifier("x"), + arguments=[], + qubits=[Identifier("b")], + ), + ], + ) def test_if(): @@ -1828,10 +1876,20 @@ def classical(bit[4] bits) { """ context = Interpreter().run(qasm) assert context.get_value("before") == ArrayLiteral( - [BooleanLiteral(False), BooleanLiteral(False), BooleanLiteral(False), BooleanLiteral(False)] + [ + BooleanLiteral(False), + BooleanLiteral(False), + BooleanLiteral(False), + BooleanLiteral(False), + ] ) assert context.get_value("after") == ArrayLiteral( - [BooleanLiteral(True), BooleanLiteral(False), BooleanLiteral(False), BooleanLiteral(False)] + [ + BooleanLiteral(True), + BooleanLiteral(False), + BooleanLiteral(False), + BooleanLiteral(False), + ] )