diff --git a/mpqp/core/circuit.py b/mpqp/core/circuit.py index 674627c3..2d9d2108 100644 --- a/mpqp/core/circuit.py +++ b/mpqp/core/circuit.py @@ -1383,16 +1383,33 @@ def to_other_language( "Cannot simulate noisy circuit with CRk gate due to " "an error on AWS Braket side." ) + from braket.circuits import Circuit as BracketCircuit - qasm3_code = circuit.to_other_language( - Language.QASM3, - skip_pre_measure=skip_pre_measure, - skip_measurements=skip_measurements, - ) - - from mpqp.qasm.qasm_to_braket import qasm3_to_braket_Circuit - - braket_circuit = qasm3_to_braket_Circuit(qasm3_code) + braket_circuit = BracketCircuit() + for instruction in circuit.instructions: + if isinstance(instruction, (Barrier, Breakpoint)): + continue + if isinstance(instruction, Measure): + if not skip_pre_measure: + for pre_measure in instruction.pre_measure: + bracket_pre_measure = pre_measure.to_other_language( + Language.BRAKET + ) + braket_circuit.add(bracket_pre_measure, instruction.targets) + if not skip_measurements: + if isinstance(instruction, BasisMeasure): + braket_circuit.measure(instruction.targets) + continue + braket_instr = instruction.to_other_language(Language.BRAKET) + try: + target = instruction.targets + if isinstance(instruction, ControlledGate): + target = instruction.controls + target + braket_circuit.add_instruction(braket_instr, target=target) + except Exception as e: + raise ValueError( + f"{type(braket_instr)}{braket_instr} cannot be added to the braket circuit: {e}" + ) if len(self.noises) == 0: return braket_circuit diff --git a/mpqp/core/instruction/gates/custom_gate.py b/mpqp/core/instruction/gates/custom_gate.py index 55c80218..3fee080a 100644 --- a/mpqp/core/instruction/gates/custom_gate.py +++ b/mpqp/core/instruction/gates/custom_gate.py @@ -121,6 +121,32 @@ def to_other_language( dummy_circuit.rx(param, 0) return dummy_circuit.to_gate(label="CustomGate") return UnitaryGate(self.matrix) + elif language == Language.BRAKET: + from sympy import Expr + + gate_symbols = set().union( + *( + elt.free_symbols + for elt in self.matrix.flatten() + if isinstance(elt, Expr) + ) + ) + + if len(gate_symbols) > 0: + if not printing: + raise ValueError( + "Custom gates defined with symbolic variables cannot be " + "exported to Braket for now (only numerical matrices are supported)." + ) + return None + else: + from braket.circuits import Instruction as BraketInstruction + from braket.circuits.gates import Unitary as BraketUnitary + + return BraketInstruction( + operator=BraketUnitary(self.definition.matrix), + target=list(range(self.nb_qubits)), + ) elif language == Language.QASM2: from qiskit import QuantumCircuit, qasm2 diff --git a/mpqp/core/instruction/gates/native_gates.py b/mpqp/core/instruction/gates/native_gates.py index 86840954..809e2214 100644 --- a/mpqp/core/instruction/gates/native_gates.py +++ b/mpqp/core/instruction/gates/native_gates.py @@ -22,6 +22,7 @@ if TYPE_CHECKING: from sympy import Expr from qiskit.circuit import Parameter + from braket.circuits import FreeParameter import numpy as np import numpy.typing as npt @@ -84,6 +85,22 @@ def _qiskit_parameter_adder( return qiskit_param +def _sympy_to_braket_param(val: Expr | float) -> "float | FreeParameter": + from sympy import Expr + from braket.circuits import FreeParameter + + if isinstance(val, Expr): + if val.free_symbols: + return FreeParameter(str(val)) # note: Braket won't parse expressions + else: + try: + return float(val.evalf()) # pyright: ignore[reportArgumentType] + except Exception as e: + raise ValueError(f"Failed to evaluate sympy expression '{val}': {e}") + else: + return float(val) + + class NativeGate(Gate, SimpleClassReprABC): """The standard on which we rely, OpenQASM, comes with a set of gates supported by default. More complicated gates can be defined by the user. @@ -224,24 +241,25 @@ def to_other_language( language: Language = Language.QISKIT, qiskit_parameters: Optional[set["Parameter"]] = None, ): - if qiskit_parameters is None: - qiskit_parameters = set() + try: theta = float(self.theta) except: theta = self.theta if language == Language.QISKIT: + if qiskit_parameters is None: + qiskit_parameters = set() return self.qiskit_gate(_qiskit_parameter_adder(theta, qiskit_parameters)) elif language == Language.BRAKET: - from sympy import Expr + from braket.circuits import Instruction - # TODO: handle symbolic parameters for Braket - if isinstance(theta, Expr): - raise NotImplementedError( - "Symbolic expressions are not yet supported for braket " - "export, this feature is coming very soon!" - ) - return self.braket_gate(theta) + connection = self.targets + if isinstance(self, ControlledGate): + connection += self.controls + return Instruction( + operator=self.braket_gate(_sympy_to_braket_param(theta)), + target=list(range(len(connection))), + ) elif language == Language.CIRQ: return self.cirq_gate(theta) if language == Language.QASM2: @@ -346,7 +364,14 @@ def to_other_language( if language == Language.QISKIT: return self.qiskit_gate() elif language == Language.BRAKET: - return self.braket_gate() + from braket.circuits import Instruction + + connection = self.targets + if isinstance(self, ControlledGate): + connection += self.controls + return Instruction( + operator=self.braket_gate(), target=list(range(len(connection))) + ) elif language == Language.CIRQ: return self.cirq_gate elif language == Language.QASM2: @@ -430,7 +455,14 @@ def to_other_language( return self.qiskit_gate(label=self.label) return self.qiskit_gate() elif language == Language.BRAKET: - return self.braket_gate() + from braket.circuits import Instruction + + connection = self.targets + if isinstance(self, ControlledGate): + connection += self.controls + return Instruction( + operator=self.braket_gate(), target=list(range(len(connection))) + ) elif language == Language.CIRQ: return self.cirq_gate elif language == Language.QASM2: @@ -1079,20 +1111,19 @@ def to_other_language( lam=_qiskit_parameter_adder(self.gamma, qiskit_parameters), ) elif language == Language.BRAKET: - from sympy import Expr - - # TODO handle symbolic parameters - if ( - isinstance(self.theta, Expr) - or isinstance(self.phi, Expr) - or isinstance(self.gamma, Expr) - ): - raise NotImplementedError( - "Symbolic expressions are not yet supported for braket " - "export, this feature is coming very soon!" - ) + from braket.circuits import Instruction - return self.braket_gate(self.theta, self.phi, self.gamma) + connection = self.targets + if isinstance(self, ControlledGate): + connection += self.controls + return Instruction( + operator=self.braket_gate( + _sympy_to_braket_param(self.theta), + _sympy_to_braket_param(self.phi), + _sympy_to_braket_param(self.gamma), + ), + target=list(range(len(connection))), + ) elif language == Language.CIRQ: return self.cirq_gate(self.theta, self.phi, self.gamma) elif language == Language.QASM2: diff --git a/mpqp/core/instruction/measurement/expectation_value.py b/mpqp/core/instruction/measurement/expectation_value.py index 908ad45f..4ab496b9 100644 --- a/mpqp/core/instruction/measurement/expectation_value.py +++ b/mpqp/core/instruction/measurement/expectation_value.py @@ -80,8 +80,8 @@ def __init__( self._is_diagonal = None self._diag_elements: Optional[npt.NDArray[np.float64]] = None self.label = label - self.transpile = None "See parameter description." + self.pre_transpile = None if isinstance(observable, PauliString): self.nb_qubits = observable.nb_qubits @@ -306,6 +306,10 @@ def to_other_language( return QLMObservable(self.nb_qubits, matrix=self.matrix) elif language == Language.BRAKET: + # TODO: Braket does not handle pauli with coef because it uses QASM2 + # if self._pauli_string: + # return self.pauli_string.to_other_language(Language.BRAKET) + # else: from braket.circuits.observables import Hermitian return Hermitian( @@ -380,7 +384,7 @@ def __init__( targets: Optional[list[int]] = None, shots: int = 0, commuting_type: CommutingTypes = CommutingTypes.QUBITWISE, - grouping_method: GroupingMethods = GroupingMethods.GREEDY, + grouping_method: GroupingMethods = GroupingMethods.QISKIT, label: Optional[str] = None, optimize_measurement: Optional[bool] = True, optim_diagonal: Optional[bool] = False, @@ -397,6 +401,7 @@ def __init__( """See parameter description.""" self.optimize_measurement = optimize_measurement """See parameter description.""" + self.pre_transpile = None if isinstance(observable, Observable): observable = [observable] else: @@ -417,6 +422,7 @@ def __init__( if obs.label is None: while f"{default_label}_{label_counter}" in label_defined: label_counter += 1 + # Create a new instance of Observable with the new label new_obs = Observable.__new__(Observable) for attr, val in obs.__dict__.items(): @@ -500,6 +506,29 @@ def get_pauli_grouping(self) -> list[list[PauliStringMonomial]]: from mpqp.tools.pauli_grouping import pauli_grouping_greedy return pauli_grouping_greedy(unique_monos, self.commuting_type) + elif self.grouping_method == GroupingMethods.QISKIT: + from qiskit.quantum_info import PauliList + + pauli_labels = [mono.short_name for mono in unique_monos] + pauli_list = PauliList(pauli_labels) + + # Choose grouping based on commutativity type + if self.commuting_type == CommutingTypes.QUBITWISE: + grouped = pauli_list.group_qubit_wise_commuting() + else: + grouped = pauli_list.group_commuting() + + grouped_monomials = [ + [ + PauliString.from_str( + mono.to_label() # pyright: ignore[reportAttributeAccessIssue] + ) + for mono in pauli + ] + for pauli in grouped + ] + + return grouped_monomials # pyright: ignore[reportReturnType] else: raise NotImplementedError(f"{self.grouping_method} is not yet supported.") diff --git a/mpqp/core/instruction/measurement/pauli_string.py b/mpqp/core/instruction/measurement/pauli_string.py index 1182423e..05e62d6c 100644 --- a/mpqp/core/instruction/measurement/pauli_string.py +++ b/mpqp/core/instruction/measurement/pauli_string.py @@ -10,7 +10,7 @@ from enum import Enum, auto from functools import reduce from numbers import Real -from operator import matmul, mul +from operator import mul from typing import TYPE_CHECKING, Any, Literal, Optional, Union import numpy as np @@ -57,6 +57,7 @@ class GroupingMethods(Enum): COLORING_DB = auto() COLORING_DSATUR = auto() CLIQUE_REMOVING = auto() + QISKIT = auto() class PauliString: @@ -136,7 +137,7 @@ def from_str(compact_str: str, dict_value: Optional[dict[str, Coef]] = None): pattern = re.compile(r'([^IXYZ]+)?([IXYZ]+)') - monomials = [] + monomials: list[PauliStringMonomial] = [] for match in pattern.finditer(compact_str.replace(" ", "")): coef_str, atoms_str = match.groups() @@ -161,7 +162,8 @@ def from_str(compact_str: str, dict_value: Optional[dict[str, Coef]] = None): monomials.append( PauliStringMonomial(coef, [atoms_dict[atom] for atom in atoms_str]) ) - + if len(monomials) == 1: + return monomials[0] return PauliString(monomials) def _non_null_str(self): @@ -820,15 +822,15 @@ def to_other_language( elif language == Language.MY_QLM: return [mono.to_other_language(language) for mono in self.monomials] elif language == Language.BRAKET: - pauli_string = None + from braket.circuits.observables import Sum + + pauli_string = [] + for mono in self.monomials: braket_mono = mono.to_other_language(Language.BRAKET) - pauli_string = ( - pauli_string + braket_mono - if pauli_string is not None - else braket_mono - ) - return pauli_string + pauli_string.append(braket_mono) + + return Sum(pauli_string) elif language == Language.CIRQ: cirq_pauli_string = None for monomial in self.monomials: @@ -963,6 +965,10 @@ def __deepcopy__(self, memo: Optional[dict[int, Any]] = None): def name(self) -> str: return f"{'@'.join(map(str, self.atoms))}" + @property + def short_name(self) -> str: + return f"{''.join(atom.label for atom in self.atoms)}" + def __str__(self): from sympy import Expr @@ -1019,10 +1025,6 @@ def __imul__(self, other: "Coef") -> PauliStringMonomial: self.coef = new_coef return self - res = deepcopy(self) - res *= other - return res - def __itruediv__(self, other: "Coef") -> PauliStringMonomial: new_coef: "Coef" = ( self.coef / other @@ -1115,10 +1117,7 @@ def commutes_with( ) elif method == CommutingTypes.FULL: return ( - sum( - 1 for a, b in zip(self.atoms, other.atoms) if not a.commutes_with(b) - ) - % 2 + sum(not a.commutes_with(b) for a, b in zip(self.atoms, other.atoms)) % 2 == 0 ) else: @@ -1182,8 +1181,15 @@ def to_other_language( atom.to_other_language(Language.BRAKET) for atom in self.atoms # pyright: ignore[reportAssignmentType] ] - - return self.coef * reduce(matmul, braket_atoms) + from braket.circuits.observables import TensorProduct + + if len(braket_atoms) == 1: + return ( + self.coef * braket_atoms[0] + ) # pyright: ignore[reportOperatorIssue] + return self.coef * TensorProduct( + braket_atoms + ) # pyright: ignore[reportOperatorIssue] elif language == Language.CIRQ: from cirq.devices.line_qubit import LineQubit @@ -1407,7 +1413,7 @@ def commutes_with( f"Expected a PauliStringAtom in parameter but got {type(other).__name__}" ) if method == CommutingTypes.FULL: - return other == pI or self == pI or self == other + return other is pI or self is pI or self is other raise ValueError( f"PauliStringAtoms can only fully commutes with each others, instead received {method}" ) diff --git a/mpqp/execution/connection/aws_connection.py b/mpqp/execution/connection/aws_connection.py index bdc0aaad..efac7109 100644 --- a/mpqp/execution/connection/aws_connection.py +++ b/mpqp/execution/connection/aws_connection.py @@ -197,6 +197,7 @@ def configure_account_iam() -> tuple[str, list[Any]]: save_env_variable("BRAKET_AUTH_METHOD", "IAM") save_env_variable("BRAKET_CONFIGURED", "True") + return "IAM configuration successful.", [] @@ -259,6 +260,7 @@ def configure_account_sso() -> tuple[str, list[Any]]: save_env_variable("BRAKET_AUTH_METHOD", "SSO") save_env_variable("BRAKET_CONFIGURED", "True") + return "SSO configuration successful.", [] diff --git a/mpqp/execution/providers/atos.py b/mpqp/execution/providers/atos.py index 5495578d..b2377955 100644 --- a/mpqp/execution/providers/atos.py +++ b/mpqp/execution/providers/atos.py @@ -252,10 +252,10 @@ def generate_observable_job(myqlm_circuit: "Circuit", job: Job) -> list["JobQLM" assert job.measure is not None and isinstance(job.measure, ExpectationMeasure) result = [] for obs in job.measure.observables: - if obs.transpile is None: + if obs.pre_transpile is None: qlm_obs = obs.to_other_language(Language.MY_QLM) else: - qlm_obs = obs.transpile + qlm_obs = obs.pre_transpile result.append( myqlm_circuit.to_job( job_type="OBS", diff --git a/mpqp/execution/providers/aws.py b/mpqp/execution/providers/aws.py index 6fb6aec7..6654f5a4 100644 --- a/mpqp/execution/providers/aws.py +++ b/mpqp/execution/providers/aws.py @@ -1,9 +1,9 @@ import math +from contextlib import contextmanager from typing import TYPE_CHECKING, Optional import numpy as np -from mpqp.core.languages import Language from mpqp.core.circuit import QCircuit from mpqp.core.instruction.gates import CRk from mpqp.core.instruction.measurement import ( @@ -11,6 +11,7 @@ ExpectationMeasure, Observable, ) +from mpqp.core.languages import Language from mpqp.execution.connection.aws_connection import get_braket_device from mpqp.execution.devices import AWSDevice from mpqp.execution.job import Job, JobStatus, JobType @@ -85,7 +86,7 @@ def apply_noise_to_braket_circuit( return noisy_circuit -def run_braket(job: Job) -> Result: +def run_braket(job: Job, **provider_options) -> Result: """Executes the job on the right AWS Braket device (local or remote) precised in the job in parameter and waits until the task is completed, then returns the Result. @@ -101,6 +102,8 @@ def run_braket(job: Job) -> Result: This function is not meant to be used directly, please use :func:`~mpqp.execution.runner.run` instead. """ + reservation_arn: Optional[str] = provider_options.get("reservation_arn") + # TODO : [multi-obs] update this to take into account the case when we have list of Observables # TODO : [multi-obs] check if Braket allows for a list of several observables if not isinstance(job.device, AWSDevice): @@ -112,8 +115,8 @@ def run_braket(job: Job) -> Result: from braket.tasks import GateModelQuantumTaskResult if isinstance(job.measure, ExpectationMeasure): - return run_braket_observable(job) - _, task = submit_job_braket(job) + return run_braket_observable(job, **provider_options) + _, task = submit_job_braket(job, **provider_options) res = task.result() if TYPE_CHECKING: assert isinstance(res, GateModelQuantumTaskResult) @@ -121,7 +124,7 @@ def run_braket(job: Job) -> Result: return extract_result(res, job, job.device) -def run_braket_observable(job: Job): +def run_braket_observable(job: Job, **provider_options): """Returns the result of an ``OBSERVABLE`` job. TODO: check that the link bellow is correctly generated. @@ -140,6 +143,8 @@ def run_braket_observable(job: Job): from braket.circuits import Circuit from braket.tasks import GateModelQuantumTaskResult + reservation_arn: Optional[str] = provider_options.get("reservation_arn") + assert isinstance(job.device, AWSDevice) if job.circuit.transpiled_circuit is None: transpiled_circuit = job.circuit.to_other_device(job.device) @@ -154,25 +159,39 @@ def run_braket_observable(job: Job): if job.measure is None: raise NotImplementedError("job.measure is None") assert isinstance(job.measure, ExpectationMeasure) - results = {} - errors = {} + + results, errors = {}, {} if job.measure.optimize_measurement: - grouping = job.measure.get_pauli_grouping() from mpqp.tools.pauli_grouping import ( find_qubitwise_rotations, pauli_monomial_eigenvalues, ) + if job.measure.pre_transpile is None: + grouping = job.measure.get_pauli_grouping() + transpiled_pre_measures = [ + QCircuit(find_qubitwise_rotations(group)).to_other_language( + Language.BRAKET + ) + for group in grouping + ] + eigenvalues = [ + {monom.name: pauli_monomial_eigenvalues(monom) for monom in group} + for group in grouping + ] + + else: + eigenvalues, transpiled_pre_measures = ( + job.measure.pre_transpile + ) # pyright: ignore[reportGeneralTypeIssues] + expectation_values = {} - for group in grouping: - transpiled_pre_measure = QCircuit( - find_qubitwise_rotations(group) - ).to_other_language(Language.BRAKET) + for eigenvalues, pre_measure in zip(eigenvalues, transpiled_pre_measures): job.status = JobStatus.RUNNING if job.measure.shots == 0: from copy import deepcopy - cirq = deepcopy(transpiled_circuit + transpiled_pre_measure) + cirq = deepcopy(transpiled_circuit + pre_measure) cirq.state_vector() # pyright: ignore[reportAttributeAccessIssue] local_result = device.run(cirq, shots=0, inputs=None).result() @@ -183,7 +202,7 @@ def run_braket_observable(job: Job): sorted_values.append(float(np.abs(values[i]) ** 2)) else: local_result = device.run( - transpiled_circuit + transpiled_pre_measure, + transpiled_circuit + pre_measure, shots=job.measure.shots, inputs=None, ) @@ -199,17 +218,18 @@ def run_braket_observable(job: Job): ) else: sorted_values.append(0) - for monom in group: + for name, eigenvalue in eigenvalues.items(): expectation_value: float = np.dot( - pauli_monomial_eigenvalues(monom), + eigenvalue, np.array(sorted_values, dtype=np.float64), ) - expectation_values.update({monom.name: expectation_value}) + expectation_values[name] = expectation_value for i, obs in enumerate(job.measure.observables): string = obs.pauli_string local: float = 0 for monoms in string.monomials: - assert isinstance(monoms.coef, (int, float)) + if TYPE_CHECKING: + assert isinstance(monoms.coef, (int, float)) local += expectation_values[monoms.name] * monoms.coef results.update({f"observable_{i}": local}) errors.update({f"observable_{len(errors)}": None}) @@ -222,15 +242,25 @@ def run_braket_observable(job: Job): for obs in job.measure.observables: from copy import deepcopy + from braket.circuits.observables import Sum + copy = deepcopy(transpiled_circuit) braket_obs = obs.to_other_language(Language.BRAKET) + if isinstance(braket_obs, Sum): + targets = [job.measure.targets] * len(braket_obs.summands) + else: + targets = job.measure.targets copy.expectation( # pyright: ignore[reportAttributeAccessIssue] - observable=braket_obs, target=job.measure.targets + observable=braket_obs, target=targets ) job.status = JobStatus.RUNNING - local_result = device.run( - copy, shots=job.measure.shots, inputs=None - ).result() + + if TYPE_CHECKING: + assert isinstance(device, AWSDevice) + with optional_reservation_arn(device, reservation_arn): + local_result = device.run( + copy, shots=job.measure.shots, inputs=None + ).result() assert isinstance(local_result, GateModelQuantumTaskResult) results.update({f"observable_{len(results)}": local_result.values[0].real}) errors.update({f"observable_{len(errors)}": None}) @@ -239,7 +269,10 @@ def run_braket_observable(job: Job): return Result(job, results, errors, job.measure.shots) -def submit_job_braket(job: Job) -> tuple[str, "QuantumTask"]: +def submit_job_braket( + job: Job, + **provider_options, +) -> tuple[str, "QuantumTask"]: """Submits the job to the right local/remote device and returns the generated task. @@ -261,11 +294,14 @@ def submit_job_braket(job: Job) -> tuple[str, "QuantumTask"]: This function is not meant to be used directly, please use :func:`~mpqp.execution.runner.run` instead. """ + reservation_arn: Optional[str] = provider_options.get("reservation_arn") + if not isinstance(job.device, AWSDevice): raise ValueError( "`job` must correspond to an `AWSDevice`, but corresponds to a " f"{job.device} instead" ) + if job.job_type == JobType.STATE_VECTOR and job.device.is_remote(): raise DeviceJobIncompatibleError( "State vector cannot be computed using AWS Braket remote simulators" @@ -294,32 +330,56 @@ def submit_job_braket(job: Job) -> tuple[str, "QuantumTask"]: if TYPE_CHECKING: assert isinstance(braket_circuit, Circuit) - if job.job_type == JobType.STATE_VECTOR: + # rebind safe_retrieve_samples from braket to Normalize the probability + # because the bracket does not do so and this causes a crash. + from braket.default_simulator.state_vector_simulation import ( + StateVectorSimulation, + ) + + def safe_retrieve_samples(self): # pyright: ignore[reportMissingParameterType] + probs = self.probabilities + probs = probs / np.sum(probs) + return np.random.choice(len(self._state_vector), p=probs, size=self._shots) + + StateVectorSimulation.retrieve_samples = safe_retrieve_samples + # ---- + braket_circuit.state_vector() # pyright: ignore[reportAttributeAccessIssue] job.status = JobStatus.RUNNING - task = device.run(braket_circuit, shots=0, inputs=None) + + if TYPE_CHECKING: + assert isinstance(device, AWSDevice) + with optional_reservation_arn(device, reservation_arn): + task = device.run(braket_circuit, shots=0, inputs=None) elif job.job_type == JobType.SAMPLE: if TYPE_CHECKING: assert job.measure is not None job.status = JobStatus.RUNNING - task = device.run(braket_circuit, shots=job.measure.shots, inputs=None) + if TYPE_CHECKING: + assert isinstance(device, AWSDevice) + with optional_reservation_arn(device, reservation_arn): + task = device.run(braket_circuit, shots=job.measure.shots, inputs=None) elif job.job_type == JobType.OBSERVABLE: # TODO : [multi-obs] update this to take into account the case when we have list of Observables if TYPE_CHECKING: assert isinstance(job.measure, ExpectationMeasure) - if job.measure.observables[0].transpile is None: + if job.measure.observables[0].pre_transpile is None: herm_op = job.measure.observables[0].to_other_language(Language.BRAKET) else: - herm_op = job.measure.observables[0].transpile + herm_op = job.measure.observables[0].pre_transpile braket_circuit.expectation( # pyright: ignore[reportAttributeAccessIssue] observable=herm_op, target=job.measure.targets ) job.status = JobStatus.RUNNING - task = device.run(braket_circuit, shots=job.measure.shots, inputs=None) + + if TYPE_CHECKING: + assert isinstance(device, AWSDevice) + with optional_reservation_arn(device, reservation_arn): + task = device.run(braket_circuit, shots=job.measure.shots, inputs=None) else: raise NotImplementedError(f"Job of type {job.job_type} not handled.") @@ -522,3 +582,15 @@ def estimate_cost_single_job( else: return 0 + + +@contextmanager +def optional_reservation_arn(device: AWSDevice, reservation_arn: Optional[str] = None): + from braket.aws import DirectReservation + + if reservation_arn: + braket_device = get_braket_device(device) + with DirectReservation(braket_device, reservation_arn=reservation_arn): + yield + else: + yield diff --git a/mpqp/execution/providers/google.py b/mpqp/execution/providers/google.py index 55040cbe..3ee73efe 100644 --- a/mpqp/execution/providers/google.py +++ b/mpqp/execution/providers/google.py @@ -249,12 +249,12 @@ def run_cirq_observable( expectation_values = {} for obs in job.measure.observables: - if obs.transpile is None: + if obs.pre_transpile is None: cirq_obs = obs.to_other_language( language=Language.CIRQ, circuit=circuit ) else: - cirq_obs = obs.transpile + cirq_obs = obs.pre_transpile if TYPE_CHECKING: assert type(cirq_obs) in (CirqPauliSum, CirqPauliString) job.status = JobStatus.RUNNING diff --git a/mpqp/execution/providers/ibm.py b/mpqp/execution/providers/ibm.py index 147043e2..5fe74a16 100644 --- a/mpqp/execution/providers/ibm.py +++ b/mpqp/execution/providers/ibm.py @@ -100,10 +100,10 @@ def compute_expectation_value( qiskit_observables: list[SparsePauliOp] = [] for obs in job.measure.observables: - if obs.transpile is None: + if obs.pre_transpile is None: translated = obs.to_other_language(Language.QISKIT) else: - translated = obs.transpile + translated = obs.pre_transpile if TYPE_CHECKING: assert isinstance(translated, SparsePauliOp) qiskit_observables.append(translated) @@ -557,8 +557,8 @@ def submit_remote_ibm(job: Job) -> tuple[str, "RuntimeJobV2"]: qiskit_observables = [ ( obs.to_other_language(Language.QISKIT) - if obs.transpile is None - else obs.transpile + if obs.pre_transpile is None + else obs.pre_transpile ) for obs in meas.observables ] diff --git a/mpqp/execution/runner.py b/mpqp/execution/runner.py index 7755d13d..626fad84 100644 --- a/mpqp/execution/runner.py +++ b/mpqp/execution/runner.py @@ -206,6 +206,7 @@ def _run_single( device: AvailableDevice, values: "Optional[dict[Expr | str, Complex]]" = None, display_breakpoints: bool = True, + provider_options: Optional[dict] = None, ) -> Result: """Runs the circuit on the ``backend``. If the circuit depends on variables, the ``values`` given in parameters are used to do the substitution. @@ -240,6 +241,8 @@ def _run_single( Error: None """ + provider_options = provider_options or {} + from mpqp.execution.simulated_devices import ( SimulatedDevice, StaticIBMSimulatedDevice, @@ -272,7 +275,7 @@ def _run_single( elif isinstance(device, ATOSDevice): return run_atos(job) elif isinstance(device, AWSDevice): - return run_braket(job) + return run_braket(job, **provider_options) elif isinstance(device, GOOGLEDevice): return run_google(job) elif isinstance(device, AZUREDevice): @@ -287,6 +290,7 @@ def run( device: Sequence[AvailableDevice], values: "Optional[dict[Expr | str, Complex]]" = None, display_breakpoints: bool = True, + provider_options: Optional[dict] = None, ) -> BatchResult: ... @@ -296,6 +300,7 @@ def run( device: OneOrMany[AvailableDevice], values: "Optional[dict[Expr | str, Complex]]" = None, display_breakpoints: bool = True, + provider_options: Optional[dict] = None, ) -> BatchResult: ... @@ -305,6 +310,7 @@ def run( device: AvailableDevice, values: "Optional[dict[Expr | str, Complex]]" = None, display_breakpoints: bool = True, + provider_options: Optional[dict] = None, ) -> Result: ... @@ -313,6 +319,7 @@ def run( device: OneOrMany[AvailableDevice], values: "Optional[dict[Expr | str, Complex]]" = None, display_breakpoints: bool = True, + provider_options: Optional[dict] = None, ) -> Result | BatchResult: """Runs the circuit on the backend, or list of backend, provided in parameter. @@ -397,19 +404,23 @@ def namer(circ: QCircuit, i: int): dev, values, display_breakpoints, + provider_options, ) for i, circ in enumerate(flatten(circuit)) for dev in flatten(device) ] ) else: - return _run_single(circuit, device, values, display_breakpoints) + return _run_single( + circuit, device, values, display_breakpoints, provider_options + ) def submit( circuit: QCircuit, device: AvailableDevice, - values: "Optional[dict[Expr | str, Complex]]" = None, + values: Optional[dict[Expr | str, Complex]] = None, + provider_options: Optional[dict] = None, ) -> tuple[str, Job]: """Submit the job related to the circuit on the remote backend provided in parameter. The submission returns a ``job_id`` that can be used to retrieve @@ -442,6 +453,8 @@ def submit( Note: Unlike :func:`run`, you can only submit on one device at a time. """ + provider_options = provider_options or {} + if values is None: values = {} if not device.is_remote(): @@ -457,7 +470,7 @@ def submit( elif isinstance(device, ATOSDevice): job_id, _ = submit_QLM(job) elif isinstance(device, AWSDevice): - job_id, _ = submit_job_braket(job) + job_id, _ = submit_job_braket(job, **provider_options) elif isinstance(device, AZUREDevice): job_id, _ = submit_job_azure(job) else: diff --git a/mpqp/tools/pauli_grouping.py b/mpqp/tools/pauli_grouping.py index 84b345ee..b88150be 100644 --- a/mpqp/tools/pauli_grouping.py +++ b/mpqp/tools/pauli_grouping.py @@ -55,6 +55,7 @@ def pauli_grouping_greedy( [[pI@pX@pX, pY@pY@pZ, pI@pI@pI], [-3*pZ@pY@pX, -1*pZ@pZ@pY], [pY@pX@pY], [2*pX@pX@pY]] """ groups: list[list[PauliStringMonomial]] = [] + for monomial in monomials: added = False for group in groups: diff --git a/tests/core/instruction/gates/test_custom_gate.py b/tests/core/instruction/gates/test_custom_gate.py index d8cd11f4..790d474c 100644 --- a/tests/core/instruction/gates/test_custom_gate.py +++ b/tests/core/instruction/gates/test_custom_gate.py @@ -1,4 +1,3 @@ -import contextlib import random from functools import reduce from itertools import product @@ -12,7 +11,6 @@ from mpqp.execution import AvailableDevice from mpqp.gates import * from mpqp.tools.circuit import random_circuit -from mpqp.tools.errors import UnsupportedBraketFeaturesWarning from mpqp.tools.maths import is_unitary, matrix_eq, rand_orthogonal_matrix @@ -46,12 +44,7 @@ def test_random_orthogonal_matrix(circ_size: int, device: AvailableDevice): for _ in range(targets_start + gate_size, circ_size): exp_state_vector = np.kron(exp_state_vector, np.array([1, 0])) - with ( - pytest.warns(UnsupportedBraketFeaturesWarning) - if isinstance(device, AWSDevice) - else contextlib.suppress() - ): - result = run(c, device) + result = run(c, device) # we reduce the precision because of approximation errors coming from CustomGate usage assert isinstance(result, Result) @@ -76,19 +69,9 @@ def test_custom_gate_with_native_gates(device: AvailableDevice): ) c2 = QCircuit([X(0), H(1), CNOT(1, 2), Z(0)]) - with ( - pytest.warns(UnsupportedBraketFeaturesWarning) - if isinstance(device, AWSDevice) - else contextlib.suppress() - ): - result1 = run(c1, device) + result1 = run(c1, device) - with ( - pytest.warns(UnsupportedBraketFeaturesWarning) - if isinstance(device, AWSDevice) - else contextlib.suppress() - ): - result2 = run(c2, device) + result2 = run(c2, device) # we reduce the precision because of approximation errors coming from CustomGate usage assert isinstance(result1, Result) @@ -102,13 +85,8 @@ def test_custom_gate_with_random_circuit(circ_size: int, device: AvailableDevice matrix = random_circ.to_matrix() custom_gate_circ = QCircuit([CustomGate(matrix, list(range(circ_size)))]) - with ( - pytest.warns(UnsupportedBraketFeaturesWarning) - if isinstance(device, AWSDevice) - else contextlib.suppress() - ): - result1 = run(random_circ, device) - result2 = run(custom_gate_circ, device) + result1 = run(random_circ, device) + result2 = run(custom_gate_circ, device) assert isinstance(result1, Result) assert isinstance(result2, Result) @@ -179,13 +157,8 @@ def _test_execution_equivalence( ) targets = [position for _, position in gates_n_positions] - with ( - pytest.warns(UnsupportedBraketFeaturesWarning) - if isinstance(device, AWSDevice) - else contextlib.suppress() - ): - result_custom_gate = run(QCircuit([CustomGate(matrix, targets)]), device) - result_circuit = run(circuit, device) + result_custom_gate = run(QCircuit([CustomGate(matrix, targets)]), device) + result_circuit = run(circuit, device) assert matrix_eq( result_custom_gate.amplitudes, result_circuit.amplitudes, 1e-4, 1e-4 ) diff --git a/tests/core/instruction/measurement/test_basis.py b/tests/core/instruction/measurement/test_basis.py index f080b162..e6daacba 100644 --- a/tests/core/instruction/measurement/test_basis.py +++ b/tests/core/instruction/measurement/test_basis.py @@ -1,4 +1,3 @@ -import contextlib from itertools import product import numpy as np @@ -21,7 +20,6 @@ ) from mpqp.execution import AvailableDevice from mpqp.gates import * -from mpqp.tools.errors import UnsupportedBraketFeaturesWarning from mpqp.tools.maths import matrix_eq @@ -310,14 +308,9 @@ def test_valid_run_custom_basis_state_vector_one_qubit( ): vectors = [np.array([np.sqrt(3) / 2, 1 / 2]), np.array([-1 / 2, np.sqrt(3) / 2])] - with ( - pytest.warns(UnsupportedBraketFeaturesWarning) - if isinstance(device, AWSDevice) - else contextlib.suppress() - ): - result = run( - circuit + QCircuit([BasisMeasure(basis=Basis(vectors), shots=0)]), device - ) + result = run( + circuit + QCircuit([BasisMeasure(basis=Basis(vectors), shots=0)]), device + ) assert isinstance(result, Result) assert matrix_eq(vectors[expected_vector_index], result.amplitudes) @@ -325,14 +318,14 @@ def test_valid_run_custom_basis_state_vector_one_qubit( def test_run_custom_basis_sampling_one_qubit(): vectors = [np.array([np.sqrt(3) / 2, 1 / 2]), np.array([-1 / 2, np.sqrt(3) / 2])] basis = Basis(vectors) - with pytest.warns(UnsupportedBraketFeaturesWarning): - run( - QCircuit([X(0), X(0), BasisMeasure(basis=basis)]), - [ - IBMDevice.AER_SIMULATOR, - ATOSDevice.MYQLM_PYLINALG, - AWSDevice.BRAKET_LOCAL_SIMULATOR, - GOOGLEDevice.CIRQ_LOCAL_SIMULATOR, - ], - ) + + run( + QCircuit([X(0), X(0), BasisMeasure(basis=basis)]), + [ + IBMDevice.AER_SIMULATOR, + ATOSDevice.MYQLM_PYLINALG, + AWSDevice.BRAKET_LOCAL_SIMULATOR, + GOOGLEDevice.CIRQ_LOCAL_SIMULATOR, + ], + ) assert True diff --git a/tests/core/test_circuit.py b/tests/core/test_circuit.py index 4f4ae59f..ec7495f7 100644 --- a/tests/core/test_circuit.py +++ b/tests/core/test_circuit.py @@ -66,7 +66,7 @@ statevector_from_random_circuit, ) from mpqp.tools.display import one_lined_repr -from mpqp.tools.errors import NonReversibleWarning, UnsupportedBraketFeaturesWarning +from mpqp.tools.errors import NonReversibleWarning from mpqp.tools.generics import Matrix, OneOrMany from mpqp.tools.maths import matrix_eq @@ -570,14 +570,7 @@ def test_without_measurements(circuit: QCircuit, printed_result_filename: str): def test_to_other_language( circuit: QCircuit, args: tuple[Language], result_type: type, result_repr: str ): - language = Language.QISKIT if len(args) == 0 else args[0] - # TODO: test other languages - if language == Language.BRAKET: - with pytest.warns(UnsupportedBraketFeaturesWarning) as record: - converted_circuit = circuit.to_other_language(*args) - assert len(record) == 1 - else: - converted_circuit = circuit.to_other_language(*args) + converted_circuit = circuit.to_other_language(*args) assert type(converted_circuit) == result_type if isinstance(converted_circuit, QiskitCircuit): assert repr(converted_circuit.data) == result_repr diff --git a/tests/examples/test_demonstrations.py b/tests/examples/test_demonstrations.py index 87aa3d83..749cefe9 100644 --- a/tests/examples/test_demonstrations.py +++ b/tests/examples/test_demonstrations.py @@ -1,5 +1,3 @@ -from typing import Any, Callable - import numpy as np import pytest from braket.devices import LocalSimulator @@ -15,7 +13,6 @@ QCircuit, run, ) -from mpqp.execution import AvailableDevice from mpqp.gates import * from mpqp.qasm.qasm_to_braket import qasm3_to_braket_Circuit from mpqp.tools.errors import UnsupportedBraketFeaturesWarning @@ -23,14 +20,6 @@ # TODO: add CIRQ local simulator devices to this file -def warn_guard(device: AvailableDevice, run: Callable[[], Any]): - if isinstance(device, AWSDevice): - with pytest.warns(UnsupportedBraketFeaturesWarning): - return run() - else: - return run() - - def test_sample_demo(): # Declaration of the circuit with the right size circuit = QCircuit(4) @@ -56,7 +45,7 @@ def test_sample_demo(): circuit.add(BasisMeasure([0, 1, 2, 3], shots=2000)) # Run the circuit on a selected device - runner = lambda: run( + run( circuit, [ IBMDevice.AER_SIMULATOR, @@ -71,8 +60,6 @@ def test_sample_demo(): ], ) - warn_guard(AWSDevice.BRAKET_LOCAL_SIMULATOR, runner) - assert True @@ -129,7 +116,7 @@ def test_statevector_demo(): ) # when no measure in the circuit, must run in statevector mode - runner = lambda: run( + run( circuit, [ IBMDevice.AER_SIMULATOR_STATEVECTOR, @@ -141,13 +128,11 @@ def test_statevector_demo(): ], ) - warn_guard(AWSDevice.BRAKET_LOCAL_SIMULATOR, runner) - # same when we add a BasisMeasure with 0 shots circuit.add(BasisMeasure([0, 1, 2, 3], shots=0)) # Run the circuit on a selected device - runner = lambda: run( + run( circuit, [ IBMDevice.AER_SIMULATOR_STATEVECTOR, @@ -159,8 +144,6 @@ def test_statevector_demo(): ], ) - warn_guard(AWSDevice.BRAKET_LOCAL_SIMULATOR, runner) - assert True @@ -208,7 +191,7 @@ def test_observable_demo(shots: int): circuit.add(ExpectationMeasure(obs, shots=shots)) # Running the computation on myQLM and on Aer simulator, then retrieving the results - runner = lambda: run( + run( circuit, [ ATOSDevice.MYQLM_PYLINALG, @@ -223,8 +206,6 @@ def test_observable_demo(shots: int): ], ) - warn_guard(AWSDevice.BRAKET_LOCAL_SIMULATOR, runner) - assert True @@ -240,8 +221,8 @@ def test_aws_qasm_executions(): c[0] = measure q[0]; c[1] = measure q[1];""" - runner = lambda: qasm3_to_braket_Circuit(qasm_str) - circuit = warn_guard(AWSDevice.BRAKET_LOCAL_SIMULATOR, runner) + with pytest.warns(UnsupportedBraketFeaturesWarning): + circuit = qasm3_to_braket_Circuit(qasm_str) device.run(circuit, shots=100).result() @@ -268,9 +249,7 @@ def test_aws_mpqp_executions(): # Add measurement circuit.add(BasisMeasure([0, 1, 2, 3], shots=2000)) - runner = lambda: run(circuit, AWSDevice.BRAKET_LOCAL_SIMULATOR) - - warn_guard(AWSDevice.BRAKET_LOCAL_SIMULATOR, runner) + run(circuit, AWSDevice.BRAKET_LOCAL_SIMULATOR) ##################################################### @@ -294,10 +273,7 @@ def test_aws_mpqp_executions(): circuit.add(ExpectationMeasure(obs, shots=0)) # Running the computation on myQLM and on Braket simulator, then retrieving the results - runner = lambda: run( - circuit, [AWSDevice.BRAKET_LOCAL_SIMULATOR, ATOSDevice.MYQLM_PYLINALG] - ) - warn_guard(AWSDevice.BRAKET_LOCAL_SIMULATOR, runner) + run(circuit, [AWSDevice.BRAKET_LOCAL_SIMULATOR, ATOSDevice.MYQLM_PYLINALG]) ##################################################### @@ -307,10 +283,7 @@ def test_aws_mpqp_executions(): ) # Running the computation on myQLM and on Aer simulator, then retrieving the results - runner = lambda: run( - circuit, [AWSDevice.BRAKET_LOCAL_SIMULATOR, ATOSDevice.MYQLM_PYLINALG] - ) - warn_guard(AWSDevice.BRAKET_LOCAL_SIMULATOR, runner) + run(circuit, [AWSDevice.BRAKET_LOCAL_SIMULATOR, ATOSDevice.MYQLM_PYLINALG]) def test_all_native_gates(): @@ -326,13 +299,12 @@ def test_all_native_gates(): circuit.to_other_language(Language.QASM2) circuit.to_other_language(Language.QASM3) - with pytest.warns(UnsupportedBraketFeaturesWarning): - run( - circuit, - [ - ATOSDevice.MYQLM_PYLINALG, - ATOSDevice.MYQLM_CLINALG, - IBMDevice.AER_SIMULATOR_STATEVECTOR, - AWSDevice.BRAKET_LOCAL_SIMULATOR, - ], - ) + run( + circuit, + [ + ATOSDevice.MYQLM_PYLINALG, + ATOSDevice.MYQLM_CLINALG, + IBMDevice.AER_SIMULATOR_STATEVECTOR, + AWSDevice.BRAKET_LOCAL_SIMULATOR, + ], + ) diff --git a/tests/execution/providers/test_aws.py b/tests/execution/providers/test_aws.py index 4760134a..2c45ef18 100644 --- a/tests/execution/providers/test_aws.py +++ b/tests/execution/providers/test_aws.py @@ -2,7 +2,6 @@ import pytest from mpqp import CNOT, AWSDevice, H, QCircuit, run -from mpqp.tools.errors import UnsupportedBraketFeaturesWarning @pytest.mark.parametrize( @@ -15,5 +14,4 @@ ], ) def test_braket_non_contiguous_qubits(circuit: QCircuit): - with pytest.warns(UnsupportedBraketFeaturesWarning): - run(circuit, AWSDevice.BRAKET_LOCAL_SIMULATOR) + run(circuit, AWSDevice.BRAKET_LOCAL_SIMULATOR) diff --git a/tests/execution/test_validity.py b/tests/execution/test_validity.py index cf3593c3..46ae255a 100644 --- a/tests/execution/test_validity.py +++ b/tests/execution/test_validity.py @@ -1,4 +1,3 @@ -import contextlib from copy import deepcopy import numpy as np @@ -42,7 +41,6 @@ from mpqp.tools.circuit import random_gate, random_noise from mpqp.tools.errors import ( DeviceJobIncompatibleError, - UnsupportedBraketFeaturesWarning, ) from mpqp.tools.maths import matrix_eq @@ -134,8 +132,7 @@ def hae_3_qubit_circuit( def test_state_vector_result_HEA_ansatz( parameters: list[float], expected_vector: npt.NDArray[np.complex128] ): - with pytest.warns(UnsupportedBraketFeaturesWarning): - batch = run(hae_3_qubit_circuit(*parameters), state_vector_devices) + batch = run(hae_3_qubit_circuit(*parameters), state_vector_devices) assert isinstance(batch, BatchResult) for result in batch: assert isinstance(result, Result) @@ -205,8 +202,7 @@ def test_state_vector_result_HEA_ansatz( ], ) def test_state_vector_various_native_gates(gates: list[Gate], expected_vector: Matrix): - with pytest.warns(UnsupportedBraketFeaturesWarning): - batch = run(QCircuit(gates), state_vector_devices) + batch = run(QCircuit(gates), state_vector_devices) assert isinstance(batch, BatchResult) for result in batch: assert isinstance(result, Result) @@ -255,8 +251,7 @@ def test_state_vector_various_native_gates(gates: list[Gate], expected_vector: M def test_sample_basis_state_in_samples(gates: list[Gate], basis_states: list[str]): c = QCircuit(gates) c.add(BasisMeasure(list(range(c.nb_qubits)), shots=10000)) - with pytest.warns(UnsupportedBraketFeaturesWarning): - batch = run(c, sampling_devices) + batch = run(c, sampling_devices) assert isinstance(batch, BatchResult) nb_states = len(basis_states) for result in batch: @@ -282,8 +277,7 @@ def test_sample_counts_in_trust_interval(instructions: list[Gate]): assert isinstance(res, Result) expected_counts = [int(count) for count in np.round(shots * res.probabilities)] c.add(BasisMeasure(list(range(c.nb_qubits)), shots=shots)) - with pytest.warns(UnsupportedBraketFeaturesWarning): - batch = run(c, sampling_devices) + batch = run(c, sampling_devices) assert isinstance(batch, BatchResult) for result in batch: assert isinstance(result, Result) @@ -336,8 +330,7 @@ def test_observable_ideal_case( expected_value = float( expected_vector.transpose().conjugate().dot(observable.dot(expected_vector)) ) - with pytest.warns(UnsupportedBraketFeaturesWarning): - batch = run(c, sampling_devices) + batch = run(c, sampling_devices) assert isinstance(batch, BatchResult) for result in batch: evs = result.expectation_values @@ -383,23 +376,12 @@ def test_validity_run_job_type(device: AvailableDevice, circuits_type: list[QCir if not device.is_remote(): if device.supports_samples(): - with ( - pytest.warns(UnsupportedBraketFeaturesWarning) - if isinstance(device, AWSDevice) - else contextlib.suppress() - ): - assert run(circuit_samples, device) is not None + assert run(circuit_samples, device) is not None else: - with pytest.raises(NotImplementedError): - run(circuit_samples, device) + run(circuit_samples, device) if device.supports_state_vector(): - with ( - pytest.warns(UnsupportedBraketFeaturesWarning) - if isinstance(device, AWSDevice) - else contextlib.suppress() - ): - assert run(circuit_state_vector, device) is not None + assert run(circuit_state_vector, device) is not None else: if isinstance(device, IBMDevice) and not device.supports_state_vector(): with pytest.raises(DeviceJobIncompatibleError): @@ -415,12 +397,7 @@ def test_validity_run_job_type(device: AvailableDevice, circuits_type: list[QCir circuit_observable.measurements[0].shots = 10 assert run(circuit_observable, device) is not None else: - with ( - pytest.warns(UnsupportedBraketFeaturesWarning) - if isinstance(device, AWSDevice) - else contextlib.suppress() - ): - assert run(circuit_observable, device) is not None + assert run(circuit_observable, device) is not None else: if isinstance(device, IBMDevice): @@ -437,12 +414,7 @@ def test_validity_run_job_type(device: AvailableDevice, circuits_type: list[QCir circuit_observable_ideal.measurements[0].shots = 10 assert run(circuit_observable_ideal, device) is not None else: - with ( - pytest.warns(UnsupportedBraketFeaturesWarning) - if isinstance(device, AWSDevice) - else contextlib.suppress() - ): - assert run(circuit_observable_ideal, device) is not None + assert run(circuit_observable_ideal, device) is not None else: if isinstance(device, IBMDevice): with pytest.raises(DeviceJobIncompatibleError): diff --git a/tests/execution/test_vqa.py b/tests/execution/test_vqa.py index 77fb7323..b74c8e5c 100644 --- a/tests/execution/test_vqa.py +++ b/tests/execution/test_vqa.py @@ -19,7 +19,6 @@ from mpqp.execution.devices import AvailableDevice from mpqp.execution.vqa.vqa import OptimizableFunc from mpqp.gates import * -from mpqp.tools.errors import UnsupportedBraketFeaturesWarning # the symbols function is a bit wacky, so some manual type definition is needed here theta: Expr = symbols("θ") @@ -55,11 +54,7 @@ def run(): assert minimize(circ, Optimizer.BFGS, device)[0] - minimum < 0.05 try: - if isinstance(device, AWSDevice): - with pytest.warns(UnsupportedBraketFeaturesWarning): - run() - else: - run() + run() except (ValueError, NotImplementedError) as err: if "not handled" not in str(err): raise diff --git a/tests/noise/test_noisy_execution.py b/tests/noise/test_noisy_execution.py index e12f7520..1befc1d8 100644 --- a/tests/noise/test_noisy_execution.py +++ b/tests/noise/test_noisy_execution.py @@ -3,7 +3,7 @@ import sys from itertools import product -from typing import Any, Callable, Iterable +from typing import Any import numpy as np import pytest @@ -25,7 +25,7 @@ ) from mpqp.execution import AvailableDevice from mpqp.gates import * -from mpqp.tools.errors import UnsupportedBraketFeaturesWarning +from mpqp.noise import AmplitudeDamping, BitFlip, Depolarizing, PhaseDamping from mpqp.tools.theoretical_simulation import validate_noisy_circuit noisy_devices: list[Any] = [ @@ -38,20 +38,6 @@ noisy_devices = [AWSDevice.BRAKET_LOCAL_SIMULATOR, IBMDevice.AER_SIMULATOR] -def filter_braket_warning( - action: Callable[[AvailableDevice], Any], - devices: AvailableDevice, -): - if ( - isinstance(devices, Iterable) - and any(isinstance(device, AWSDevice) for device in devices) - ) or isinstance(devices, AWSDevice): - with pytest.warns((UnsupportedBraketFeaturesWarning)): - return action(devices) - else: - return action(devices) - - @pytest.fixture def circuit(): return QCircuit( @@ -106,8 +92,7 @@ def test_noisy_expectation_value_execution_without_error( PhaseDamping(0.6), ] ) - with pytest.warns(UnsupportedBraketFeaturesWarning): - run(circuit, devices) + run(circuit, devices) assert True @@ -127,8 +112,7 @@ def test_all_native_gates_global_noise_execution_without_error( PhaseDamping(0.4, gates=[CNOT, H]), ] ) - with pytest.warns(UnsupportedBraketFeaturesWarning): - run(circuit, devices) + run(circuit, devices) assert True @@ -149,8 +133,7 @@ def test_all_native_gates_local_noise( PhaseDamping(0.4, [0, 1, 2], gates=[CNOT, H]), ] ) - with pytest.warns(UnsupportedBraketFeaturesWarning): - run(circuit, devices) + run(circuit, devices) assert True @@ -166,6 +149,4 @@ def test_validate_depolarizing_noise( circuit: QCircuit, depol_noise: float, shots: int, device: AvailableDevice ): circuit.add(Depolarizing(depol_noise)) - assert filter_braket_warning( - lambda d: validate_noisy_circuit(circuit, shots, d), device - ) + assert validate_noisy_circuit(circuit, shots, device) diff --git a/tests/qasm/test_qasm_to_braket.py b/tests/qasm/test_qasm_to_braket.py index 220efe76..d738f77f 100644 --- a/tests/qasm/test_qasm_to_braket.py +++ b/tests/qasm/test_qasm_to_braket.py @@ -3,7 +3,7 @@ from braket.circuits.gates import CNot, H from mpqp.qasm.qasm_to_braket import qasm3_to_braket_Circuit -from mpqp.tools.errors import UnsupportedBraketFeaturesWarning +from mpqp.tools import UnsupportedBraketFeaturesWarning @pytest.mark.parametrize(